(*********************************************************************************)
(*                Chamo                                                          *)
(*                                                                               *)
(*    Copyright (C) 2003-2021 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 Lesser General Public License version        *)
(*    3 as published by the Free Software Foundation.                            *)
(*                                                                               *)
(*    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                                          *)
(*                                                                               *)
(*********************************************************************************)

module Id = Stk.Misc.Id()

type id = Id.t
let id_wrapper = Id.wrapper

let string_of_id id = string_of_int (Id.to_int id)
let id_of_string id = Id.of_int (int_of_string id)

type multiclip = {
  abst_len : int ;
  mutable elts : (string * string) Id.Map.t ;
  }

let default_abst_len = 80;;

let create_multiclip ?(abst_len=default_abst_len) () =
  { abst_len = abst_len ;
    elts = Id.Map.empty ;
  }
;;
(*c==v=[String.chop_n_char]=1.0====*)
let chop_n_char n s =
  let len = String.length s in
  if len <= n +1 || n < 0 then
    s
  else
    Printf.sprintf "%s..." (String.sub s 0 (n+1))
(*/c==v=[String.chop_n_char]=1.0====*)

(*c==v=[String.split_string]=1.1====*)
let split_string ?(keep_empty=false) s chars =
  let len = String.length s in
  let rec iter acc pos =
    if pos >= len then
      match acc with
        "" -> []
      | _ -> [acc]
    else
      if List.mem s.[pos] chars then
        match acc with
          "" ->
            if keep_empty then
              "" :: iter "" (pos + 1)
            else
              iter "" (pos + 1)
        | _ -> acc :: (iter "" (pos + 1))
      else
        iter (Printf.sprintf "%s%c" acc s.[pos]) (pos + 1)
  in
  iter "" 0
(*/c==v=[String.split_string]=1.1====*)

let replace_blanks s =
  let l = split_string s ['\n';'\r';'\t';' '] in
  String.concat " " l
;;

let gen_id =
  let rec iter map =
    let id = Id.gen () in
    match Id.Map.find_opt id map with
    | None -> id
    | Some _ -> iter map
  in
  iter

let add t ?id ?abs contents =
  let id = match id with
    | None -> gen_id t.elts
    | Some id -> id
  in
  let abstract =
    match abs with
      None -> replace_blanks (chop_n_char t.abst_len contents)
    | Some s -> replace_blanks s
  in
  t.elts <- Id.Map.add id (abstract, contents) t.elts;
  id
;;

let elements t = List.map (fun (id,(abs,str)) -> (id,abs,str)) (Id.Map.bindings t.elts)
let ids t = List.sort Id.compare (List.map (fun (id,_) -> id) (Id.Map.bindings t.elts))
let get t id = Id.Map.find_opt id t.elts
let remove t id = t.elts <- Id.Map.remove id t.elts

type storable_multiclip =
  { mutable clip : multiclip ;
    file : string ;
    op_group : [`Open] Ocf.group ;
    op_abst_len : int Ocf.conf_option ;
    op_elts : (id * string * string) list Ocf.conf_option ;
  }

let create_storable_multiclip ?(abst_len=default_abst_len) file =
  let op_elts = Ocf.list Ocf.Wrapper.(triple id_wrapper string string) [] in
  let g = Ocf.add Ocf.group ["elements"] op_elts in
  let op_abst_len = Ocf.int abst_len in
  let g = Ocf.add g ["abstract_length"] op_abst_len in
  let clip = create_multiclip ~abst_len () in
  { clip = clip ;
    file = file ;
    op_group = g ;
    op_abst_len = op_abst_len ;
    op_elts = op_elts ;
  }

let read_multiclip t =
  Ocf.from_file t.op_group t.file;
  let clip = create_multiclip ~abst_len: (Ocf.get t.op_abst_len) () in
  List.iter
    (fun (id, abs, contents) -> ignore(add clip ~id ~abs contents))
    (Ocf.get t.op_elts);
  t.clip <- clip
;;

let write_multiclip t =
  Ocf.set t.op_elts (elements t.clip);
  Ocf.set t.op_abst_len t.clip.abst_len ;
  Ocf.to_file t.op_group t.file
;;

let storable_get_multiclip t = t.clip;;
let storable_get_file t = t.file;;


