#include <multirec.h>

void TMultiple_rectype::set_body_key(TRectype & rowrec)
{
  const RecDes* recd = rowrec.rec_des();    // Descrizione del record della testata
  const KeyDes& kd = recd->Ky[0];       // Elenco dei campi della chiave 1
  
    // Copia tutti i campi chiave, tranne l'ultimo, in tutti i records
  for (int i = kd.NkFields-2; i >= 0; i--)
  {                        
    const int nf = kd.FieldSeq[i] % MaxFields;
    const RecFieldDes& rf = recd->Fd[nf];  
    const TString& val = get(rf.Name);
    rowrec.renum_key(rf.Name, val);
  }
}

void TMultiple_rectype::load_rows_file(int logicnum)
{
  const int index = log2ind(logicnum);
  TRectype * rec = new_body_record(logicnum);
  set_body_key(*rec);
  if (_files.objptr(index) == NULL)
  {
    // crea
    TRecord_array * r = new TRecord_array(logicnum, (TString &) _numfields[index]);
    _files.add( r, index);
  }
  ((TRecord_array &)_files[index]).read(rec);  // rilegge
  _changed[index]=FALSE;
}

int TMultiple_rectype::find(int logicnum, const char * fieldname, const char * s, int from, bool reverse) const
{
  const TRecord_array & recarray = body(logicnum);
  const int last = recarray.last_row();
  const int len = s ? strlen(s) : 0;

  if (reverse)
  {
    if (from > 0)
    {
      if (len == 0)
        return from - 1;
      for (int i = recarray.pred_row(from); i > 0; i = recarray.pred_row(i))
        if (recarray[i].get(fieldname) == s)
          return i;
    }
  }
  else
  {
    if (last > from)
    {
      if (len == 0)
        return from + 1;
      for (int i = recarray.succ_row(from); i <= last; i = recarray.succ_row(i))
        if (recarray[i].get(fieldname) == s)
          return i;
    }
  }
  return -1;
}

int TMultiple_rectype::write_rewrite(TBaseisamfile & f, bool re) const
{
  int err = NOERR;

  if (_nuovo && re)           // E' nuovo di zecca! quindi ...
    re = FALSE;               // ... non fare la rewrite

  if (re)
  {
    for (int i = _nfiles - 1; err == NOERR && i >= 0 ; i--)
    {
      TRecord_array * r = (TRecord_array *) _files.objptr(i);
      if (r && !_changed[i])
        err = r->write(re);
    }
    // rewrite:
    if (err == NOERR)
    {
      err = TRectype::rewrite(f);
      if (err != NOERR)
        err = TRectype::write(f);
    }
  }
  else
  {
    // write:
    TMultiple_rectype & myself = *(TMultiple_rectype *)this;
    const bool to_complete = !myself.key_complete();
    if (to_complete)
      myself.renum();
    myself.renum_key(); 
    myself._nuovo |= to_complete;
    if (_nuovo)
    {
      do
      {
        err = TRectype::write(f);
        if (err == _isreinsert && myself.renum())
           myself.renum_key();
      } while (err == _isreinsert);
      myself._nuovo = (err != NOERR);
    }
    else
    {
      err = TRectype::write(f);
      if (err != NOERR)
        err = TRectype::rewrite(f);
    }
    for (int i = _nfiles - 1; err == NOERR && i >= 0 ; i--)
    {
      TRecord_array * r = (TRecord_array *)_files.objptr(i);
      if (r && !_changed[i])
        err = r->write(re);
    }
  }
  return err;
}

void TMultiple_rectype::remove_body(int logicnum)
{
  const int index = log2ind(logicnum);

  if (_files.objptr(index) != NULL)
    _files.remove(index);
}

int TMultiple_rectype::log2ind(int logicnum) const
{
  if (logicnum == 0)
    return 0;
  for (int i = _nfiles - 1; i >= 0 ; i--)
    if (_logicnums[i] == logicnum)
      return i;
  NFCHECK("Can't find file %d in multiple record", logicnum);
  return 0;
}

TRecord_array & TMultiple_rectype::body(int logicnum) const
{
  const int index = log2ind(logicnum);

  if (_files.objptr(index) == NULL ||   _changed[index])
    ((TMultiple_rectype *) this)->load_rows_file(_logicnums[index]);
  return (TRecord_array &) _files[index];
}

void TMultiple_rectype::renum_key()
{
  for (int i = _nfiles - 1; i >= 0 ; i--) 
  {
    const int logicnum = _logicnums[i];
    TRecord_array * b = (TRecord_array *) _files.objptr(i);
    if (b)
    {
      TRectype * rec = new TRectype(b->key());

      set_body_key(*rec);
      b->set_key(rec); // Aggiorna righe
    }
  }
}


TRectype & TMultiple_rectype::operator =(const TRectype & r)
{
  TRectype::operator=(r);
  reset_fields(*this);
  set_fields(*this);
  return *this;
}

TRectype & TMultiple_rectype::operator =(const char * r)
{
  TRectype::operator=(r);
  reset_fields(*this);
  set_fields(*this);
  return *this;
}

