(*********************************************************************************)

(*                Cameleon                                                       *)
(*                                                                               *)
(*    Copyright (C) 2005,2006 Institut National de Recherche en Informatique     *)
(*    et en Automatique. All rights reserved.                                    *)
(*                                                                               *)
(*    This program is free software; you can redistribute it and/or modify       *)
(*    it under the terms of the GNU Library General Public License as            *)
(*    published by the Free Software Foundation; either version 2 of the         *)
(*    License, or  any later version.                                            *)
(*                                                                               *)
(*    This program is distributed in the hope that it will be useful,            *)
(*    but WITHOUT ANY WARRANTY; without even the implied warranty of             *)
(*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *)
(*    GNU Library General Public License for more details.                       *)
(*                                                                               *)
(*    You should have received a copy of the GNU Library General Public          *)
(*    License along with this program; if not, write to the Free Software        *)
(*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA                   *)
(*    02111-1307  USA                                                            *)
(*                                                                               *)
(*    Contact: Maxence.Guesdon@inria.fr                                          *)
(*                                                                               *)
(*********************************************************************************)


(* $Id: cam_server.ml 512 2007-01-19 09:02:29Z zoggy $ *)

let socket_file = Filename.concat
  Cam_config.socket_dir
    (Printf.sprintf ".cameleon_%s" Cam_messages.login)

let send_to_server command =
  try
    let fd = Unix.openfile socket_file
      [Unix.O_WRONLY]
        0
    in
    let oc = Unix.out_channel_of_descr fd in
    output_string oc (command^"\n");
    flush oc;
    close_out oc
  with
    Unix.Unix_error (e,s1,s2) ->
      failwith (Printf.sprintf "%s %s: %s" (Unix.error_message e) s1 s2)

let buf_size = 2048
let buf = String.create buf_size
let handle_input =
  let cur_pos = ref 0 in
  fun fd ->
    (*prerr_endline "read";*)
    match Unix.read fd buf !cur_pos (buf_size - !cur_pos) with
      0 ->
        (*prerr_endline "done";*)
        cur_pos := 0;
        true
    | n ->
        let s = String.sub buf 0 (!cur_pos + n) in
        (*
           prerr_endline (Printf.sprintf "done: %s" s);
        *)

        let nl =
          try Some (String.index s '\n')
          with Not_found -> None
        in
        (
         match nl with
           None -> cur_pos := !cur_pos + n
         | Some p ->
             let com = String.sub s 0 p in
             String.blit s (p+1) buf 0 (!cur_pos + n - p - 1);
             cur_pos := (!cur_pos + n - p - 1);
             Cam_hooks.display_message (Printf.sprintf "server received command: %s" com);
             (
              try Cam_commands.eval_command com
              with
                Failure s -> prerr_endline s
              | e -> prerr_endline (Printexc.to_string e)
             )
        );
        if !cur_pos >= buf_size then
          cur_pos := 0;
        true

let start_server () =
  try
    Unix.mkfifo socket_file 0o600 ;
    let fd = Unix.openfile socket_file [Unix.O_RDONLY ; Unix.O_NONBLOCK] 0o600 in
    (*  TODO: par sécurité, interdire l'usage de la commande "external" *)
    let f () =
      try
        match Unix.select [fd] [] [] 0.0 with
          [_],[],[] ->
            (* mystery: when we passed here once, we pass every time *)
            handle_input fd
        | _ -> true
      with
        Unix.Unix_error (e,s1,s2) ->
          prerr_endline (Printf.sprintf "%s %s: %s" (Unix.error_message e) s1 s2);
          true
      |        e ->
          prerr_endline (Printexc.to_string e);
          true
    in
    ignore (Glib.Timeout.add ~ms: 500 ~callback:f);

    Pervasives.at_exit
      (fun () ->
         (try Unix.close fd with _ -> ());
         (try Unix.unlink socket_file with _ -> ())
      );
  with
    Unix.Unix_error (e,s1,s2) ->
      failwith (Printf.sprintf "%s %s: %s" (Unix.error_message e) s1 s2)

let _ =
  Cam_commands.register
    { Cam_commands.com_name = "cam_start_server" ;
      com_args = [| |] ;
      com_more_args = None ;
      com_f = (fun _ -> start_server()) ;
    }