(* The implementation *)

type chunk = {
  mutable pos : int64;
  mutable len : int64;
  mutable filename : string option;
  mutable next : chunk option;
}

type t = {
  filename : string;
  size : int64;
  mutable chunks : chunk;
}

let create filename size = 
  let dirname = Printf.sprintf "%s.chunks" filename in
  Unix.mkdir dirname;
  {
    filename = filename;
    size = size;
    chunks = {
        pos = Int64.zero; 
        len = size; 
        created = None;
        next = None;
       }; 
  }

let chunk_min_size = ref 65000

let split_chunk c pos =
  let cc = {
    created = None;
    pos = pos;
    len = c.len -- pos;
   } in
  c.next <- Some cc;
  c.len <- pos -- c.pos

let rec extend_chunk c size = 
  match c.next with
    None -> 
     lprintf "Cannot extend last chunk\n";
     assert false
  | Some cc ->
     if cc.created <> None then
       begin
         append_chunk c cc;
         remove_chunk cc;
         c.next <- cc.next;
         c.len <- (cc.pos ++ cc.len) -- c.pos;
       end
     else
     if cc.len >= size ++ !chunk_min_size then 
       begin
         split_chunk cc (cc.pos ++ size);
         extend_chunk c size;
       end 
     else
       begin
         c.next <- cc.next;
         c.len <- (cc.pos ++ cc.len) -- c.pos;
         ftruncate_chunk c (c.len ++ cc.len);
       end

let open_chunk c = ()
let create_chunk c = () 

let get_chunk t pos len = 
  let c = t.chunks in
  let rec iter c =

    if c.created <> None && c.pos <= pos && c.pos ++ c.len >= pos ++ len then
       (open_chunk c, pos -- c.pos)
    else
    if c.created <> None && pos <= c.pos ++ c.len ++ !chunk_min_size then
      begin
        extend_chunk c (maxi !chunk_min_size (pos ++ len -- c.pos -- c.len ++ !chunk_min_size));
        iter c
      end
    else
    if c.pos ++ c.len <= pos then
      match c.next with
      | None -> 
         lprintf "Invalid access in file pos %Ld is after last chunk\n" pos;
         assert false
      | Some c -> iter c
    else
    if c.pos ++ !chunk_min_size < pos then 
      begin
        split_chunk c pos;
        iter c     
      end
    else
    if c.pos ++ c.len > pos ++ (maxi len !chunk_min_size) ++ !chunk_min_size then
      begin
        split_chunk c (pos ++ (maxi len !chunk_min_size) ++ !chunk_min_size);
        iter c
      end
    else 
      begin
        create_chunk c;
        iter c
      end
  in
  iter c

let build t =
  if t.chunks.created = None then create_chunk c;
  while t.chunks.next <> None do
    extend_chunk c t.size
  done;
