(*open Spectrum*)

let rec print_mat m = 
    for i = 0 to (Array.length m)-1 do
        print_char ' ';
        for j = 0 to (Array.length m.(i))-1 do
            if m.(i).(j) <> -1 then
                print_int m.(i).(j) 
            else
                print_char '_';
            print_char ' ';
            print_char '|';
            print_char ' ';
        done;
        print_char '\n';
        for j = 0 to (Array.length m.(i))-1 do
            print_char '-';
            print_char '-';
            print_char '-';
            print_char '+';
        done;
        print_char '\n';
    done ;;

exception ReturnBool of bool ;;

let pi = 3.14159265358979343 ;;

let abs x =
    if x >= 0 then x else -x ;;

let absf x =
    if x >= 0. then x else -1. *. x ;;

let is_rempaceable c arr =
    try
        for i = 0 to (Array.length arr - 1) do
            if c = arr.(i) then
                raise (ReturnBool true)
            else
                ()
        done;
        raise (ReturnBool false)
    with
        | ReturnBool b -> b ;;

let draw_line_bresenham mat cls origin x1 y1 x2 y2 cutoff = 
    let slope = ref 0. in
    let override_arr = [|' '; '.'; '|'; '-'|] in
    if x2 <> x1 || y2 <> y1 then
        slope := (float_of_int (y2 - y1) /. float_of_int (x2 - x1))
    else 
        ();
    (*Printf.printf "(%f)\n" !slope;*)
    if absf (!slope) < 1. then
        if x1 < x2 then
            for k = x1 to x2 do
                let cur_y = ref ((!slope) *. float_of_int (k - x1) +. float_of_int y1) in
                if !slope = 0. then cur_y := float_of_int y2 else ();
                if is_rempaceable mat.(k).(int_of_float (!cur_y)) override_arr then  begin
                    if x2 - k <= cutoff || k mod 2 = 0 || cls.(k).(int_of_float (!cur_y)) = 0 then
                        cls.(k).(int_of_float (!cur_y)) <- origin+1
                    else
                        ();
                    if x2 - k > cutoff then 
                        mat.(k).(int_of_float (!cur_y)) <- '|' 
                    else 
                        mat.(k).(int_of_float (!cur_y)) <- 'v'
                end
                else
                    ()
            done
        else
            for k = x1 downto x2 do
                let cur_y = ref ((!slope) *. float_of_int (k - x1) +. float_of_int y1) in
                if !slope = 0. then cur_y := float_of_int y2 else ();
                if is_rempaceable mat.(k).(int_of_float (!cur_y)) override_arr then  begin
                    if k - x2 <= cutoff || k mod 2 = 0 || cls.(k).(int_of_float (!cur_y)) = 0 then
                        cls.(k).(int_of_float (!cur_y)) <- origin+1
                    else
                        ();
                    if k - x2 > cutoff then 
                        mat.(k).(int_of_float (!cur_y)) <- '|' 
                    else 
                        mat.(k).(int_of_float (!cur_y)) <- '^'
                end
                else
                    ()
            done
    else
        if y1 < y2 then
            for l = y1 to y2 do
                let cur_x = ref (float_of_int x2) in
                if (!slope) <> 1.0 /. 0.0 && (!slope) <> (-. 1.) /. 0. then
                    cur_x := ((float_of_int l) +. ((!slope) *. (float_of_int x1) -. (float_of_int y1))) /. (!slope) 
                else
                    ();
                if is_rempaceable mat.(int_of_float (!cur_x)).(l) override_arr then begin
                    if y2 - l <= cutoff || l mod 2 = 0 || cls.(int_of_float (!cur_x)).(l) = 0 then
                        cls.(int_of_float (!cur_x)).(l) <- origin+1
                    else
                        ();
                    if y2 - l > cutoff then 
                        mat.(int_of_float (!cur_x)).(l) <- '-' 
                    else 
                        mat.(int_of_float (!cur_x)).(l) <- '>'
                end
                else 
                    ()
            done
        else
            for l = y1 downto y2 do
                let cur_x = ref (float_of_int x2) in
                if (!slope) <> 1.0 /. 0.0 && (!slope) <> (-. 1.) /. 0. then
                    cur_x := ((float_of_int l) +. ((!slope) *. (float_of_int x1) -. (float_of_int y1))) /. (!slope) 
                else
                    ();
                    if is_rempaceable mat.(int_of_float (!cur_x)).(l) override_arr then begin
                        if l - y2 <= cutoff || l mod 2 = 0 || cls.(int_of_float (!cur_x)).(l) = 0 then
                            cls.(int_of_float (!cur_x)).(l) <- origin+1
                        else
                            ();
                        if l - y2 > cutoff then 
                            mat.(int_of_float (!cur_x)).(l) <- '-' 
                        else 
                            mat.(int_of_float (!cur_x)).(l) <- '<'
                    end
                else 
                    ()
            done;;

