#include <memo.h>
#include <utility.h>
#include <sys/locking.h>

#define HEADER_SIZE (sizeof(long)+sizeof(char))
#define INFO_SIZE (sizeof(long)+sizeof(long)) 

// block size used in writes 
// must not exceed sizeof(__tmp_string)
#define BLOCK_SIZE (1024)
// max allowed size for locking is 100k
#define MAX_SIZE   (100000l)             

TMemo_file::TMemo_file(const char* a) : 
_fname(a), _fp(NULL), _isnew(NULL)
{ 
  // handles: open file or create               
  _fname.ext("mem");  
  if (!(_fp = fopen(_fname,"rb+")))
  {
    _fp = fopen(_fname,"wb+");
    _isnew = TRUE;
  }
  
  if (_fp == NULL) 
    error_box("File memo %s non creato per errori di disco", _fname); 

  // read (write) header
  if (_isnew)
  {
    _id_max = 0; 
    fwrite(&_id_max, sizeof(long), 1, _fp);
    char eof = 26;  // only to inhibit 'type'
    fwrite(&eof, sizeof(char), 1, _fp);    
  }
  else
    fread(&_id_max, sizeof(long), 1, _fp);
}

TMemo_file::~TMemo_file()
{ 
  if (_fp) fclose(_fp);
}

bool TMemo_file::_find_id(long id)
{                                
  if (id > _id_max && id < 0)
    error_box("File memo inconsistente");
  
  bool dndir = FALSE /* id > (_id_max/2l) */ ;
  
  _current = -1l; 

  long pos = dndir ? INFO_SIZE : HEADER_SIZE;
  fseek(_fp, pos, dndir ? SEEK_END : SEEK_SET);
  
  int ok = 0;  
  
  while (_current < id)
  {                                           
    // se si va all'indietro, questa non funzia: nonostante
    // fflush il file non e' tutto scritto su disco, e fread
    // ritorna 0 lasciando tutto al valore precedente
    // Perche'? Eh?
    fread(&_current, sizeof(long), 1, _fp);  
    fread(&_next_ofs, sizeof(long), 1, _fp);    
    if (_current < id)                    
    { 
      long ofs =  dndir ? -_next_ofs-INFO_SIZE-INFO_SIZE :
      _next_ofs+INFO_SIZE;
      
      ok = fseek(_fp, ofs, SEEK_CUR); 
    }
    if (ok) break;
  }               
  
  _current = ok ? -1l : id;
  return _current == id;
}  

bool TMemo_file::get_field(TTextfile& t, long id)
{          
  bool ok = _find_id(id);
  if (ok)
  {
    _current = id;
    
    // te lo faccio io, poi so' cazzi tua se sbagli
    t.destroy();
    
    TString256 line;
    
    // block read until done; transfer block to txt 
    // block by block
    
    long size = _next_ofs < BLOCK_SIZE ? _next_ofs : BLOCK_SIZE;  
    int  lcnt = 0;
    
    for (long cnt = 0; cnt < _next_ofs; cnt += size)
    {                               
      if (fread(__tmp_string, sizeof(char), (int)size, _fp) != (size_t)size)
        error_box("%s: unexpected end of file", _fname);

      // move block into text
      for(long j = 0; j <= size; j++)
      {
        char ch = j < size ? __tmp_string[j] : '\0';
        if (ch == '\n' || j == size)
        {
          line[lcnt] = '\0';
          t.append(line); 
          lcnt = 0;
        }
        else line[lcnt++] = ch;
      }
      
      // size of next read
      size = cnt + size < BLOCK_SIZE ? _next_ofs - cnt : BLOCK_SIZE;   
    }
  }
  return ok;
}

