#ifndef __RECARRAY_H
#define __RECARRAY_H

#ifndef __ASSOC_H
#include <assoc.h>
#endif

#ifndef __ISAM_H
#include <isam.h>
#endif

///////////////////////////////////////////////////////////
// TRecord_Array
///////////////////////////////////////////////////////////

// @doc EXTERNAL

// @class TRecord_array | Classe per la gestione di un array di record
//
// @base public | TArray
class TRecord_array : public TObject

// @author:(INTERNAL) Guido

// @access:(INTERNAL) Private Member
{ 
  // @cmember:(INTERNAL) Numero logico del file principale
  int _file;
  // @cmember:(INTERNAL) Array di records
  TArray _data;
  // @cmember:(INTERNAL) Offset iniziale del record array
  int _offset;
  // @cmember:(INTERNAL) Nome del campo col numero di riga
  TString16 _num;

// @access Protected Member
protected:
  // @cmember Ritorna il numero riga del record <p r>
  int rec2row(const TRectype& r) const;
  // @cmember Ritorna il nome del campo con numero di riga
  const TString& num_field() const 
  { return _num; }
  // @cmember Elimina il record di posizione <p i> (ritorna il risultato dell'operazione)
  int remove_from(int i) const;
  // @cmember Confronta i campi della chiave 1 scartando l'ultimo
  bool good(const TRectype& rec) const;
    // @cmember Duplica un record array
  virtual TObject* dup() const { return new TRecord_array(*this);}

  int remove_from(TIsamfile& f, int pos) const;

// @access Public Member
public:     
  // @cmember Ritorna il record che fa da chiave per tutti gli altri
  const TRectype& key() const;
  // @cmember Ritorna il numero di righe presenti
  int rows() const 
  { return _data.items()-1; }
  
  // @cmember Ritorna la riga successiva non vuota a partire da <p r>
  int succ_row(int r) const 
  { return _data.succ(r - _offset) + _offset; }
  // @cmember Ritorna la riga precedente non vuota a partire da <p r>
  int pred_row(int r) const 
  { return _data.pred(r - _offset) + _offset; };
  // @cmember Ritorna l'ultma riga non vuota
  int last_row() const 
  { return _data.last() + _offset; }
  // @cmember Ritorna la prima riga non vuota
  int first_row() const 
  { return succ_row(0); }
  
  // @cmember Ritorna il record <p r>-esimo
  const TRectype& row(int r) const
  { CHECKD(r > _offset, "Bad record number ", r); return (const TRectype&)operator[](r - _offset); }
  
  // @cmember Controlla se esiste la riga <p r> (TRUE se esiste)
  bool exist(int r) const;
  // @cmember Ritorna la riga <p r>-esima; se tale riga non esiste e se <p create> assume il valore TRUE la riga viene creata 
  TRectype& row(int r, bool create);
  // @cmember Ritorna la riga <p r>-esima se tale riga esiste
  const TRectype& operator [](int r) const { return (const TRectype &) ((TRecord_array *) this)->row(r, FALSE);}
  // @cmember Ritorna la riga <p r>-esima se tale riga esiste
  TRectype& operator [](int r) { return row(r, FALSE);}
 
  // @cmember Inserisce una riga alla posizione indicata nel record, sposta gli altri elementi se la posizione era gia' occupata
  virtual int insert_row(TRectype* r);
  // @cmember Inserisce una riga alla posizione indicata nel record, sposta gli altri elementi se la posizione era gia' occupata
  int insert_row(const TRectype& r) 
  { return insert_row((TRectype*)r.dup()); }

  // @cmember Aggiunge/sostituisce una riga alla posizione indicata nel record 
  virtual int add_row(TRectype* r);
  // @cmember Aggiunge/sostituisce una riga alla posizione indicata nel record
  int add_row(const TRectype& r) 
  { return add_row((TRectype*)r.dup()); }
  // @cmember Compatta le righe piene
  virtual void pack() ;
  // @cmember Cancella una riga identificata da <p n>
  virtual bool destroy_row(int n, bool pack = FALSE);
  // @cmember Cancella una riga identificata da <p r>
  virtual bool destroy_row(const TRectype& r, bool pack = FALSE) 
  { return destroy_row(rec2row(r), pack); }
  // @cmember Cancella tutte le righe
  void destroy_rows();
  // @cmember Copia un record array
  TRecord_array& copy(const TRecord_array& a);
  // @cmember Operatore di assegnamento di un record array
  TRecord_array& operator= (const TRecord_array& a) { return copy(a); }

  // @cmember Ritorna il numero logico del file principale
  int logic_num() const
  { return _file; }
  // @cmember Cambia l'intera chiave (solo se vuoto)
  void set_key(TRectype* r);
  // @cmember Rinumera il campo chiave in seguito a reinsert
  bool renum_key(const char* field, const TString& num);
  // @cmember Rinumera il campo chiave in seguito a reinsert
  bool renum_key(const char* field, long num);
  // @cmember Ordina il Record Array secondo il criterio definito in <t COMPARE_FUNCTION>
  void sort(COMPARE_FUNCTION sort_func);

  // @cmember Legge tutto l'array dal file
  virtual int read(const TRectype& r);
  // @cmember Legge tutto l'array dal file
  virtual int read(TRectype* r);
  // @cmember Aggiorna il file (se <p re> == TRUE allora viene aggiornato il record se esisteva)
  virtual int write(bool re = FALSE) const;
  // @cmember Aggiorna il record sul file
  virtual int rewrite() const 
  { return write(TRUE); }
  // @cmember Cancella tutti i record dal file
  virtual int remove() const;
  
  // @cmember Costruttore
  TRecord_array(const TRectype& r, const char* numfield, int first = 1);
  // @cmember Costruttore
  TRecord_array(int logicnum, const char* numfield, int first = 1);
  // @cmember Costruttore
  TRecord_array(const TRecord_array& a);
  // @cmember Costruttore moderno (c) by Guy
  TRecord_array(const char* keytok, int logicnum, int first = 1);
  // @cmember Distruttore
  virtual ~TRecord_array();
};