let display mat cls =
    let colors = [|"\027[0m"; "\027[41m"; "\027[42m"; "\027[43m"; "\027[44m"; "\027[45m"; "\027[46m"; "\027[47m"; "\027[100m"; "\027[101m"; "\027[102m"; "\027[103m"; "\027[104m"; "\027[105m"; "\027[106m"; "\027[107m"|] in
    for i = 0 to (Array.length mat -1) do
        for j = 0 to (Array.length mat.(i) -1) do
            if cls.(i).(j) = 0 then
                print_string colors.(0)
            else
                print_string colors.(max 1 (cls.(i).(j) mod (Array.length colors)));
            if mat.(i).(j) = '&' then
                print_char ' '
            else
                print_char mat.(i).(j)
        done;
        print_string "\027[0m";
        print_char '\n'
    done;;

let display_specific mat cls digit =
    let colors = [|"\027[0m"; "\027[41m"; "\027[42m"; "\027[43m"; "\027[44m"; "\027[45m"; "\027[46m"; "\027[47m"; "\027[100m"; "\027[101m"; "\027[102m"; "\027[103m"; "\027[104m"; "\027[105m"; "\027[106m"; "\027[107m"|] in
    for i = 0 to (Array.length mat -1) do
        for j = 0 to (Array.length mat.(i) -1) do
            if cls.(i).(j) <> digit then
                print_string colors.(0)
            else
                print_string colors.(max 1 (cls.(i).(j) mod (Array.length colors)));
            if mat.(i).(j) = '&' then
                print_char ' '
            else
                print_char mat.(i).(j)
        done;
        print_string "\027[0m";
        print_char '\n'
    done;;

let extend dgt mat cls i0 j0 dst =
    let ni = Array.length mat in
    let nj = Array.length mat.(0) in
    cls.(i0).(j0) <- dgt + 1;
    for i = -dst to dst do
        for j = -dst to dst do
            if i0 - i >= 0 && j0 - j >= 0 && i0 - i < ni && j0 - j < nj then begin
                cls.(i0-i).(j0-j) <- dgt + 1;
                if abs i = dst || abs j = dst then
                    mat.(i0-i).(j0-j) <- '*'
                else if i <> 0 || j <> 0 then
                    mat.(i0-i).(j0-j) <- '&'
                else
                    ()
            end
            else
                ()
        done
    done ;;

let identity x = x ;;

let copy_arr src =
    let dest = Array.make (Array.length src) [||] in
    for i = 0 to (Array.length src -1) do
        dest.(i) <- Array.make (Array.length src.(i)) src.(0).(0);
        for j = 0 to (Array.length src.(i) -1) do
            dest.(i).(j) <- src.(i).(j)
        done
    done;
    dest ;;

let rec pw x n = match n with
    | 0 -> 1
    | 1 -> x
    | k -> x * pw x (n-1) ;;

