(**************************************************************************)
(*                   Cameleon                                             *)
(*                                                                        *)
(*      Copyright (C) 2002 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 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 General Public License for more details.                      *)
(*                                                                        *)
(*      You should have received a copy of the GNU 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                                *)
(**************************************************************************)

(* Interfacing with Efuns. *)


open Cam_data
open Cam_types

let print_DEBUG s = print_string s ; print_newline ()

(** This values and types must be exactly the same as in the efuns server.ml file. *)

let efuns_property = "_EFUNS_SERVER"  
let user = try Sys.getenv "USER" with _ -> "noname"
let socket_name = Printf.sprintf "/tmp/efuns-server.%s.%s:0" user ""


type proto =
    LoadFile of string * int * string
  | SaveFile of string
  | IsFileModified of string
  | RepBool of bool
  | IsFileEdited of string
  | CloseFile of string

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

(* The list of files open in efuns. *)
let efuns_files = ref ([] : (Cam_types.file * Cam_data.data) list)

let remove_efuns_file file =
  efuns_files := List.filter (fun (f,_) -> file.f_abs_name <> f.f_abs_name) !efuns_files

let close_connection outc =
  try close_out outc 
  with Sys_error _ -> ()

let file_edited file =
  try
    print_DEBUG ("file_edited "^file.f_abs_name);
    let (inc,outc) = Unix.open_connection (Unix.ADDR_UNIX socket_name) in
    output_value outc (IsFileEdited file.f_abs_name) ;
    flush outc ; 
    let b = 
      match input_value inc with
	RepBool b -> b
      | _ ->
	  prerr_endline "Protocol error, RepBool expected";
	  false
    in
    close_connection outc ;
    b
  with
    Unix.Unix_error (e, s1, s2) ->
      prerr_endline ((Unix.error_message e)^": "^s1^" "^s2) ;
      false

let check_efuns_files () =
  List.iter 
    (fun (file, data) ->
      if file_edited file then
	(
	 print_DEBUG ("file "^file.f_abs_name^" still open");
	 ()
	)
      else
	(
	 print_DEBUG ("file "^file.f_abs_name^" was closed");
	 data#close_file file ;
	 remove_efuns_file file
	)
    )
    !efuns_files

class editor file =
  object
    method close =
      remove_efuns_file file;
      try
	let (inc,outc) = Unix.open_connection (Unix.ADDR_UNIX socket_name) in
	output_value outc (CloseFile file.f_abs_name) ;
	flush outc ; 
	close_connection outc
      with
	Unix.Unix_error (e, s1, s2) ->
	  raise (Failure ((Unix.error_message e)^": "^s1^" "^s2))

    method reload = ()
    method save =
      try
	let (inc,outc) = Unix.open_connection (Unix.ADDR_UNIX socket_name) in
	output_value outc (SaveFile file.f_abs_name) ;
	flush outc ; 
	close_connection outc
      with
	Unix.Unix_error (e, s1, s2) ->
	  raise (Failure ((Unix.error_message e)^": "^s1^" "^s2))

    method changed =
      try
	let (inc,outc) = Unix.open_connection (Unix.ADDR_UNIX socket_name) in
	output_value outc (IsFileModified file.f_abs_name) ;
	flush outc ; 
	let b = 
	  match input_value inc with
	    RepBool b -> b
	  | _ ->
	      prerr_endline "Protocol error, RepBool expected";
	      false
	in
	close_connection outc ;
	b
      with
	Unix.Unix_error (e, s1, s2) ->
	  prerr_endline ((Unix.error_message e)^": "^s1^" "^s2);
	  false

  end

let edit data ?char file =
  try
    let (inc,outc) = Unix.open_connection (Unix.ADDR_UNIX socket_name) in
    output_value outc (LoadFile (file.f_abs_name,
				 (match char with None -> 0 | Some n -> n),
				 "")); 
    flush outc ; 
    close_connection outc ;
    efuns_files := (file, data) :: !efuns_files ;
    new editor file
  with
    Unix.Unix_error (e, s1, s2) ->
      raise (Failure ((Unix.error_message e)^": "^s1^" "^s2))
