open Boxes
open Picture

let rec tex_string = function
    [] -> ""
  | h::t -> let chr = match h
                       with '#' -> "\char'43{}"
                          | '$' -> "\char'44{}"
                          | '%' -> "\char'45{}"
                          | '&' -> "\char'46{}"
                          | '*' -> "\char'52{}"
                          | '^' -> "\char'136{}"
                          | '_' -> "\char'137{}"
                          | '{' -> "\char'173{}"
                          | '}' -> "\char'175{}"
                          | '~' -> "\char'176{}"
                          | '\\' -> "\char'134{}"
                          | _ -> String.make 1 h
            in (tex_string t) ^ chr

let last s = List.hd (List.rev s)

let strbalance = "\\rule[-\\bdepth]{0pt}{\\bheight}"
and strfont = "\\ttfamily{}"
and epsilon = 1.0e-10

let generate box_con box_off char_off box_height box_sep_frac t = 
  let rec out_list xend yoff = function
      [] -> []
    | h :: s -> 
        let difference = xend -. dim_l h -. box_con 
        and box_sep = box_height *. box_sep_frac 
        and ypos = if yoff -. dim_h h < epsilon then 0. 
                   else yoff -. dim_h h in
        if abs_float difference < epsilon then
          SubPicture ((box_con, ypos), generate_aux false h, []) ::
          out_list xend (ypos -. box_sep) s
        else
          SubPicture ((box_con, ypos), generate_aux false h, []) ::
          Line ((box_con +. dim_l h, ypos +. dim_rc h), 1, 0, 
               difference, []) ::
          out_list xend (ypos -. box_sep) s
  and generate_aux islast = function
      TermBox (d, s) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, [Arrowhead]);
          Oval ((d.l /. 2., d.h /. 2.), d.l -. 2. *. box_con, box_height, []); 
          Text ((d.l /. 2., d.h /. 2.), strfont ^ tex_string s ^ strbalance, 
               []);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | ParamBox (d, s) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, [Arrowhead]);
          Framebox ((box_con, 0.), d.l -. 2. *. box_con, box_height, 
                   [Dashed 2.]); 
          Text ((d.l /. 2., d.h /. 2.), strfont ^ tex_string s ^ strbalance, 
               []);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | NonTermBox (d, s) ->
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, [Arrowhead]);
          Framebox ((box_con, 0.), d.l -. 2. *. box_con, box_height, []); 
          Text ((d.l /. 2., d.h /. 2.), strfont ^ tex_string s ^ strbalance, 
               []);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | OptBox (d, t) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, []);
          SubPicture ((box_con, box_height /. 2.), generate_aux false t, []);
          Line ((box_con, d.h -. d.lc), 0, -1, d.h -. d.lc, []);
          Line ((box_con, 0.), 1, 0, d.l -. 2. *. box_con, []);
          Line ((d.l -. box_con, 0.), 0, 1, d.rc, [Arrowhead]);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | RepBox (d, t) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, []);
          SubPicture ((box_con, 0.), generate_aux false t, []);
          Line ((box_con, d.h), 0, -1, d.lc, [Arrowhead]);
          Line ((d.l -. box_con, d.h), -1, 0, d.l -. 2. *. box_con, []);
          Line ((d.l -. box_con, d.h), 0, -1, d.h -. d.rc, []);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | TermRepBox (d, t1, TermBox (d1, s)) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, []);
          SubPicture ((box_con, 0.), generate_aux false t1, []);
          Oval ((d.l /. 2., d.h -. box_height /. 2.), d1.l, box_height, []);
          Text ((d.l /. 2., d.h -. box_height /. 2.), 
               strfont ^ tex_string s ^ strbalance, []);
          Line ((box_con, d.h -. box_height /. 2.), 0, -1, 
               d.lc -. box_height /. 2., [Arrowhead]);
          Line ((d.l -. box_con, d.h -. box_height /. 2.), -1, 0, 
               d.l /. 2. -. box_con -. d1.l /. 2., []);
          Line ((box_con, d.h -. box_height /. 2.), 1, 0,  
               d.l /. 2. -. box_con -. d1.l /. 2., []);
          Line ((d.l -. box_con, d.h -. box_height /. 2.), 0, -1,  
               d.h -. d.rc -. box_height /. 2., []);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
        ])
    | OptRepBox (d, t) -> 
        Picture (d.l, d.h, 0., 0., [ 
          Line ((0., d.h -. d.lc), 1, 0, box_con, []);
          SubPicture ((box_con, box_height /. 2.), generate_aux false t, []);
          Line ((box_con, d.h), 0, -1, d.lc, [Arrowhead]);
          Line ((d.l -. box_con, d.h), -1, 0, d.l -. 2. *. box_con, []);
          Line ((d.l -. box_con, d.h), 0, -1, d.h -. d.rc, []);
          Line ((box_con, d.h -. d.lc), 0, -1, d.h -. d.lc, []);
          Line ((box_con, 0.), 1, 0, d.l -. 2. *. box_con, []);
          Line ((d.l -. box_con, 0.), 0, 1, d.rc, [Arrowhead]);
          Line ((d.l -. box_con, d.rc), 1, 0, box_con,
               if islast then [Arrowhead] else [])
        ])
    | OrBoxList (d, l) ->
        Picture (d.l, d.h, 0., 0.,  
          Line ((0., d.h -. d.lc), 1, 0, box_con, []) ::
          (out_list (d.l -. box_con) d.h l) @ [
            Line ((d.l -. box_con, d.h -. dim_h (List.hd l) +. 
                                   dim_rc (List.hd l)), 0, -1, 
                 d.h -. dim_h (List.hd l) +. dim_rc (List.hd l) -. d.rc, 
                 [Arrowhead]);
            Line ((box_con, d.h -. d.lc), 0, -1, 
                 d.h -. d.lc -. (let lst = last l 
                                 in dim_h lst -. dim_lc lst), []);
            Line ((d.l -. box_con, d.rc), 1, 0, box_con, 
               if islast then [Arrowhead] else [])
          ])
    | ConBox (d, t1, t2) ->
        let a = dim_h t2 -. dim_lc t2 -. dim_rc t1 in
        Picture (d.l, d.h, 0., 0., [ 
          SubPicture ((0., if a > 0. then a else 0.), generate_aux false t1, 
                     []);
          SubPicture ((dim_l t1 -. box_con, if a < 0. then ~-.a else 0.), 
                     generate_aux islast t2, [])
        ])
    | _ -> raise (Failure "Bug im Parser!")
in generate_aux true t