///////////////////////////////////////////////////////////
// TFile_cache
///////////////////////////////////////////////////////////

class TFile_cache : public TObject
{ 
  static unsigned long _hits, _misses;

  TToken_string _code;
  TString8 _filecode; // Codice tabella es: %STA, &AUT, LVCAU
  TLocalisamfile* _file;
  int _key;
  long _last_firm, _limit;
  int _error;
  bool _test_changes, _changed;

protected:
  TAssoc_array _cache;
  void construct(int key);  // Common costruction

protected:
  void init_file(TLocalisamfile* f=NULL);
  void test_firm();
  void kill_file();

  const TObject& query(const char* chiave);
  virtual TObject* rec2obj(const TRectype& rec) const pure;
  TLocalisamfile& file();
  
public:            
  virtual bool discard(const char* victim);

  int io_result();
  const int key_number() const
  { return _key; }
  
  bool already_loaded(const char* code) const;
  bool already_loaded(long code) const;

  long items() const
  { return _cache.items(); }

  long fill();
  void destroy();
  virtual void flush();

  void set_items_limit(long l) { _limit = l; }
  void test_file_changes(bool t = TRUE) { _test_changes = t; }
  void notify_change();

  static void stats(unsigned long& hits, unsigned long& misses);
  
  TFile_cache(TLocalisamfile *f,int key = 1);
  TFile_cache(int num, int key = 1);
  TFile_cache(const char* table, int key = 1);
  virtual ~TFile_cache();
};

class TDecoder : public TFile_cache
{                                                
  TString16 _outf;

protected:
  virtual TObject* rec2obj(const TRectype& rec) const;
  
public:
  const TString& decode(const char* code);
  const TString& decode(long code);
  
  TDecoder(int num, const char* field, int key = 1);
  TDecoder(const char* table, const char* field = "S0", int key = 1);
  virtual ~TDecoder() { }
};

class TRecord_cache : public TFile_cache
{                                                
protected:
  virtual TObject* rec2obj(const TRectype& rec) const;
    // @cmember Elimina la chiave dalla cache
  
public:
  // @cmember ritorna il record con una determinata chiave 
  virtual const TRectype& get(const char* chiave);

  // @cmember ritorna il campo  (chiama  get(chiave))
  const TString& get(const char* chiave, const char* campo);
  // @cmember ritorna il record con una determinata chiave  numerica
  const TRectype& get(long chiave);
  const TString&  get(long chiave, const char* campo);

  TRecord_cache(int num, int key = 1);
  TRecord_cache(TLocalisamfile *f, int key = 1);
  TRecord_cache(const char* table, int key = 1);
  virtual ~TRecord_cache() { }
};


class TDB_cache : public TArray
{
protected:
  int build_table_key(const char* table, const char* key, TToken_string& k) const;

public: 
  TRecord_cache& rec_cache(int file);

  bool discard(int file, const char* key);
  bool discard(const char *table, const char* key);
  bool discard(const TRectype& rec);
  void flush(int file) { rec_cache(file).flush(); }
	void discard(int file);

  const TRectype& get(int file, const char* key_tok) { return rec_cache(file).get(key_tok); }
  const TRectype& get(int file, long key) { return rec_cache(file).get(key); }
  const TRectype& get(const char* table, const char* key_tok);
  const TRectype& get(const TRectype& key);
  
  // Smarter get: no token string key needed
  const TRectype& get_rec(int file, const char* key1, const char* key2=NULL, const char* key3=NULL, const char* key4=NULL);

  const TString& get(int file, const char* key_tok, const char * campo);
  const TString& get(int file, long key, const char * campo);
  const TString& get(const char* table, const char* key_tok, const char* campo);

  void test_file_changes(int file, bool t = TRUE) 
  { rec_cache(file).test_file_changes(t); }
};

// Best performance by exclusive locking ... when possible :-)
class TFast_isamfile : public TIsamfile
{
public: 
  TFast_isamfile(int logicnum);
  ~TFast_isamfile();
};

TDB_cache& cache();
TRecord_cache& rec_cache(int file);
const TString_array& user_and_groups();

#endif