(**
 	Module: Interpreter	
	Description: Input wave -> interpretation -> output wave
	@author WANG Haisheng	
	Created: 15/05/2013	Modified: 14/08/2013
*)

open Aux;;
open Beam;;
open Process;;
open Faustio;;

exception Missing_Expression;;

let version = "Faustine: 0.0.1";;

 let set_GC () = 
	let _ = Gc.set { (Gc.get()) 
		       with Gc.minor_heap_size = 0xFFFFFF } in
	let _ = Gc.set { (Gc.get()) 
		       with Gc.major_heap_increment = 0xFFFFFF } in
	let _ = Gc.set { (Gc.get()) 
		       with Gc.space_overhead = 100 } in
	let _ = Gc.set { (Gc.get()) 
		       with Gc.max_overhead = 0xFFF } in
	let _ = Gc.set { (Gc.get()) 
		       with Gc.stack_limit = 0xFFFFF } in
	let _ = Gc.set { (Gc.get()) 
		       with Gc.allocation_policy = 1 } in
	() ;;

let path_dsp = ref "";;
let size_input = ref 0;;
let inputs = ref [];;
let time_max = ref 0xFFFF;;
let dir_output = ref "";;
let format_output = ref "";;
let basename_output = ref "";;
let stdout_filename = ref "";;

let option_usage = "usage: " ^ Sys.argv.(0) 
  ^ " [-f dsp_src] [-i input] [-l length] [--odir dir] [--oformat wav/csv/nst] [--obasename name]";;

let option_unknown = 
  fun x -> raise (Arg.Bad ("Bad argument : " ^ x))

let speclist = [
  ("-f", Arg.String (fun s -> path_dsp := s), ": faust .dsp source file");
  ("-i", Arg.String (fun s -> incr size_input; inputs := !inputs @ [s]), ": set input file");
  ("-l", Arg.Int (fun i -> time_max := i), ": maximun number of output samples");
  ("--odir", Arg.String (fun s -> dir_output := s), ": set output directory");
  ("--oformat", Arg.String (fun s -> format_output := s), ": set output format wav/csv/nst");
  ("--obasename", Arg.String (fun s -> basename_output := s), ": set output basename");
  ];;
 
let file_of_path : string -> string = 
  fun (path : string) ->
    let fragments = Str.split (Str.regexp "/") path in
    let n = List.length fragments in
    List.nth fragments (n - 1);;  

let get_extension : string -> string = 
  fun (file : string) ->
    let fragments = Str.split (Str.regexp "\.") file in
    let n = List.length fragments in
    List.nth fragments (n - 1);;

let chk_extension : string -> bool = 
  fun (file : string) ->
    let extension = get_extension file in
    if extension = "csv" || extension = "wav" || extension = "nst" then true
    else false;;

let chk_input_path : string -> bool =
  fun (path : string) ->
    let file_in = file_of_path path in
    chk_extension file_in;;

let stdinput = fun (x : unit) ->
  let path = Unix.readlink "/dev/fd/0" in
  if chk_input_path path then
    ( incr size_input;
      inputs := !inputs @ [path] )
  else ();;

let chk_output_path : string -> bool =
  fun (path : string) ->
    let fragments = Str.split (Str.regexp "/") path in
    let location = List.nth fragments 0 in
    if location = "dev" then false
    else true;;

let stdoutput = fun (x : unit) ->
  let path = Unix.readlink "/dev/fd/1" in
  if chk_output_path path then stdout_filename := path
  else ();;

let stdio = fun (x : unit) -> 
  stdinput ();
  stdoutput ();;

let main () = 

	let () = Arg.parse speclist option_unknown option_usage in
        let () = stdio () in 
        let _ = Sys.signal Sys.sigalrm Sys.Signal_ignore in
	let _ = set_GC () in
	let io = new iomanager in
	let () = io#set !stdout_filename !dir_output !format_output !basename_output in


	let () = output_string stderr ("\n    Faustine -> Reading input ...") in
	let tic0 = Unix.time () in
	let input : beam = io#read !inputs in
	let toc0 = Unix.time () in
	let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc0 -. tic0)) ^ "s.)\n") in


	let () = output_string stderr ("    Faustine -> Preprocessing...") in
	let tic1 = Unix.time () in
	let faust_core = Preprocess.preprocess !path_dsp in
	let toc1 = Unix.time () in
	let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc1 -. tic1)) ^ "s.)\n") in


	let () = output_string stderr ("    Faustine -> Constructing process...") in
	let tic2 = Unix.time () in
	let faust_exp = exp_of_string faust_core in
	let proc = (new proc_factory)#make faust_exp in
	let toc2 = Unix.time () in
	let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc2 -. tic2)) ^ "s.)\n") in


	let () = output_string stderr ("    Faustine -> Constructing signals...") in
	let tic3 = Unix.time () in
	let output : beam = proc#eval input in
	let toc3 = Unix.time () in
	let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc3 -. tic3)) ^ "s.)\n") in


        if (!stdout_filename = "" && !format_output = "") || (!format_output = "nst") 
               || (!stdout_filename <> "" && (get_extension !stdout_filename) = "nst") then (
          let () = output_string stderr ("    Faustine -> Evaluating...") in
	  let tic4 = Unix.time () in
	  let raws = output#output_values !time_max in
	  let rates = output#frequency in
	  let toc4 = Unix.time () in
	  let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc4 -. tic4)) ^ "s.)\n") in


	  let () = output_string stderr ("    Faustine -> Writing output...") in
	  let tic5 = Unix.time () in
	  let output_paths = io#write_nst rates raws in
	  let toc5 = Unix.time () in
	  let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc5 -. tic5)) ^ "s.)\n") in

	  let _ = Array.map (output_string stderr) (Array.map decorate output_paths) in
	  ()
        )

        else (
	  let () = output_string stderr ("    Faustine -> Evaluating...") in
	  let tic6 = Unix.time () in
	  let data = output#output !time_max in
	  let rates = output#frequency in
	  let toc6 = Unix.time () in
	  let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc6 -. tic6)) ^ "s.)\n") in


	  let () = output_string stderr ("    Faustine -> Writing output...") in
	  let tic7 = Unix.time () in
	  let output_paths = io#write rates data in
	  let toc7 = Unix.time () in
	  let () = output_string stderr (" Done. (duration: " ^ (string_of_float (toc7 -. tic7)) ^ "s.)\n") in

	  let _ = Array.map (output_string stderr) (Array.map decorate output_paths) in
	  ()
        );;

main();;