(**************************************************************************)
(*                   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                                *)
(**************************************************************************)

open Ocamlcvs.Types
open Cam_types

let print_DEBUG s = print_string s ; print_newline ()

let (!!) = Options.(!!)

(* Return the extension of the given filename, or [""].
   The extension does not include the '.', and is the string
   after the last '.'. *)
let extension filename =
  try
    let basename = Filename.basename filename in
    let pos = (String.rindex basename '.') + 1 in
    let len = String.length basename in
    String.sub basename pos (len - pos)
  with
    _ -> ""

let file_type_of_name filename =
  let rec f filetypes = 
    match filetypes with
      [] -> 
	print_DEBUG ("unknown file type") ;
	Cam_types.unknown_file_type ()
    | ft :: q ->
	print_DEBUG ("Testing with pattern "^ft.Cam_types.ft_name);
	if Str.string_match ft.Cam_types.ft_regexp filename 0 then
	  ft
	else
	  f q
  in
  f !!Cam_config.file_types

class data () = 
  object (self)
    val mutable elements = ([] : file list)
    
    method elements dir =
      List.filter
	(fun f -> 
	  (Filename.dirname f.f_name) = dir)
	elements

    method update_element f =
      let rec iter = function
	  [] ->
	    [f]
	| t :: q when t.f_name = f.f_name ->
	    (* A VOIR : si les dates ont changé, il faut
	       faire un reload si le fichier est édité *)
	    f :: q
	| t :: q ->
	    t :: (iter q)
      in
      elements <- iter elements

    method remove_element file =
      elements <- List.filter
	  (fun t -> t.f_name <> file)
	  elements

    method cvs_info_of_t (f : file) = 
      { cvs_file = f.f_name ;
	cvs_status = f.f_status ;
	cvs_work_rev = f.f_work_rev ;
	cvs_rep_rev = f.f_rep_rev ;
	cvs_date = f.f_date ;
	cvs_date_string = f.f_date_string
      }	
    method t_of_cvs_info (ci : cvs_info) = 
      let abs_name = Cam_misc.absolute_name ci.cvs_file in
      { f_name = ci.cvs_file ;
	f_abs_name = abs_name ;
	f_status = ci.cvs_status ;
	f_work_rev = ci.cvs_work_rev ;
	f_rep_rev = ci.cvs_rep_rev ;
	f_date = ci.cvs_date ;
	f_date_string = ci.cvs_date_string ;
	f_type = file_type_of_name abs_name
      }

    (* Get a file from a filename. Return an exising file if
       we have it, or get it from a 'cvs status' command. 
       Return None if the file is not handled by CVS. *)
    method file_of_string filename = 
      try
	Some (List.find (fun f -> f.f_name = filename) elements)
      with
	Not_found ->
	  if !Cam_args.with_rep then
	    try
	      match Ocamlcvs.Commands.status_files [filename] with
		[cvs_info] -> Some (self#t_of_cvs_info cvs_info)
	    | _ -> None
	    with
	      CvsFailure s ->
		prerr_endline s ;
		None
	  else
	    let cvs_info =
	      {
		cvs_file = filename ;
		cvs_status = Unknown ;
		cvs_work_rev = "" ;
		cvs_rep_rev = "" ;
		cvs_date_string = "" ;
		cvs_date = Unix.time () ;
	      }	
	    in
	    Some (self#t_of_cvs_info cvs_info)

    (* Reasociate the file types to the elements. *)
    method reassociate_filetypes =
      List.iter 
	(fun f -> f.f_type <- file_type_of_name f.f_abs_name)
	elements

    (* Edited files management *)
    val mutable edited_files = ([] : (file * editor_app) list)
    method edited_files = edited_files
    method is_edited file =
      List.exists (fun (f, _) -> f.f_name = file.f_name) edited_files
    method file_editor file =
      snd (List.find (fun (f, _) -> f.f_name = file.f_name) edited_files)
    method file_changed file =
      try (self#file_editor file)#changed
      with Not_found -> false
    method save_file file =
      try (self#file_editor file)#save
      with Not_found -> ()
    method edit_file file editor = 
      if self#is_edited file then ()
      else edited_files <- (file, editor) :: edited_files
    method close_file file = 
      edited_files <- List.filter
	  (fun (f, _) -> f.f_name <> file.f_name)
	  edited_files
      
    method close_files_before_exit =
      List.iter
	(fun (f, editor) ->
	  match f.f_type.ft_edit with
	    Efuns | Emacs | XEmacs -> ()
	  | _ -> editor#close
	)
	edited_files
  end

let data = new data ()