let to_hexa n =
    let res = ref "" in
    let remaining = ref n in
    if n = 0 then
        "0"
    else begin
        while !remaining > 0 do
            let cd = ref (48 + !remaining mod 16) in
            if !cd >= 58 then
                cd := !cd + 7
            else
                ();
            res := ((String.make 1 (Char.chr !cd)) ^ !res);
            remaining := !remaining / 16;
        done;
        !res ;
    end;;

let extremely_fancy_graph_printing g size wmult mode  =
    (* creation of the image *)
    let px = Array.make (size) [||] in
    for i = 0 to (size-1) do
        px.(i) <- Array.make (wmult*size) ' '
    done;

    (* color matrix *)
    let cls = Array.make (size) [||] in
    for i = 0 to (size-1) do
        cls.(i) <- Array.make (wmult*size) 0
    done;

    if Array.length g >= 100 then
        failwith "ERROR : graph is too big"
    else
        ();

    let coords = Array.make size (0, 0) in

    (* placing the points on the trig circle *)
    for k = 0 to Array.length g - 1 do
        let theta = 2. *. pi *. (float_of_int k) /. (float_of_int (Array.length g)) +. pi /. (float_of_int (Array.length g)) in
        let i = ref (int_of_float ((float_of_int size) /. 2.) + int_of_float ((float_of_int size) /. 2. *. cos theta)) in
        let j = ref (int_of_float ((float_of_int size) /. 2.) + int_of_float ((float_of_int size) /. 2. *. sin theta)) in
        if !i < 1 then i := 1 else ();
        if !j < 1 then j := 1 else ();
        if !i >= size-1 then i := size-1-1 else ();
        if !j >= size-1 then j := size-1-1 else ();
        px.(!i).(wmult* !j) <- '~';
        extend k px cls !i (wmult* !j) 2;
        let str_to_place = to_hexa k in
        for sl = 0 to (String.length str_to_place -1) do
            if !i + 1 < size && wmult* !j-sl-1 >= 0 && wmult* !j+sl-1 < Array.length px.(0) then
                px.(!i + 1).(wmult* !j-sl-1) <- str_to_place.[sl]
            else
                ()
        done;
        coords.(k) <- (!i, wmult* !j);
    done;

    let blankcls = copy_arr cls in
    let bpx = copy_arr px in
 
    (* draw the connections *)
    for i = 0 to Array.length g -1 do
        for j = 0 to Array.length g.(i) -1 do
            draw_line_bresenham px cls i (fst coords.(i)) (snd coords.(i)) (fst coords.(g.(i).(j))) (snd coords.(g.(i).(j))) 0
        done
    done;

    (* show the image *)
    ignore (Sys.command "clear");
    if mode = "SPECIFIC" then begin
        let halt = ref false in
        while !halt = false do
            
            Stdlib.print_endline "Enter the node you want to highlight (type -1 to show all; -2 to exit; -3 to stream all)";
            let nd = ref (-2) in
            
            nd := Scanf.bscanf Scanf.Scanning.stdin "%d\n" identity;
            ignore (Sys.command "clear");
                
            if !nd >= 0 && !nd < Array.length g then
                display_specific px cls (!nd+1)
            else if !nd = -1 then
                display px cls
            else if !nd = -2 then
                halt := true
            else if !nd = -3 then begin
                for i = 0 to (Array.length g -1) do
                    ignore (Sys.command "clear");
                    display_specific px cls (i+1);
                    Stdlib.print_endline "_";
                    ignore (Sys.command "sleep 1");
                done;
                display px cls;
                Stdlib.print_endline "_";
                ignore (Sys.command "sleep 1");
            end
            else
                ()
        done
    end
    else if mode <> "HIDE" then
        display px cls;
    (coords, px, bpx, cls, blankcls) ;;
    (* coords = (x, y) coords of graph point *)
    (* px = 2D matrix containing what to display *)
    (* cls = colors of associated pixel *)