void TMultiple_rectype::zero(char c)
{
  reset_fields(*this);
  TAuto_variable_rectype::zero(c);
  for (int i = _nfiles - 1; i >= 0 ; i--)
    if (_files.objptr(i) != NULL)
      ((TRecord_array &)_files[i]).destroy_rows();
}

int TMultiple_rectype::readat(TBaseisamfile& f, TRecnotype nrec, word lockop)
{
  int err = NOERR;

  _nuovo = FALSE;
  err = TRectype::readat(f, nrec, lockop);
  synchronize_bodies();
  return err;
}

int TMultiple_rectype::read(TBaseisamfile & f, word op, word lockop)
{
  int err = NOERR;

  _nuovo = FALSE;
  if (op == _isequal)
  {
    TRectype r(*this);
  
    err = TRectype::read(f, op, lockop);
    if (err != NOERR)
    {
      *this = r;
      _nuovo = TRUE;
    }
  }                
  else
    err = TRectype::read(f, op, lockop);
  synchronize_bodies();
  return err;
}


void TMultiple_rectype::synchronize_bodies()
{
  for (int i = _nfiles - 1; i >= 0 ; i--)
  {
    if (_nuovo)          
    {   
      TRecord_array * b = (TRecord_array *) _files.objptr(i);
      if (b != NULL)
      {
        _changed[i] = FALSE;
        b->destroy_rows();  
        TRectype * r = new_body_record(_logicnums[i]);
        set_body_key(*r);
        b->set_key(r);
      }
    }
    else
    {
      if (_autoload[i])
        load_rows_file(_logicnums[i]);
      else
        if (_files.objptr(i) != NULL)
          _changed[i] = TRUE;
    }
  }
}

// @mfunc Copia il multiple rectype r su quello corrente
TMultiple_rectype & TMultiple_rectype::copy(const TMultiple_rectype& r)
{
  // copia..  
  TAuto_variable_rectype::operator=((TAuto_variable_rectype&)r);
  _files = r._files;
  _nuovo=r._nuovo;
  _nfiles=r._nfiles;    // file delle righe
  for (int i = _nfiles - 1; i >= 0; i--) 
  {
    _logicnums[i] = r._logicnums[i];
    _changed[i] = r._changed[i];
    _autoload[i] = r._autoload[i];
  }
  _numfields=r._numfields;   
  return *this;
}

// @mfunc Copia il multiple rectype r su quello corrente
int TMultiple_rectype::loaded_rows(int logicnum) const
{          
  const int index = log2ind(logicnum);       
  TRecord_array * r = (TRecord_array *) ((TMultiple_rectype *) this)->_files.objptr(index);
  
  if (_changed[index] ||  r == NULL) 
    return 0;
  return r->rows();
}

int TMultiple_rectype::remove(TBaseisamfile & f) const
{
  int err = NOERR;

  for (int i = _nfiles - 1; err == NOERR && i >= 0 ; i--)
  {
    TRecord_array & r = body(_logicnums[i]);
    err = r.remove();
  }
  if (err == NOERR)
    err = TRectype::remove(f);
  return err;
}

// @doc INTERNAL
TMultiple_rectype::TMultiple_rectype(int hfn)
                  : TAuto_variable_rectype(hfn), _nuovo(TRUE), _nfiles(0)
{
}

void TMultiple_rectype::enable_autoload(int logicnum,bool on)
{
  const int index = log2ind(logicnum);
  _autoload[index] = on;
}
              
TRectype & TMultiple_rectype::insert_row(int row, int logicnum)
{       
  TRectype * r = new_body_record(logicnum);
  TRecord_array & b = body(logicnum);
  const int index = log2ind(logicnum);
  
  set_body_key(*r);
  r->put(_numfields.row(index), row);     
  b.insert_row(r);
  return *r;
}

TRectype & TMultiple_rectype::new_row(int logicnum)
{
  TRecord_array & b = body(logicnum);
  TRectype & r = b.row(-1, TRUE);
  
  return r;
}

bool TMultiple_rectype::autoload_enabled(int logicnum)
{
  return _autoload[log2ind(logicnum)];
}

// @cmember Aggiunge il corpo lgicnum
void TMultiple_rectype::add_file(int logicnum, const char * numfield)
{
  CHECK(_nfiles < maxfiles, "Too many files added");
  _logicnums[_nfiles] = logicnum;
  _autoload[_nfiles] = FALSE;
  _numfields.add(numfield, _nfiles++);
}

TMultiple_rectype::TMultiple_rectype(const TBaseisamfile* file)
                  :TAuto_variable_rectype(file), _nuovo(TRUE), _nfiles(0)
{
}

  // @ cmember costruttore dal record
TMultiple_rectype::TMultiple_rectype(const TRectype & rec)
                  :TAuto_variable_rectype(rec), _nuovo(TRUE), _nfiles(0)
{
}

// @mfunc costruttore di copia
TMultiple_rectype::TMultiple_rectype(const TMultiple_rectype& r)
                  :TAuto_variable_rectype(r)
{
  // copia..
  copy(r);
}