///////////////////////////////////////////////////////////////////////////////
//                             Lisaac Library                                //
//                                                                           //
//                   LSIIT - ULP - CNRS - INRIA - FRANCE                     //
//                                                                           //
//   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 3 of the License, or       //
//   (at your option) 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, see <http://www.gnu.org/licenses/>.   //
//                                                                           //
//                     http://isaacproject.u-strasbg.fr/                     //
///////////////////////////////////////////////////////////////////////////////
Section Header
  
  + name    := ABSTRACT_FILE;


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
  
  - comment := "Abstract File Management";
  
Section Inherit
  
  + parent_entry:ENTRY := ENTRY;   
  
Section Public
    
  + cursor:UINTEGER_32;
  
  - set_cursor n:UINTEGER_32 <-
  (
    cursor := n;
  );
  
  //
  // Update.
  //
  
  - refresh:BOOLEAN <-
  (
    physical_make
  );
    
  - is_empty:BOOLEAN <- size = 0;
  // Is collection empty ?
  
  - this:ENTRY <- parent_entry;
  
  //
  // Read.
  //
  
  - read dest:OBJECT :INTEGER <- 
  ( 
    array_intern.make_with_map_object dest;
    read_bound array_intern size (dest.object_size)
  ); 
  
  - read dest:ARRAYED size nb_elt:INTEGER :INTEGER <-
  ( 
    dest.set_capacity (dest.count + nb_elt);
    read_bound dest size nb_elt
  );
  
  //
  // Write.
  //
  
  - write src:ARRAYED from start:INTEGER size nb_elt:INTEGER :INTEGER <-
  ( + result:INTEGER;
    + elt_size,elt_ofs,size_byte:INTEGER;
    + size_block:INTEGER;
    + idx_elt:INTEGER;
    ? {(start + nb_elt - 1) <= src.upper};
    ? { is_open };
    ? { nb_elt > 0};
    ? {src!=NULL};

    elt_size:=src.element_sizeof;
    size_byte:=nb_elt * elt_size;    
    basic_seek cursor;
    cursor := cursor + size_byte.to_uinteger_32;
    size_block := 2048;
    idx_elt := start;
    {size_byte > 0}.while_do {
      (size_byte < 2048).if {
	size_block := size_byte;
      };
      0.to (size_block - 1) do {i:INTEGER;
	tmp_buffer.put (src.item_byte idx_elt offset elt_ofs) to i;
	elt_ofs := elt_ofs+1;
	(elt_ofs = elt_size).if {
	  elt_ofs := 0;
	  idx_elt := idx_elt + 1;
	};
      };
      result := result + basic_write tmp_buffer size size_block;
      size_byte := size_byte - size_block;
    };
    result        
  );
  
  //
  // Close.
  //
  
  - close:ENTRY <-
  ( + result:ENTRY;
    + dir:DIRECTORY;
    
    result := parent_entry.close;
    cursor := 0;
    (basic_close).if {
      refresh;
      dir := get_parent_intern (result.path);
      (dir != NULL).if {
	dir.refresh;
      };
    } else {
      result := NULL;
    };
    result
  );
  
Section ENTRY
  
  - make e:ENTRY :BOOLEAN <-
  (
    parent_entry := e;
    physical_open
  );

  - make_read_only e:ENTRY :BOOLEAN <-
  (
    parent_entry := e;
    physical_open_read_only
  );
  
Section Private  
  
  - read_bound dest:ARRAYED size nb_elt:INTEGER :INTEGER <-
  ( + result:INTEGER;
    + elt_size,size_byte:UINTEGER_32;
    + size_block:UINTEGER_32;
    
    ? { is_open };
    ? { nb_elt > 0};
    ? { dest!=NULL};
    
    elt_size  := dest.element_sizeof.to_uinteger_32;
    size_byte := nb_elt.to_uinteger_32 * elt_size;
    
    ((cursor + size_byte) > size).if {
      size_byte := size - cursor;
    };
    
    basic_seek cursor;
    size_block := 2048;
    
    array_intern2.make_with_map_object tmp_buffer;
    {size_byte > 0}.while_do {
      (size_byte < 2048).if {
	size_block := size_byte;
      };
      result := result + basic_read tmp_buffer size size_block;
      cursor := cursor + size_block;

      array_intern2.set_upper ((size_block - 1).to_integer);
      dest.add_last_buffer array_intern2 from 0 to (size_block - 1);
      size_byte := size_byte - size_block;
      array_intern2.clear;
    };
    result    
  );
    
  + array_intern:FAST_ARRAY[UINTEGER_8] := FAST_ARRAY[UINTEGER_8].clone;
  
  + array_intern2:FAST_ARRAY[UINTEGER_8] := FAST_ARRAY[UINTEGER_8].clone;
  
  - tmp_buffer:NATIVE_ARRAY[UINTEGER_8] := NATIVE_ARRAY[UINTEGER_8].create 2048; // 512 byte
  
  //
  // Physical implementation.
  //
  
  - physical_open:BOOLEAN <-
  ( 
    deferred;
    FALSE
  ); 

  - physical_open_read_only:BOOLEAN <-
  ( 
    deferred;
    FALSE
  ); 
    
  - basic_seek pos:INTEGER :INTEGER <-
  // return size read or 0 if end of input (-1 on error => exception ?)
  ( 
    ? {is_open};    
    deferred;
    0
  );
  
  - basic_read buf:NATIVE_ARRAY[UINTEGER_8] size s:INTEGER :INTEGER <-
  // return size read or 0 if end of input (-1 on error => exception ?)
  ( 
    ? {is_open};
    deferred;
    0
  );
  
  - basic_write buf:NATIVE_ARRAY[UINTEGER_8] size s:INTEGER :INTEGER <-
  // return size read or 0 if end of input (-1 on error => exception ?)
  ( 
    ? {is_open};
    deferred;
    0
  );
  
  - basic_close:BOOLEAN <-
  ( 
    deferred;
    FALSE
  );
  
  