open Core.Std

type t =
{ 
  title: string;
  start_value: float;
  cur_value  : float;
  end_value  : float;
  bar_length : int;
  init_time  : Time.t;
  dirty      : bool;
  next       : Time.t;
}

let init ?(bar_length=20) ?(start_value=0.) ?(end_value=1.) ~title =
  { title ; start_value ; end_value ; bar_length ; cur_value=start_value ;
    init_time= Time.now () ; dirty = false ; next = Time.now () } 

let update ~cur_value bar =
  { bar with cur_value ; dirty=true }

let increment_end bar =
  { bar with end_value=(bar.end_value +. 1.) ; dirty=false }

let increment_cur bar =
  { bar with cur_value=(bar.cur_value +. 1.) ; dirty=true }

let display_tty bar =
    let percent =
      100. *. (bar.cur_value -. bar.start_value) /.
              (bar.end_value -. bar.start_value)
    in  
    let n_hashes =
      (Float.of_int bar.bar_length) *. percent /. 100.
      |> Float.to_int
    in
    let hashes = 
      String.init bar.bar_length ~f:(fun i ->
        if (i < n_hashes) then '#'
        else ' '
      )
    in
    let now =
      Time.now ()
    in
    let running_time = 
      Time.abs_diff now bar.init_time
    in
    let stop_time =
      let x = 
        Time.Span.to_float running_time
      in
      if (percent > 0.) then
        x *. 100. /. percent -. x
        |> Time.Span.of_float
      else
        Time.Span.of_float 0.
    in
    Printf.printf "%s : [%s] %4.1f%%  | %10s, ~%10s left\r%!"
      bar.title
      hashes
      percent
      (Time.Span.to_string running_time)
      (stop_time |> Time.Span.to_string );
    { bar with dirty = false ; next = Time.add now (Time.Span.of_float 0.1) }
  

let display_file bar =
    let percent =
      100. *. (bar.cur_value -. bar.start_value) /.
              (bar.end_value -. bar.start_value)
    in  
    let running_time = 
      Time.abs_diff (Time.now ()) bar.init_time
    in
    let stop_time =
      let x = 
        Time.Span.to_float running_time
      in
      if (percent > 0.) then
        x *. 100. /. percent -. x
        |> Time.Span.of_float
      else
        Time.Span.of_float 0.
    in
    Printf.printf "%5.2f %%  in  %20s, ~%20s left\n%!"
      percent
      (Time.Span.to_string running_time)
      (Time.Span.to_string stop_time);
    { bar with dirty = false ; next = Time.add (Time.now ()) (Time.Span.of_float 2.) }
  


let display bar =
  if (not bar.dirty) then
     bar
  else if (Time.now () < bar.next) then
     bar
  else
    begin
      if (Unix.isatty Unix.stdout) then
        display_tty bar
      else
        display_file bar
    end



