open Graphics ;;
Random.self_init () ;;

type operation =
  Val of int |
  Sum of operation * operation |
  Diff of operation * operation |
  Prod of operation * operation |
  Exp of operation * int ;;

let rec calculate = function
  | Val k -> k
  | Exp (op, k) -> Math.pw (calculate op) k
  | Sum (f, g) -> (calculate f) + (calculate g)
  | Diff (f, g) -> (calculate f) - (calculate g)
  | Prod (f, g) -> (calculate f) * (calculate g) ;;

let generate_random_calc ?plus_w:(pw=10) ?minus_w:(mw=8) ?prod_w:(uw=2) ?exp_w:(ew=0) (length : int) (inf : int) (sup : int) =
  let sum = pw + mw + uw + ew in
  let rec aux = function
    | 0 | 1 -> 
      Val (inf + Random.int (sup - inf))
    | k -> 
      let r = Random.int sum in 
      if r < pw then begin
        if Random.int 2 = 0 then
          Sum(aux 0, aux (k-1))
        else
          Sum(aux (k-1), aux 0)
      end
      else if r < pw+mw then begin
        if Random.int 2 = 0 then
          Diff(aux 0, aux (k-1))
        else
          Diff(aux (k-1), aux 0)
      end
      else if r < pw+mw+uw then begin
        if Random.int 2 = 0 then
          Prod(aux 0, aux (k-1))
        else
          Prod(aux (k-1), aux 0)
      end
      else begin
        Exp(aux (k-1), 2 + Random.int 2)
      end
  in
  aux length ;;

let draw_bracket_open ?retval:(rv=Drawing.fodder) (x : int) (y : int) (size : int) =
  moveto (x + size/4) (y+5*size/4) ;
  lineto (x) (y+size/2) ;
  lineto (x) (y-size/2) ;
  lineto (x + size/4) (y-5*size/4) ;
  rv := x + size/4 +6 ;;

let draw_bracket_close ?retval:(rv=Drawing.fodder) (x : int) (y : int) (size : int) =
  moveto (x) (y+5*size/4) ;
  lineto (x + size/4) (y+size/2) ;
  lineto (x + size/4) (y-size/2) ;
  lineto (x) (y-5*size/4) ;
  rv := x + size/4 +6 ;;

let draw_sign ?retval:(rv=Drawing.fodder) (x : int) (y : int) (len : int) = function
  | '+' ->
    draw_poly_line [|(x, y); (x + len, y)|] ;
    draw_poly_line [|(x+len/2, y + len/2); (x+len/2, y - len/2)|] ;
  | '-' ->
    draw_poly_line [|(x, y); (x + len, y)|] ;
  | 'x' ->
    Drawing.draw_letter x y 'x' len ;
  | _ -> failwith "incorrect\n" ;;

let require_bracket = function
    | Val _ -> false
    | _ -> true ;;

let print_calc (f : operation) (x0 : int) (y0 : int) (size : int) =
  set_line_width 3 ;
  set_color black ;
  let x = ref x0 in
  let rec aux = function
    | Val k -> 
      Drawing.draw_integer_alignedleft !x y0 k size ~retval:x ;
      (*Printf.printf "%d --> \n" !x ;*)
    | Exp (f, k) -> ()
    | Sum (f, g) ->
      if require_bracket f then draw_bracket_open !x y0 size ~retval:x ;
      aux f ;
      if require_bracket f then draw_bracket_close !x y0 size ~retval:x ;

      draw_sign !x y0 size '+' ~retval:x ;
      x := !x + size*10/7 ;
      
      if require_bracket g then draw_bracket_open !x y0 size ~retval:x ;
      aux g ;
      if require_bracket g then draw_bracket_close !x y0 size ~retval:x ;
    | Diff (f, g) ->
      if require_bracket f then draw_bracket_open !x y0 size ~retval:x ;
      aux f ;
      if require_bracket f then draw_bracket_close !x y0 size ~retval:x ;

      draw_sign !x y0 size '-' ~retval:x ;
      x := !x + size*10/7 ;

      if require_bracket g then draw_bracket_open !x y0 size ~retval:x ;
      aux g ;
      if require_bracket g then draw_bracket_close !x y0 size ~retval:x ;
    | Prod (f, g) ->
      if require_bracket f then draw_bracket_open !x y0 size ~retval:x ;
      aux f ;
      if require_bracket f then draw_bracket_close !x y0 size ~retval:x ;

      draw_sign !x y0 (size/2) 'x' ~retval:x ;
      x := !x + size*10/7 ;

      if require_bracket g then draw_bracket_open !x y0 size ~retval:x ;
      aux g ;
      if require_bracket g then draw_bracket_close !x y0 size ~retval:x ;
  in
  aux f ;;

let maeth_main () =
  let start = Unix.gettimeofday () in
  let found = ref false in
  while not !found && (Unix.gettimeofday() -. start <= !Csts.time_to_ans_f) do
    ()
  done ;
  if !found then begin
    incr Csts.combo ;
    true
  end
  else begin
    Csts.combo := 0 ;
    false
  end ;;