long TMemo_file::set_field(TTextfile& t, long id)
{ 
  bool ok = TRUE; 
  long ret = 0;                                             
  
  // append se -1 o se l'ID e' maggiore dell'ultima presente
  bool at_end = id == -1 || (id != -1 && id > _id_max);
  // the casinating insertion in the middol
  bool in_mid = FALSE;               
  
  if (!at_end) 
  {    
    ok = _find_id(id);     
    if (!ok)
    {
      // va inserito al posto di uno scancellato
      // vediamo dove
      long pos = HEADER_SIZE;
      fseek(_fp, pos, SEEK_SET);
      
      int ok = 0;  
      
      while (_current < id)
      {                                          
        fread(&_current,  sizeof(long), 1, _fp);  
        fread(&_next_ofs, sizeof(long), 1, _fp);    
        if (_current < id)                    
        { 
          long ofs = _next_ofs+INFO_SIZE;
          ok = fseek(_fp, ofs, SEEK_CUR);  
        }
        else  
        {         
          // position the luridissim cazz at bighinnin 
          // of privius bifor riding ids 
          long oo = (long)INFO_SIZE * -1l;
          fseek(_fp, oo, SEEK_CUR);
          break;
        }        
        if (ok) error_box("file error in %s", _fname);     
        in_mid = TRUE;
      }  
    }
  }
  
  if (at_end)  // the easy case
  {
    if (id == -1) id = ++_id_max; 
    // append test, update info
    long cnt = 0l;
    fseek(_fp, 0, SEEK_END);
    
    // lock file 
    _locking(_fileno(_fp), _LK_LOCK, MAX_SIZE);

    fwrite(&id,  sizeof(long), 1, _fp);
    // will overwrite later with byte count 
    long cpos = ftell(_fp);
    fwrite(&id,  sizeof(long), 1, _fp);
    
    TString256 line;
    for (int i = 0; i < t.lines(); i++)
    {
      line = t.line(i);      
      char c;
      
      for (int k = 0; c = line[k]; k++)
      { 
        fwrite(&c, sizeof(char), 1, _fp);  
        cnt++;
      }
      if (i < t.lines() - 1)
      {
        c = '\n';
        fwrite(&c, sizeof(char), 1, _fp);  
        cnt++;
      } 
    } 
    //                        
    fwrite(&id,  sizeof(long), 1, _fp);
    fwrite(&cnt, sizeof(long), 1, _fp);
    // update count
    fseek(_fp, cpos, SEEK_SET);
    fwrite(&cnt, sizeof(long), 1, _fp);      
    fseek(_fp, 0, SEEK_SET);
    fwrite(&_id_max, sizeof(long), 1, _fp);     
    
    _locking(_fileno(_fp), _LK_UNLCK, MAX_SIZE);
    
    fflush(_fp);
    ret = _find_id(id) ? id : -1;  
  }   
  else  // the rognous one
  {
    long to   = ftell(_fp) - INFO_SIZE;
    long from = in_mid ? to : to + _next_ofs + (2*INFO_SIZE); 
    
    TFilename tmp; tmp.temp();                   
    FILE* tfp = fopen(tmp, "wb+");

    fseek(_fp, 0, SEEK_SET);   
    
    long size = BLOCK_SIZE;   
    bool stop = FALSE;
    
    // copy file into temp up to TO pos               
    // use 1k blocks for speed
    for (int i = 0; !stop; i++)
    {
      if (to < (BLOCK_SIZE * (i+1)))
      {
        size = to - i*BLOCK_SIZE;   
        stop = TRUE;
      }
      fread (__tmp_string, sizeof(char), (int)size, _fp); 
      fwrite(__tmp_string, sizeof(char), (int)size, tfp); 
    }                        
    
    // write new text and update info
    fwrite(&id,  sizeof(long), 1, tfp);
    // will overwrite later with byte count 
    long cpos = ftell(tfp);
    fwrite(&id,  sizeof(long), 1, tfp);
    
    TString256 line; long cnt = 0l;
    for (i = 0; i < t.lines(); i++)
    {
      line = t.line(i);      
      char c;
      
      for (int k = 0; c = line[k]; k++)
      { 
        fwrite(&c, sizeof(char), 1, tfp);  
        cnt++;
      }
      if (i < t.lines() - 1)
      {
        c = '\n';
        fwrite(&c, sizeof(char), 1, tfp);  
        cnt++;
      } 
    } 
    //                        
    fwrite(&id,  sizeof(long), 1, tfp);
    fwrite(&cnt, sizeof(long), 1, tfp);  
    
    // write the rest of the file  
    stop = FALSE; 
    fseek(_fp, from, SEEK_SET);
    for (i = 0; !stop; i++)
    {
      int size = fread (__tmp_string, sizeof(char), BLOCK_SIZE, _fp); 
      if (size) fwrite(__tmp_string, sizeof(char), size, tfp);     
      stop = size < BLOCK_SIZE;
    }
    
    // update count
    fseek(tfp, cpos, SEEK_SET);
    fwrite(&cnt, sizeof(long), 1, tfp);
    fclose(tfp);
    // move files
    fclose(_fp);
    fcopy(tmp, _fname);
    remove(tmp);
    _fp = fopen(_fname, "rb+");
    ret = _find_id(id) ? 0 : -1;
  } 
  
  _dirty = TRUE;

  return ret;
}

bool TMemo_file::remove_field(long id)
{             
  bool ok = _find_id(id);
  if (ok)
  {        
    long to   = ftell(_fp) - INFO_SIZE;
    long from = to + _next_ofs + (2*INFO_SIZE); 

    TFilename tmp; tmp.temp();                   
    FILE* tfp = fopen(tmp, "wb+");

    fseek(_fp, 0, SEEK_SET);   
    
    long size = BLOCK_SIZE;   
    bool stop = FALSE;
    
    // copy file into temp up to TO pos               
    // use 1k blocks for speed
    for (int i = 0; !stop; i++)
    {
      if (to < (BLOCK_SIZE * (i+1)))
      {
        size = to - i*BLOCK_SIZE;   
        stop = TRUE;
      }
      fread (__tmp_string, sizeof(char), (int)size, _fp); 
      fwrite(__tmp_string, sizeof(char), (int)size, tfp); 
    }
    // write the rest of the file  
    stop = FALSE; 
    fseek(_fp, from, SEEK_SET);
    for (i = 0; !stop; i++)
    {
      int size = fread (__tmp_string, sizeof(char), BLOCK_SIZE, _fp); 
      if (size) fwrite(__tmp_string, sizeof(char), size, tfp);     
      stop = size < BLOCK_SIZE;
    }
    
    // move files
    fclose(tfp);
    fclose(_fp);
    fcopy(tmp, _fname);
    remove(tmp);
    _fp = fopen(_fname, "rb+");
  }
  return ok;
}


bool TMemo_file::edit_field(long id)  
{          
  return TRUE;
}
