(***************************************************************)
(*                        Reactive Asco                        *)
(*             http://reactiveml.org/reactive_asco             *)
(*                                                             *)
(*                                                             *)
(*  Authors: Guillaume Baudart (guillaume.baudart@ens.fr)      *)
(*           Louis Mandel (louis.mandel@lri.fr)                *)
(*                                                             *)
(***************************************************************)

(** Handle inputs received from Max/MSP via UDP or simulate the output
    of the listening machine.
*)

open Types
open Utils

(** Manage input from the listening machine with a priority queue. *)
let process event_scheduler listen adding =
  let process spy listen deadline =
    loop
      await immediate one listen (ev) in
      emit deadline ev.index;
      pause
    end
  in
  let rec sending i sl =
    match sl with
    | [] -> ()
    | (h, sh)::q ->
        if h = i then
          (emit sh Detected; sending i q)
        else
          (emit sh (Missed i); sending i q)
  in
  signal deadline in
  run (Reactive_queue.scheduler deadline sending adding) ||
  run (spy listen deadline)

(** Wait for an instrumental event and return its status (detected or
    missed).
*)
let process wait_event i add_event listen =
  await immediate one listen (ev) in
  if ev.index = i then
    Detected
  else if ev.index > i then
    Missed i
  else
    signal s in
    emit add_event (i, s);
    await immediate one s (status) in
    status

(** Create a scheduler for instrumental events and the corresponding
    waiting process.
*)
let make_event_scheduler listen =
  signal add_event in
  let wait_event i = wait_event i add_event listen in
  let event_scheduler = event_scheduler listen add_event in
  event_scheduler, wait_event

(** Receive message from Max via UDP and send them to the motor. *)
let process udp in_port listen =
  let maxlen = 1024 in
  let sock_event = Network.init_server in_port in
  print_endline ("waiting on port : "^(string_of_int in_port));
  let rec process max_server =
    let newmsg = String.create maxlen in
    begin try
      let len,_ = Unix.recvfrom sock_event newmsg 0 maxlen [] in
      let msg = String.sub newmsg 0 len in
      let s = Str.split (Str.regexp "[ \t]+") msg in
      let c = int_of_string (List.nth s 0) in
      let b = float_of_string (List.nth s 1) /. 60. in
      let nev = {index = c; bps=b;} in
      emit listen nev
    with _ -> () end;
    pause;
    run max_server
  in
  run max_server

(** Send messages on signal events corresponding to the simulation with
    [errors] the probability of error.
*)
let process simulator simu errors freq listen =
  signal clock in
  signal t in
  let period = 1. /. freq in
  (* Time in seconds (same as elapsed but without the moving tempo) *)
  let process time =
    let x = ref 0.0 in
    loop
      x := !x +. period;
      emit t !x;
      pause
    end
  in
  (* Play the simulation with a probability of error 'error'
     for each event *)
  let rec process play s =
    match s with
    | [] -> ()
    | (i, d, b)::s' ->
          await immediate one t (e) in
          if d < e then
            let p = Random.float 100.0 in
            begin if p > errors then
              let nev = {index = i; bps = b /. 60.;} in
              emit listen nev
            end;
            run (play s')
          else
            (pause; run (play s))
  in
  do run time when clock done ||
  run (Time.emit_clock clock freq) ||
  run (play simu)