#include <recarray.h>
#include <diction.h>

#include "svlib09.h"

#define RWCACHE_SIZE 100

// funzione di default: prende una chiave a caso chiave
const TString & TRWrecord_cache::getkey2discard()
{
  THash_object * o=get_some_obj();
  CHECK(o,"E' stata chiamata la funzione getkey2discard con la cache vuota");
  return o->key();
}

void TRWrecord_cache::discard(const TString & vittima)
{
  if (items())
  {
    if (_flags.is_key(vittima))
    {
      const char fl=((TString &)_flags[vittima])[0];
      // record modificato o nuovo
      int err;                                                
      TRectype & rec=(TRectype & )TRecord_cache::get(vittima);
      file().curr()=rec;       
      if (fl == 'D')
      {
        err=file().rewrite();
        if (err!=NOERR)
          err=file().write();
        if (err!=NOERR)
          error_box(TR("Errore nella riscrittura della cache"));
      } else {
        err=file().write();
        if (err!=NOERR)
          if (err == _isreinsert)
            file().rewrite();
          else
            error_box(TR("Errore nella scrittura della cache"));
      }
      _flags.remove(vittima);

    }
    _cache.remove(vittima);
  }
}

const TRectype& TRWrecord_cache::get(const char* chiave)
{
  if (items()>=RWCACHE_SIZE)
    discard(getkey2discard());
  const TRectype & rec=TRecord_cache::get(chiave);
  if (io_result() != NOERR)
  { 
    // record non trovato: � nuovo 
    _flags.add(chiave,TString4("N"));
  }
  return rec;
}

void TRWrecord_cache::put(const TRectype &r)
{
  test_firm();
  
  TToken_string cachekey;
  if (!r.empty())
  {
    const RecDes& recd = r.rec_des();    // Descrizione del record della testata
    const KeyDes& kd = recd.Ky[key_number()-1];    // Elenco dei campi della chiave
    for (int i = file().tab() ? 1: 0; i < kd.NkFields; i++)   // Riempie la chiave selezionata
    {                        
      const int nf = kd.FieldSeq[i] % MaxFields;
      const RecFieldDes& rf = recd.Fd[nf];  
      const TString & fval = r.get(rf.Name);
      cachekey.add(fval);
    }
  }           
  else
    warning_box(TR("adding an empty record"));
  TObject* obj = _cache.objptr(cachekey);

  if (obj != NULL)
  {
    // esiste in cache ; tenta di settare il flag a "D"irty; se il flag esiste gi� � a
    TRectype & rec=(TRectype &)(*obj);
    rec=r;
    _flags.add(cachekey , TString4("D"));
  } else {
    // non esiste in cache 
    obj = rec2obj(r);
    _cache.add(cachekey, obj);
    // qui assume che non esista nemmeno su file, perci� sia "N"uovo; al flush corregger� l'errore
    _flags.add(cachekey , new TString("N"));
  }
  if (items()>=RWCACHE_SIZE)
    discard(getkey2discard());
}


void TRWrecord_cache::clear()
{
  while (items()>0)
  {
    const TString & vittima=getkey2discard();
    if (_flags.is_key(vittima))
      _flags.remove(vittima);
    _cache.remove(vittima);
  }    
}


void TRWrecord_cache::flush()
{
  while (items()>0)
    discard(TRWrecord_cache::getkey2discard());
}

THash_object * TRWrecord_cache::get_some_obj()
{
  if (items()==0)
    return NULL;
  THash_object * o;
  while ((o=_cache.get_hashobj()) == NULL) ;
  return o;
}

TRWrecord_cache::TRWrecord_cache(TLocalisamfile *f, int key, bool lock)
  :TRecord_cache(f,key)
{
  if (lock)
    file().lock();
}


TRWrecord_cache::TRWrecord_cache(int num, int key, bool lock)
  :TRecord_cache(num,key)
{
  if (lock)
    file().lock();
}


TRWrecord_cache::~TRWrecord_cache() 
{
  flush();
}