let fancy_dfs gr coords px bcls = 
    Stdlib.print_endline "Enter any integer to begin DFS : ";
    
    ignore (Scanf.bscanf Scanf.Scanning.stdin "%d\n" identity);
    let n = Array.length gr in
    let visited = Array.make n false in
    visited.(0) <- true;
    let rec explore nd =
        if true then begin
            for i = 0 to (Array.length gr.(nd) -1) do
                draw_line_bresenham px bcls nd (fst coords.(nd)) (snd coords.(nd)) (fst coords.(gr.(nd).(i))) (snd coords.(gr.(nd).(i))) 4;
                display px bcls;
                Stdlib.print_endline "_";
                ignore (Sys.command "sleep 1");
                if visited.(gr.(nd).(i)) = false then begin
                    visited.(gr.(nd).(i)) <- true;
                    explore gr.(nd).(i);
                end
            done
        end
        else
            ()
    in
    explore 0;
    display px bcls ;;

(* --------------------------------------| TESTS |-------------------------------------- *)


(*
let gr = [|[|3; 5; 7|]; [|0|]; [|1; 7; 8|]; [|2; 6|]; [|0; 1; 3|]; [|6; 7|]; [|0; 1; 2|]; [|8|]; [|0; 7; 6|]; [||]; [||]; [|9|]|] ;;
    
let (coords, map, blank_map, color_map, blank_color_map) = extremely_fancy_graph_printing gr 46 3 "SHOW" ;;

fancy_dfs gr coords map blank_color_map ;;
*)

(* --------------------------------------------------------------------------------------------------------- *)
(* --------------------------------------------------------------------------------------------------------- *)
(* --------------------------------------------------------------------------------------------------------- *)
(* --------------------------------------------------------------------------------------------------------- *)
open Graphics ;;

type 'a tree = Leaf of 'a | Node of 'a * 'a tree * 'a tree ;;

(* 
STRUCT : (digit, xcoord, ycoord) 
*)

let rec pw x n = match n with
    | 0 -> 1
    | 1 -> x
    | k when k mod 2 = 0 -> let res = pw x (n/2) in res*res
    | k -> let res = pw x (n/2) in res*res*x ;;

let rec depth_of_tree t = match t with
    | Leaf _ -> 1
    | Node (_, g, d) -> 1 + max (depth_of_tree g) (depth_of_tree d) ;;

let fill_data te ystep sx sy r =
    let depth = depth_of_tree te in
    let res = Array.make (depth+1) [] in
    let rec aux t cur_x cur_d spacing = match t with
        | Node (x, g, d) -> begin
            aux g (cur_x - spacing) (cur_d+1) (spacing/2);
            res.(cur_d) <- ((x, (cur_x, sy - r - ystep * cur_d)))::(res.(cur_d));
            aux d (cur_x + spacing) (cur_d+1) (spacing/2);
        end
        | Leaf x -> begin
            res.(cur_d) <- ((x, (cur_x, sy - r - ystep * cur_d)))::(res.(cur_d));
        end
    in aux te (sx/2) 0 (r/2 + r * ((pw 2 (depth-1)) - 1)); res ;;

let rec draw_list l d r = match l with
    | [] -> ()
    | h::t -> draw_circle (fst (snd h)) (snd (snd h)) r; draw_list t d r ;;

let even_more_pretty_printing t r ystep =
    open_graph " 1600x800" ;
    let sx = Graphics.size_x () in
    let sy = Graphics.size_y () in

    let graphdata = fill_data t ystep sx sy r in

    print_int 3;

    for dpth = 0 to (Array.length graphdata -1) do
        draw_list graphdata.(dpth) dpth r
    done;

    ignore (Sys.command "sleep 1");

    close_graph () ;
    () ;;

even_more_pretty_printing (Node (2, Node (3, Leaf 1, Leaf 6), Node (9, Leaf 0, Node (4, Leaf 0, Node (2, Leaf 5, Leaf 2))))) 25 100 ;

(* compilation command : ocamlfind ocamlc -linkpkg -package unix -linkpkg -package graphics pretty_printing.ml *)
print_int 0 ;;
print_char '\n' ;;