open Printf

module Ground_Pprz = Pprz.Messages(struct let name = "ground" end)
module LL = Latlong
open LL

type plume = { mutable utm_x : float; mutable utm_y : float; mutable value : int; utm_zone : int }

(* NW of Muret ref *)
let muret = utm_of WGS84 {LL.posn_lat=(Deg>>Rad)43.4624; posn_long=(Deg>>Rad)1.2727}
let royan = utm_of WGS84 {LL.posn_lat=(Deg>>Rad)45.7122; posn_long=(Deg>>Rad)(-1.2037)}
let source = fun () -> { utm_x = muret.LL.utm_x -. 300.; utm_y = muret.LL.utm_y -. 300.; value = 255; utm_zone = muret.LL.utm_zone}

let available_ids = ref []
let gen_id =
  let x = ref 0 in
  fun () -> 
    match !available_ids with
      [] ->
	incr x; !x
    | x::xs ->
	available_ids := xs;
	x

let dt = 1. (* s *)
let mixing_length = 5. (* m/s *)
let wind_x = ref 0.
let wind_y = ref 0.

let ivy_bus = ref "127.255.255.255:2010"


let plumes = Hashtbl.create 97
let t = ref 0 

let one_step = fun () ->
  incr t;

  (* New plume *)
  if !t mod 5 = 0 then
    Hashtbl.add plumes (gen_id ()) (source ());
  
  (* Eddy + wind *)
  Hashtbl.iter (fun id plume ->
    let a = Random.float (2.*.LL.pi) in
    plume.utm_x <- plume.utm_x +. (mixing_length*.cos a +. !wind_x)*. dt;
    plume.utm_y <- plume.utm_y +. (mixing_length*.sin a +. !wind_y)*. dt;
    plume.value <- plume.value - 1;
    if plume.value <= 0 then begin
      Hashtbl.remove plumes id;
      available_ids := id :: !available_ids;
    end)
    plumes 


let my_id = "diffusion"
let send_on_ivy = fun () ->
  if Hashtbl.length plumes > 0 then
  let ids = ref []
  and xs= ref []
  and ys = ref []
  and vs = ref [] in
  Hashtbl.iter (fun id plume ->
    ids := string_of_int id :: !ids;
    let wgs84 = LL.of_utm WGS84 {LL.utm_zone = plume.utm_zone ; utm_x = plume.utm_x; utm_y = plume.utm_y } in
    xs := sprintf "%.6f" ((Rad>>Deg)wgs84.posn_lat) :: !xs;
    ys := sprintf "%.6f" ((Rad>>Deg)wgs84.posn_long) :: !ys;
    vs := sprintf "%d" plume.value :: !vs)
    plumes ;
  let ids = String.concat "," !ids
  and xs = String.concat "," !xs
  and ys = String.concat "," !ys
  and vs = String.concat "," !vs in
  Ground_Pprz.message_send my_id "PLUMES"
    [ "ids", Pprz.String ids;
      "lats", Pprz.String xs;
      "longs", Pprz.String ys;
      "values", Pprz.String vs ]

let debug = let i = ref 0 in fun () ->
  incr i;
  let f = open_out (sprintf "plumes_%d.txt" !i) in
  Hashtbl.iter (fun id plume ->
    fprintf f "%.1f %.1f\n" plume.utm_x plume.utm_y)
    plumes;
  close_out f

let detect_distance = 20.


let flight_param_msg = fun _sender vs ->
  let lat = Pprz.float_assoc "lat" vs
  and long = Pprz.float_assoc "long" vs in
  let utm_ac = utm_of WGS84 {LL.posn_lat=(Deg>>Rad)lat; posn_long=(Deg>>Rad)long} in
  Hashtbl.iter (fun id plume ->
    let utm_plume = {LL.utm_zone = plume.utm_zone; utm_x = plume.utm_x; utm_y = plume.utm_y } in
    let d = utm_distance utm_ac utm_plume in
    if d < detect_distance then begin
      let ac_id = Pprz.string_assoc "ac_id" vs in
      for i = 0 to 2 do
	Ground_Pprz.message_send my_id "DL_SETTING"
	  ["ac_id", Pprz.String ac_id; "index", Pprz.Int i(***FIXME***); "value", Pprz.Float (float plume.value)]
      done
    end)
    plumes



let safe_bind = fun msg cb ->
  let safe_cb = fun sender vs ->
    try cb sender vs with x -> prerr_endline (Printexc.to_string x) in
  ignore (Ground_Pprz.message_bind msg safe_cb)

let gaia = fun time_scale _sender vs ->
  time_scale#set_value (Pprz.float_assoc "time_scale" vs);
  wind_x := (Pprz.float_assoc "wind_east" vs);
  wind_y := (Pprz.float_assoc "wind_north" vs)


let _ =
  Ivy.init "Paparazzi gaia" "READY" (fun _ _ -> ());
  Ivy.start !ivy_bus;

  let periodic = fun () ->
    one_step ();
    send_on_ivy ();
    (*** debug ();***)
    true in

  let time_scale = object val mutable v = 1. method value = v method set_value x = v <- x end in

  Stdlib.timer ~scale:time_scale dt periodic;

  safe_bind "FLIGHT_PARAM" flight_param_msg;
  safe_bind "WORLD_ENV" (gaia time_scale);

  Unix.handle_unix_error GMain.Main.main ()
