
open Cabs
open Cabshelper

(* This isn't the most efficient way to do things.
 * It would probably be better to not reparse rather
 * than keep the tokens in memory *)
 
(* In particular, most of the tokens we hold will be
   header files that we don't need *)

(* map cabslocs to token indexes *)

(* TODO: gather until end of line, then decide where to split *)

(* NOTE: If you find yourself getting lots of nomatch errors with
 * parens in them, then that may mean you are printing 
 * a cabs file that has had it's parens removed *)

let tokenmap : ((string * int),int) Hashtbl.t = Hashtbl.create 1000
let nextidx = ref 0

let gonebad = ref false

(* array of tokens and whitespace *)
let tokens = GrowArray.make 0 (GrowArray.Elem  ("",""))

let cabsloc_to_str cabsloc =
    cabsloc.filename ^ ":" ^ string_of_int cabsloc.lineno ^ ":" ^ 
    string_of_int cabsloc.byteno ^ ":" ^ 
    string_of_int cabsloc.ident

let lastline = ref 0

let wraplexer_enabled lexer lexbuf = 
    let white,lexeme,token,cabsloc = lexer lexbuf in
    GrowArray.setg tokens !nextidx (white,lexeme);
    Hashtbl.add tokenmap (cabsloc.filename,cabsloc.byteno) !nextidx;
    nextidx := !nextidx + 1;
    token

let wraplexer_disabled lexer lexbuf = 
    let white,lexeme,token,cabsloc = lexer lexbuf in
    token

let enabled = ref false

let wraplexer lexer =
    if !enabled then wraplexer_enabled lexer 
    else wraplexer_disabled lexer
    
let finalwhite = ref "\n"    
    
let setFinalWhite w = finalwhite := w 
    
let curidx = ref 0  
let noidx = -1  
let out = ref stdout
    
let setLoc cabsloc =
    if cabsloc != cabslu && !enabled then begin
        try 
            curidx := Hashtbl.find tokenmap (cabsloc.filename,cabsloc.byteno)
        with
            Not_found -> Errormsg.s 
                (Errormsg.error "setLoc with location for non-lexed token: %s"
                    (cabsloc_to_str cabsloc)) 
    end else begin curidx := noidx; () end
    
let setOutput out_chan = 
    out := out_chan

(* TODO: do this properly *)
let invent_white () = " "

let rec chopwhite str =
    if String.length str = 0 then str 
    else if String.get str (String.length str - 1) = ' ' then
        chopwhite (String.sub str 0 (String.length str - 1))
    else if String.get str 0 = ' ' then
        chopwhite (String.sub str 1 (String.length str - 1)) 
    else str
    
let last_was_maybe = ref false    
let last_str = ref ""
    
let print str =
    let str = chopwhite str in
    if str = "" then ()
    else if !curidx == noidx || not !enabled then 
        output_string !out (invent_white() ^ str) 
    else begin
        let srcwhite,srctok = GrowArray.getg tokens !curidx in
        let white = if str = srctok 
            then srcwhite
            else if !gonebad then invent_white ()
            else begin                
                ignore (Errormsg.warnOpt "%s" ("nomatch:["^String.escaped str^"] expected:["^String.escaped srctok ^ 
                    "] - NOTE: cpp not supported"));
                gonebad := true;
                invent_white ()
            end in
        if !last_was_maybe && str = !last_str then () else begin
            output_string !out (white ^ str);
            curidx := !curidx + 1
        end
    end;
    last_was_maybe := false

let printl strs = 
    List.iter print strs   
    
let printu str =
    if not !enabled then print str
    else
        let srcwhite,srctok = GrowArray.getg tokens !curidx in
        if chopwhite str = "" then () 
        else if srctok = str 
          || srctok = str ^ "__" 
          || srctok = "__" ^ str
          || srctok = "__" ^ str ^ "__"
          then
          print srctok
        else (print_endline ("u-nomatch:["^str^"]"); print str)
                
let print_maybe str =
    if not !enabled then print str
    else
        let srcwhite,srctok = GrowArray.getg tokens !curidx in
        if str = srctok then begin 
            print str;
            last_was_maybe := true;
            last_str := str
        end else ()


let printEOF () = output_string !out !finalwhite


