open Ast
open Bytecode
open Thread
open Random


(* Stack layout just after "Ent":

              <-- SP
   Local n
   ...
   Local 0
   Saved FP   <-- FP
   Saved PC
   Arg 0
   ...
   Arg n *)

let rec printstack i sp = function
   | [] -> []
   | hd :: tl ->
      if i = sp then
         ["|"] @ [string_of_int hd] @ printstack (i+1) sp tl
      else
         [string_of_int hd ] @ printstack (i+1) sp tl

let execute_prog prog =
  let debug = false in
  let stack = Array.make (1024*1024) 0
  and globals = Array.make prog.num_globals 0 
  and new_fp = ref 0 and new_pc = ref 0 in

  let sleep n  = Thread.join(Thread.create(Thread.delay)(float_of_int n /. 1000.0)) in

  let random n = Random.self_init(); Random.int n in

  let drawcurve m =
      Graphics.moveto  (stack.(m)) (stack.(m+1));
      Graphics.curveto ((stack.(m+2)),stack.(m+3)) ((stack.(m+4)),stack.(m+5)) ((stack.(m+6)),stack.(m+7)) in

  let rec exec fp sp pc =  
      if debug then
         print_endline( (Bytecode.string_of_stmt prog.text.(pc)) ^
            " fp: " ^ string_of_int fp ^
            " sp: " ^ string_of_int sp ^
            " pc: " ^ string_of_int pc ^ "     " ^
            String.concat " " (printstack 0 sp
               (Array.to_list (Array.sub stack 0 300))))
      else ();
  match prog.text.(pc) with
  | Ogr -> Graphics.open_graph " 1440x900"; exec fp (sp) (pc+1)
  | Lit i  -> stack.(sp) <- i ; exec fp (sp+1) (pc+1)
  | Drp    -> exec fp (sp-1) (pc+1)
  | Bin op -> let op1 = stack.(sp-2) and op2 = stack.(sp-1) in     
      stack.(sp-2) <- (let boolean i = if i then 1 else 0 in
      match op with
	   | Add     -> op1 + op2
      | Sub     -> op1 - op2
      | Mult    -> op1 * op2
      | Div     -> op1 / op2
      | Equal   -> boolean (op1 =  op2)
      | Neq     -> boolean (op1 != op2)
      | Less    -> boolean (op1 <  op2)
      | Leq     -> boolean (op1 <= op2)
      | Greater -> boolean (op1 >  op2)
      | Geq     -> boolean (op1 >= op2)) ;
      exec fp (sp-1) (pc+1)
  | Lod i   -> stack.(sp)   <- globals.(i)  ; exec fp (sp+1) (pc+1)
  | Str i   -> globals.(i)  <- stack.(sp-1) ; exec fp sp     (pc+1)
  | Lfp i   -> stack.(sp)   <- stack.(fp+i) ; exec fp (sp+1) (pc+1)
  | Sfp i   -> stack.(fp+i) <- stack.(sp-1) ; exec fp sp     (pc+1)
  | Ind i   -> stack.(sp) <- stack.(fp-stack.(fp-i-2)-i-2) ; 
               exec fp (sp+1) (pc+1)
  | Ins i   -> stack.(fp-stack.(fp-i-2)-i-2) <- stack.(sp-1) ; 
               exec fp sp (pc+1)
  | Jsr(-1) -> print_endline (string_of_int stack.(sp-1));
               exec fp sp (pc+1)
  | Jsr(-2) -> for j=1 to stack.(sp-1) do
                ignore(drawcurve (sp - 1 - (j * 8)));
               done;
               exec fp sp (pc+1)
  | Jsr(-3) -> sleep stack.(sp-1);
               exec fp (sp) (pc+1)
  | Jsr(-4) -> Graphics.clear_graph ();
               exec fp (sp+1) (pc+1)
  | Jsr(-5) -> stack.(sp-1) <- random stack.(sp-1);
               exec fp (sp) (pc+1)
  | Jsr i   -> stack.(sp)   <- pc + 1       ; exec fp (sp+1) i
  | Ent i   -> stack.(sp)   <- fp           ; exec sp (sp+i+1) (pc+1)
  | Rta     -> ignore(new_fp := stack.(fp));
               ignore(new_pc := stack.(fp-1));
               exec fp (sp) (pc+1)
  | Rts i   -> (* let new_fp = stack.(fp) and new_pc = stack.(fp-1) in *)
               (*stack.(fp-i-1) <- stack.(sp-1); *) 
               exec !new_fp (fp-i) !new_pc
  | Beq i   -> exec fp (sp-1) (pc + if stack.(sp-1) =  0 then i else 1)
  | Bne i   -> exec fp (sp-1) (pc + if stack.(sp-1) != 0 then i else 1)
  | Bra i   -> exec fp sp (pc+i)
  | Hlt     -> Graphics.close_graph ()

  in exec 0 0 0
