#include <codeb.h>
#include <diction.h>
#include <extcdecl.h>
#include <currency.h>
#include <prefix.h>
#include <progind.h>
#include <scanner.h>
#include <tabutil.h>
#include <utility.h>

#include <nditte.h>

// Definita in isam.cpp
extern int get_error(int);

///////////////////////////////////////////////////////////
// extern variables are NO-NO!
///////////////////////////////////////////////////////////

HIDDEN TPrefix* _prefhndl = NULL;

// @doc EXTERNAL

// @func Inizializza (crea) un nuovo file "prefix.txt"
//
// @rdesc Ritorna l'oggetto <c TPrefix> creato
TPrefix& prefix_init()
{
  CHECK(_prefhndl == NULL, "Can't create two prefix objects");
  _prefhndl = new TPrefix;
  return *_prefhndl;
}

// @doc EXTERNAL

// @func Determina se il prefix e' stato inizializzato
//
// @rdesc Ritorna TRUE o FALSE
bool prefix_valid()
{
  return _prefhndl != NULL;
}

// @doc EXTERNAL

// @func Legge il file "prefix.txt"
//
// @rdesc Ritorna l'oggetto <c TPrefix> letto
TPrefix& prefix()
{
  CHECK(_prefhndl, "Can't access null prefix");
  return *_prefhndl;
}

// @doc EXTERNAL

// @func Distrugge l'oggett <c TPrefix> in memoria
void prefix_destroy()
{
  if (_prefhndl != NULL)
  {
    delete _prefhndl;
    _prefhndl = NULL;
  }  
}

///////////////////////////////////////////////////////////
// TFile_manager
///////////////////////////////////////////////////////////

class TRecord_info : public TObject
{
  RecDes _recdes;
  TDirtype _dirtype;
  int _len;

protected:
  void translate_key(TToken_string& t) const;
  void compute_len();
  
  bool create(TTrec& rec, TToken_string& keys);

public: // TObject
  virtual bool ok() const;

public:
  operator const RecDes&() const { return _recdes; }
  int len() const { return _len; }

  TDirtype dir_type() const { return _dirtype; }
  bool mutable_dir() const { return _dirtype <= _studir; }
  bool fixed_dir() const { return _dirtype > _studir; }

  TRecord_info(int logicnum);
  TRecord_info(const char* name);
  TRecord_info(TTrec& rec, TToken_string& keys);
  virtual ~TRecord_info() { }
};

void TRecord_info::translate_key(TToken_string& t) const// Traduce l'espressione chiave di CodeBase
{
  // Trasforma l'espressione
  TToken_string k(t.get(0),'+');
  TToken_string range("",',');
  TString ws;
  const bool is_dup = t.get(1)[0] == 'X';
  const int items = k.items();
  t = "";
  for (int i = 0; i<items; i++) // scorre i campi dell'espressione
  {
    ws = k.get(i); // Primo campo
    const bool is_upper = ws.find("UPPER")  >= 0;
    const bool is_sub   = ws.find("SUBSTR") >= 0;
    int   paren1        = ws.find('('); // Trova la prima parentesi aperta
    int   paren2,last,from = 0,to = 0;
    
    if (paren1 >= 0 && is_sub && is_upper)
      paren1 = ws.find('('); // Trova la seconda parentesi (in questo caso c'e' per forza)

    if (paren1 >= 0) // Trova la prima virgola o parentesi chiusa (per qualsiasi espressione)
    {
      paren2 = ws.find(',');
      if (paren2 == -1) // se non ci sono virgole trova la parentesi chiusa
        paren2 = ws.find(')');
      CHECK(paren2 > paren1,"Something wrong happened translating CodeBase expressions.");
      if (is_sub) // Se e' una sottostringa estrae i campi DA e A
      {
        range = ws;
        last = ws.find(')');
        range.sub(paren2,last); // dalla virgola alla parentesi
        from = range.get_int(0);
        to   = range.get_int(1);
      }
      ws = ws.sub(paren1+1,paren2); // Nome del campo pulito pulito
      ws.upper();
    }
    
    if (is_upper)
      t << "UPPER(";
    
    t << ws; // aggiunge il nome del campo

    if (is_sub)
    {
      t << "[";
      t << from << ",";
      t << to << "]";
    }
    if (is_upper)
      t << ")";
    t << '+';
  }
  t.rtrim(1); // Toglie il + in piu'
  t.add(is_dup ? "X" : " "); 
}

bool TRecord_info::ok() const 
{ 
  return _recdes.NFields > 0 &&
         _recdes.NKeys > 0 && _recdes.NKeys < MaxKeys;
}

void TRecord_info::compute_len()
{
  _len = 0;
  for (int f = _recdes.NFields-1; f >= 0; f--)
    _len += _recdes.Fd[f].Len;
  if (_len > 0)
    _len++;
}

TRecord_info::TRecord_info(int logicnum)
{
  memset(&_recdes, 0, sizeof(_recdes));

  FileDes fd; memset(&fd, 0, sizeof(fd));
  CGetFile(logicnum, &fd, _nolock, _nordir);
  if (fd.SysName[0])
  {
    _dirtype = fd.SysName[0] != '$' ? _comdir : _nordir;
    CGetRec(logicnum, &_recdes, _dirtype);
  }
  if (ok())
    compute_len();
}

bool TRecord_info::create(TTrec& rec, TToken_string& keys)
{
  rec.rehash(); 
  const int num_keys = rec.keys();

  TToken_string trans;
  for (int i = 0; i < num_keys; i++)
  {
    trans = keys.get(i);
    translate_key(trans); // Traduce l'espressione chiave di CodeBase
    rec.update_keydef(i, trans); 
  }
  memcpy(&_recdes, rec.rec(), sizeof(RecDes));
  compute_len();
  return _len > 0;
}

TRecord_info::TRecord_info(TTrec& rec, TToken_string& keys) 
            : _dirtype(_extdir) 
{ 
  create(rec, keys); 
}

TRecord_info::TRecord_info(const char* name)
            : _dirtype(_extdir)
{
  TFilename fname(name); fname.ext("dbf");
  if (fname.exist())
  {
    TToken_string keys(256*MaxKeys, '$');

    fname.ext("");
    FileDes fd;
    TTrec rec;
    int err = DB_recinfo(fname, &fd, rec.rec(), keys.get_buffer());
    if (err != NOERR)
      create(rec, keys); 
    else
      memset(&_recdes, 0, sizeof(_recdes));
  }
  else
    memset(&_recdes, 0, sizeof(_recdes));
}


class TFile_info : public TObject
{
  TFilename _name;
  int _ref_count;

  int _num;
  int _handle;
  clock_t _last_access, _last_change;
  bool _locked, _exclusive;
  TDirtype _dir;
  FileDes _filedes;

  int _last_key;

protected:
  int open_low(bool exclusive, bool index);
  int close_low();

public:  // TObject
  virtual bool ok() const { return _name.not_empty(); }

public:
  const TFilename& pathname() const { return _name; }
  int num() const { return _num; }
  int handle() const { return _handle; }
  bool is_open() const { return _handle >= 0; }
  clock_t last_access() const { return _last_access; }
  clock_t last_change() const { return _last_change; }
  bool is_exclusive() const { return _exclusive; }
  bool is_locked() const { return _locked; }
  int ref_count() const { return _ref_count; }
  int last_key() const { return _last_key; }
  void touch() { _last_access = clock(); }
  void set_dirty() { _last_change = clock(); }

  TDirtype dir_type() const { return _dir; }
  bool mutable_dir() const { return _dir <= _studir; }
  bool fixed_dir() const { return _dir > _studir; }

  int open(bool exclusive, bool index);
  int close();
  const TFilename& load_filedes();

  void lock_record(TRecnotype rec);
  void unlock_record(TRecnotype rec);

  int auto_open(int key);
  void auto_close();

  operator const FileDes&() const { return _filedes; }
  TFile_info(int logicnum, TFilename& name);
  virtual ~TFile_info();
};


int TFile_info::open_low(bool exclusive, bool index)
{
  if (_handle < 0)
    _handle = DB_open(_name, exclusive, index);
#ifdef DBG
  else
    error_box("You shouldn't reopen file %s", (const char*)_name);
#endif
  int err = NOERR;
  if (_handle >= 0)
  {
    if (num() < LF_EXTERNAL && (_dir == _nordir || _dir == _comdir))
    {
      TRecnotype n = DB_reccount(_handle);
      TDir d;
      d.get(num(),_nolock,_dir,_sysdirop);
      _filedes.EOD = d.eod() = n;
      _filedes.EOX = d.eox() = n; 
      d.put(num(),_dir,_sysdirop); 
    }

    if (index)
      err = DB_tagselect(_handle, _last_key = 1);
    else
      _last_key = 0;
  }
  else
  {
    err = get_error(_handle);
  }
  return err;
}

int TFile_info::close_low()
{
  int err = NOERR;
  if (_handle >= 0)
  {
    DB_close(_handle);
    _handle = -1;
    _last_key = -1;
    _exclusive = _locked = FALSE; 
  }
  else
  {
#ifdef DBG
    error_box("You shouldn't reclose file %s", (const char*)_name);
#endif
    err = _isnotopen;
  }
  return err;
}

int TFile_info::open(bool exclusive, bool index)
{
  int err = NOERR;
  if (exclusive || !index)
  {
    if (_ref_count > 0 || is_open())  
    {
#ifdef DBG            
      if (_ref_count > 0)
        error_box("You shouldn't reopen file %d exclusively", num());
#endif
      close_low();
    }
    _exclusive = _locked = TRUE;
    err = open_low(exclusive, index);
  }
  touch();  // Memorizza ora apertura del file
  _ref_count++;
  return err;
}

int TFile_info::close()
{
  int err = NOERR;
  if (_ref_count > 0)
  {
    _ref_count--;
    if (_ref_count == 0)
    {
      if (is_open()) 
      {
        // Chiudi fisicamente solo se necessario
        if (_locked || _exclusive || dir_type() == _extdir)
          err = close_low();
      }  
      _locked = _exclusive = FALSE;
    }
  }
  else
    err = _isnotopen;
  return err;
}

void TFile_info::auto_close()
{
  const bool yes = is_open() && !(is_locked() || is_exclusive());
  if (yes)
    close_low();
  else
    NFCHECK("Can't auto_close file '%s'", (const char*)_name);
}

int TFile_info::auto_open(int key)
{
  if (_handle < 0)
    open_low(FALSE, TRUE);

  if (_handle >= 0)
  {
    if (key > 0 && key != _last_key)
    {
      const int err = DB_tagselect(_handle, _last_key = key);
      if (err != NOERR)
      {
#ifdef DBG
        NFCHECK("Can't set key %d on file %d", key, num());
#endif
        return err;
      }
    }
  }
  return _handle;
}

void TFile_info::lock_record(TRecnotype) 
{ _locked = TRUE; }

void TFile_info::unlock_record(TRecnotype) 
{ _locked = FALSE; }

const TFilename& TFile_info::load_filedes()
{
  memset(&_filedes, 0, sizeof(_filedes));
  CGetFile(num(), &_filedes, _nolock, _nordir);
  if (_filedes.SysName[0])
  {
    if (_filedes.SysName[0] != '$')
      _dir = _comdir;
    else
      _dir = _nordir;
    COpenFile(num(), &_filedes, _nolock, _dir);
    _name = _filedes.SysName;
  }
  else
    _name.cut(0);
  return _name;
}

TFile_info::TFile_info(int logicnum, TFilename& name)
: _ref_count(0), _num(logicnum), _handle(-1),
  _last_access(0), 
  _last_change(0), _locked(FALSE), _exclusive(FALSE), _last_key(-1)
{
  if (logicnum < LF_EXTERNAL)
  {
    load_filedes();
    if (name.not_empty())
    {
      // File dati temporaneo
      _dir = _extdir;
      _name = name;
      _name.ext("");
    }
    else
      name = _name;
  }
  else
  {
    // File dati esterno
    _dir = _extdir;
    _name = name;
    _name.ext("dbf");
    if (_name.exist())
    {
      TToken_string keys(256*MaxKeys, '$');

      _name.ext("");
      TTrec rec;
      int err = DB_recinfo(_name, &_filedes, rec.rec(), keys.get_buffer());
      if (err == NOERR && prefix().add_recdes(logicnum, rec, keys))
      {
        strncpy(_filedes.SysName, _name, 40);
        _filedes.SysName[41] = '\0';
      }
      else
        _name.cut(0);
    }
    else
      _name.cut(0);
  }
}

TFile_info::~TFile_info()
{
  if (is_open())
    close_low();
}

TFile_info& TFile_manager::fileinfo(TIsam_handle num) const
{
  TFile_info* i = (TFile_info*)_fileinfo.objptr(num);
  if (i == NULL && num > 0 && num < LF_EXTERNAL)
  {                    
    TFilename name;
    i = new TFile_info(num, name);
    if (i->ok())
      ((TFile_manager*)this)->_fileinfo.add(i, num);
    else  
    {          
      delete i;
      i = NULL;
    }
  }

  CHECKD(i, "Unknown file ", num);
  return *i;
}

TRecord_info& TFile_manager::recinfo(int logicnum) const
{
  TRecord_info* i = (TRecord_info*)_recinfo.objptr(logicnum);
  if (i == NULL)
  {
    i = new TRecord_info(logicnum);
    ((TFile_manager*)this)->_recinfo.add(i, logicnum);
  }
  return *i;
}

bool TFile_manager::add_recdes(int logicnum, TTrec& rec, TToken_string& keys)
{
  TRecord_info* i = (TRecord_info*)_recinfo.objptr(logicnum);
  if (i == NULL)
  {
    if (logicnum < LF_EXTERNAL)
      i = new TRecord_info(logicnum);
    else
      i = new TRecord_info(rec, keys);
    if (i->ok())
      _recinfo.add(i, logicnum);
    else
    {
      delete i;
      i = NULL;
    }
  }
  return i != NULL;
}


bool TFile_manager::close_oldest()
{
  int oldest = 0;
  clock_t age = 0;
  _open_files = 0;  // Intanto ricalcolo il numero di file veramente aperti
  for (int n = _fileinfo.last(); n > 0; n = _fileinfo.pred(n))
  {
    TFile_info& i = (TFile_info&)_fileinfo[n];
    if (i.is_open())
    {
      _open_files++;
      if (!i.is_locked() && !i.is_exclusive())
      {
        if (oldest == 0 || i.last_access() < age)
        {
          oldest = n;
          age = i.last_access();
        }
      }
    }
  }
  if (oldest)
  {
    TFile_info& i = (TFile_info&)_fileinfo[oldest];
    i.auto_close();
    _open_files--;
  }
  
  return oldest != 0; //verificare
}

// name    : nome del file
TIsam_handle TFile_manager::get_handle(TFilename& name)
{
	
  TFile_info* i = NULL;
	TFilename fname(name); fname.ext("");
	int num;
	
  for (num = LF_EXTERNAL; (i = (TFile_info*)_fileinfo.objptr(num)) != NULL; num++)
  {
    if (i->pathname() == fname)
      break;
  }
	if (i == NULL)
	{
    i = new TFile_info(num, name);
    if (!i->ok())
    {
      delete i;
      return -60;
    }
    _fileinfo.add(i, num);
	}
			
  return num;
}

// name    : nome del file (vuoto per i file normali viene riempito automaticamente)
// logicnum: numero logico del file (LF_EXTERNAL per gli esterni viene generato automaticamente)
TIsam_handle TFile_manager::open(int& logicnum, TFilename& name, bool exclusive, bool index)
{
  TIsam_handle num = logicnum;
  if (name.not_empty())
  {
		num = get_handle(name);
    if (logicnum >= LF_EXTERNAL)
      logicnum = num;
  }
  
  TFile_info* i = (TFile_info*)_fileinfo.objptr(num);
  if (i == NULL)
  {
    i = new TFile_info(logicnum, name);
    if (!i->ok())
    {
      delete i;
      return -60;
    }
    _fileinfo.add(i, num);
  }
  i->open(exclusive, index);
  name = i->pathname();
  return num;
}

int TFile_manager::close(TIsam_handle& name)
{
  int err = _isnotopen;

  TFile_info* i = (TFile_info*)_fileinfo.objptr(name);
  if (i != NULL)
  {
    const bool was_open = i->is_open();
    err = i->close();
    // Se chiuso veramente ... (Per efficienza non chiude sempre)
    if (err == NOERR && !i->is_open()) 
    {
      if (was_open)
        _open_files--;
      if (name >= LF_EXTERNAL && i->ref_count() <= 0)
      {
        _fileinfo.remove(name);
        _recinfo.remove(name);
      }
    }
  }
  name = 0;
  return err;
}

TCodeb_handle TFile_manager::get_handle(TIsam_handle name, int key)
{
  TFile_info& i = fileinfo(name);
  if (i.ref_count() == 0)
    NFCHECK("Can't use closed file %d", i.num());

  TCodeb_handle handle = i.handle();
  if (handle < 0)
  {
    if (_open_files >= _max_open_files)
      close_oldest();

    // Se ho passato key = -1 mi va bene la chiave principale
    if (key < 0) 
      key = 1;
    
    handle = i.auto_open(key);
    if (handle >= 0)
      _open_files++;
  }
  else
  {
    // Se ho passato key = -1 mi va bene la chiave corrente
    if (key >= 0 && i.last_key() != key)
      handle = i.auto_open(key);
  }
//  i.touch();  // Memorizza troppo spesso l'accesso
  return handle;
}

void TFile_manager::lock_record(TIsam_handle num, TRecnotype rec)
{
  TFile_info& i = fileinfo(num);
  i.lock_record(rec);
}

void TFile_manager::unlock_record(TIsam_handle num, TRecnotype rec)
{
  TFile_info& i = fileinfo(num);
  i.unlock_record(rec);
}

const RecDes& TFile_manager::get_recdes(int logicnum) const
{
  const TRecord_info& i = recinfo(logicnum);
  return i;
}

const RecDes& TFile_manager::update_recdes(int logicnum)
{
  _recinfo.destroy(logicnum);
  return get_recdes(logicnum);
}

int TFile_manager::get_reclen(int logicnum)
{
  const TRecord_info& i = recinfo(logicnum);
  return i.ok() ? i.len() : 0;
}

TDirtype TFile_manager::get_dirtype(int logicnum)
{
  const TRecord_info& i = recinfo(logicnum);
  return i.dir_type();
}

void TFile_manager::notify_change(TIsam_handle name)
{
  TFile_info& i = fileinfo(name);
  i.set_dirty();
}

long TFile_manager::last_change(TIsam_handle name) const
{
  TFile_info& i = fileinfo(name);
  return i.last_change();
}

int TFile_manager::close_closeable()
{
  _open_files = 0;
  for (TIsam_handle n = _fileinfo.last(); n > 0; n = _fileinfo.pred(n))
  {
    TFile_info& i = fileinfo(n);
    if (i.is_open())
    {
      if (!i.is_exclusive())
        i.auto_close();
      else
        _open_files++;
    }
    if (i.ref_count() <= 0)
      _fileinfo.destroy(n);
  }
  return _open_files;
}

void TFile_manager::close_all()
{
  const int zoccolo_duro = close_closeable();
#ifdef DBG
  if (zoccolo_duro > 0)
    NFCHECK("%d files refuse can't be closed!", zoccolo_duro);
#endif
  
  for (int n = _recinfo.last(); n > 0; n = _recinfo.pred(n))
  {
    const TRecord_info& r = recinfo(n);
    if (r.mutable_dir())
      _recinfo.destroy(n);
  }
}

void TFile_manager::open_all()
{
  for (TIsam_handle n = _fileinfo.last(); n > 0; n = _fileinfo.pred(n))
  {
    TFile_info& i = fileinfo(n);
    if (!i.is_exclusive())
    {
      if (i.mutable_dir())
        i.load_filedes();
    }
  }
}

const FileDes& TFile_manager::get_filedes(TIsam_handle id) const
{
  TFile_info& i = fileinfo(id);
  return i;
}

const TFilename& TFile_manager::get_filename(TIsam_handle id) const
{
  TFile_info& i = fileinfo(id);
  return i.pathname();
}


TFile_manager::TFile_manager()
             : _open_files(0)
{
  TConfig prawin(CONFIG_INSTALL, "Main");
  _max_open_files = prawin.get_int("MaxHandles", NULL, -1, 16);
  if (_max_open_files < 16)
    _max_open_files = 16;
  else
    if (_max_open_files > 64)
      _max_open_files = 64;
}

///////////////////////////////////////////////////////////
// TFirm
///////////////////////////////////////////////////////////

bool TFirm::read(long cod)
{
  int err = NOERR;
  if (cod < 0L)
    cod = prefix().get_codditta();
  _rec.destroy();
  TLocalisamfile ditte(LF_NDITTE);
  if (cod > 0L)
  {           
    ditte.put("CODDITTA", cod);
    err = ditte.read();
  }
  else
    err = ditte.first();
  if (err == NOERR)
	{
    const TRectype& ditta = ditte.curr();   
    for (int f = ditta.items()-1; f >= 0; f--)
    {
      const char* name = ditta.fieldname(f);
      _rec.add(name, ditta.get(name));
    }
	}
	else
    NFCHECK("Can't read firm %ld: error %d", cod, err);

  return _rec.items() > 0;
}

const TString& TFirm::get(const char* attr) const
{
  const TString* str = (const TString*)_rec.objptr(attr);
  if (str == NULL)
    str = &EMPTY_STRING;
  return *str;
}

long TFirm::get_long(const char* attr) const
{ return atol(get(attr)); }

long TFirm::codice() const
{
  return get_long(NDT_CODDITTA);
}

const TString& TFirm::codice_valuta() const
{          
  const TString& codval = get(NDT_VALUTA);
  if (codval.empty())
    return TCurrency::get_base_val();
  return codval;
}

TFirm::TFirm(long code)
{
  read(code);
}

///////////////////////////////////////////////////////////
// TPrefix
///////////////////////////////////////////////////////////

TPrefix::TPrefix() : _filelevel(0), _stdlevel(0), _items(0), _firm(NULL)
{
  _prefix = ".";
  CGetPref();
  
  if (!fexist(__ptprf) || strchr(__ptprf, ' ') != NULL)
    fatal_box(FR("Percorso dati non valido: '%s'"), __ptprf);

  const TFilename dir(cprefix);
  const long primaditta = atol(dir.name());
  if (primaditta > 0L && !exist(primaditta))
    set_codditta(0L, TRUE);

  DB_init();

  set("");    // Dati standard
  _stdlevel = filelevel();
  set("DEF"); // Ditta corrente
}

TPrefix::~TPrefix()
{
  set();
  if (_firm)
    delete _firm;
  _manager.close_all();
  DB_exit();
}

// @doc EXTERNAL

// @mfunc Riapre tutti gli archivi della ditta attiva
void TPrefix::reopen() const
{
  if (_prefix != ".")
  {
    ((TPrefix*)this)->_manager.close_all();
    ((TPrefix*)this)->_manager.open_all();
  }
}

// @doc EXTERNAL

// @mfunc Setta la ditta corrente                 
void TPrefix::set(
  const char* name, // @parm Nome del direttorio dati da attivare (default NULL)
  bool force,       // @parm Permette di settarla anche se non esiste (default FALSE)
  TFilelock mode)   // @parm Permette di aprire la ditta in modo esclusivo (default _manulock)
  
  // @comm Il parametro <p name> puo' assumere i seguenti valori:
  //
  // @flag NULL | Chiude tutti i files
  // @flag COM | Apre il direttorio con i dati comuni
  // @flag DEF | Riapre la ditta indicata nel file prefix.txt
  // @flag codice ditta | Apre la ditta indicata
{                 
  if (name == NULL)
  {
    close_closeable_isamfiles();
    CCloseDir(NORDIR);
    CCloseDir(COMDIR);
    CCloseRecDir(NORDIR);
    CCloseRecDir(COMDIR);
    _prefix = ".";
    return;
  }

  if (_prefix == name) 
    return;
  if (!force && !test(name)) 
    return;
  if (_prefix != ".")
  {
    _manager.close_all();
    CCloseDir(NORDIR);
    CCloseDir(COMDIR);
    CCloseRecDir(NORDIR);
    CCloseRecDir(COMDIR);
  }

  if (strcmp(name, "DEF") == 0)
  {
    CGetPref();
    xvt_fsys_parse_pathname(cprefix, NULL, NULL, _prefix.get_buffer(), NULL, NULL);
  }
  else
  {      
    _prefix = name;
   if (*name) 
     xvt_fsys_build_pathname(cprefix, NULL, __ptprf, name, NULL, NULL);
   else 
     strcpy(cprefix, "");
  }

  if (!test(_prefix)) 
    fatal_box("Impossibile utilizzare la ditta %s", name);

  COpenDir((int) mode, NORDIR);
  COpenDir((int) mode, COMDIR);
  COpenRecDir((int) mode, NORDIR);
  COpenRecDir((int) mode, COMDIR);
  if (_prefix != ".")
  {    
    FileDes d;
    CGetFile(LF_DIR, &d, _nolock, NORDIR);
    _filelevel = d.Flags;
    _items = (int)d.EOD;
    _manager.open_all();
  }
}

bool TPrefix::exist(long codditta) const
{
  if (codditta > 0L && codditta < 100000L)
  {
    TFilename s(firm2dir(codditta)); 
    s.add("dir.gen");
    if (s.exist())
    {
      s = s.path();
      s.add("trc.gen");
      return s.exist();
    }
  }
  return FALSE;
}

bool TPrefix::test(const char* s) const
{
  if (s && *s && strcmp(s, "DEF"))
  {
    TFilename s1(__ptprf);
    s1.add(s);
    s1.add("dir.gen");
    
    if (!s1.exist())
      return error_box(FR("Impossibile trovare il file '%s'"), (const char*)s1);
  }

  return TRUE;
}

void TPrefix::lock_record(TIsam_handle num, TRecnotype rec)
{
  _manager.lock_record(num, rec);
}

void TPrefix::unlock_record(TIsam_handle num, TRecnotype rec)
{
  _manager.unlock_record(num, rec);
}

bool TPrefix::test(long codditta) const
{
  TString16 s("com");
  if (codditta > 0L)
    s.format("%05lda", codditta);
  return test(s);
}


long TPrefix::get_codditta() const
{
  const long codditta = atol(_prefix);
  return codditta;
}

bool TPrefix::set_codditta(long codditta, bool force)
{
  if (force || test(codditta))
  {
    TString16 s("com");
    if (codditta > 0L) 
      s.format("%05lda", codditta); 
    set(s, force);
    CPutPref(_prefix);
    if (_firm) 
      _firm->read(codditta);
    return TRUE;
  }
  return FALSE;
}

const char* TPrefix::get_studio() const
{
  return __ptprf;
}

bool TPrefix::set_studio(const char* study, long ditta)
{             
  TFilename dirtest(study);
  if (!dirtest.exist() || dirtest.find(' ') >= 0)
    return FALSE;

  dirtest.add("com/dir.gen");
  if (!dirtest.exist())
    return FALSE;

  // Chiudi tutti i files!
  _manager.close_all();

  const TString old_study(__ptprf);
  const TString old_firm(_prefix);
  
  strcpy(__ptprf, study);
  const word len = strlen(__ptprf);
  if (len > 0 && __ptprf[len-1] != '\\' && __ptprf[len-1] != '/')
  {
    __ptprf[len] = SLASH;
    __ptprf[len+1] = '\0';
  }
  if (!test(ditta))
    ditta = 0L;
  
  bool ok = set_codditta(ditta, TRUE);
  if (!ok)
  {
    strcpy(__ptprf, old_study);
    set(old_firm, TRUE);
  }
  return ok;
}

// @doc EXTERNAL

// @mfunc Ritorna la descrizione del file passato
const char* TPrefix::description(
  const char* cod) const // @parm Nome del file di cui si vuole conoscere la descrizione

  // @syntax const char* description(const char* cod);
  // @syntax const char* description(int cod);
  //                    
  // @comm Il parametro <p cod> puo' indicare anche il nome di una tabella. In questo caso ne
  //       ritorna la descrizione.
  //             <nl>Passando il nome di una tabella in <p cod> si ottiene la stessa cosa della
  //             funzione <mf TDir::Tab_des>, ma viene cercato la descrizione nel titolo della maschera.
{
  TString& n = get_tmp_string();
  n = cod;

  if (n[0] == '%') 
    n.ltrim(1);
  
  const int logicnum = atoi(n);
  if (logicnum == 0)
  {
    TTable t(cod);
    n = t.description();
  }  
  else
  {
    if (logicnum > 0 && logicnum < items())
    { 
      TBaseisamfile f(logicnum);
      n = f.description();
    } 
    else n.cut(0);
  }  
  
  return n;
}

const char* TPrefix::description(int cod) const
{
  TString16 n; n << cod;
  return description(n);
}

const TFirm& TPrefix::firm()
{
  if (_firm == NULL)
	  _firm = new TFirm;
  return *_firm;
}


// Certified 90%
// @doc EXTERNAL

// @func Converte il numero di una ditta nella sua directory dati
//
// @rdesc Restituisce il nome di una directory dati
const char* firm2dir(
      long codditta) // @parm Codice ditta da convertire
{                  
  TString8 firm;
  switch (codditta)
  {
  case -2:                                     // Dati generali campione
  case -1:                                     // Dati di studio
    firm = ""; break;
  case  0:                                     // Dati comuni
    firm = "com"; break;
  default:                                     // Dati ditta
    firm.format("%05lda", codditta); break;
  }    

  TString& path = get_tmp_string(256);
  xvt_fsys_build_pathname(path.get_buffer(), NULL, __ptprf, firm, NULL, NULL);
  return path;
}                      

int safely_close_closeable_isamfiles()
{
  int f = 0;
  if (_prefhndl)
    f = _prefhndl->close_closeable_isamfiles();
  return f;
}

bool TPrefix::build_firm_data(long codditta, bool flagcom)
{
  const char* const ndir = "dir.gen";
  const char* const ntrc = "trc.gen";
  TFilename  s(firm2dir(codditta)); s.add(ndir);
  bool exist = s.exist();
  
  if (!exist)
  {
    s = s.path(); s.add(ntrc);
    exist = s.exist();
  }
  if (exist)
    return message_box("Direttorio dati danneggiato, impossibile attivare la ditta %ld", codditta);
  if (!yesno_box("Gli archivi della ditta %ld non esistono: si desidera generarli?", codditta))
    return FALSE;         
  
  TLocalisamfile ditte(LF_NDITTE);
  ditte.zero();
  ditte.put(NDT_CODDITTA,codditta);   
  if (ditte.read(_isequal,_testandlock) == _islocked)
  {
    message_box("Archivi della ditta %ld in fase di creazione da parte di un altro utente.",codditta);
    return FALSE;
  }
  
  set_autoload_new_files(yesno_box("Si desidera precaricare gli archivi standard"));
  s = s.path(); s.rtrim(1);
  
  if (!s.exist() && !make_dir(s))
    return error_box("Impossibile creare il direttorio della ditta %ld (%s)",
                     codditta, (const char*)s);
  
  s.add(ndir);
  if (!fcopy(ndir, s))
    return error_box("Impossibile copiare il file %s della ditta %ld",
                     ndir, codditta);
  s = s.path(); s.add(ntrc);
  if (!fcopy(ntrc, s))
    return error_box("Impossibile copiare il file %s della ditta %ld",
                     ntrc, codditta);

  TDir dir, dir1;
  TTrec rec;

  prefix().set("");
  dir1.get(LF_DIR, _nolock, _nordir, _sysdirop);
  const long maxeod0 = dir1.eod();

  prefix().set_codditta(codditta);
  dir.get(LF_DIR, _nolock, _nordir, _sysdirop);
  if (dir.eod() == 0)
  {
    dir1.eod() = 1L;
    dir1.put(LF_DIR, _nordir, _sysdirop);
    dir.get(LF_DIR, _nolock, _nordir, _sysdirop);
  }
  const long    maxeod1 = dir.eod();

  if (maxeod0 > maxeod1)
  {
    dir.get(LF_DIR, _nolock, _nordir, _sysdirop);
    dir.eod() = maxeod0;
    dir.put(LF_DIR, _nordir, _sysdirop);
    rec.zero();
  }
  TString mess("Generazione archivi della ditta "); mess << codditta;
  TProgind p(maxeod0 ? maxeod0 : 1, mess, FALSE, TRUE);

  for (int i = LF_DIR + 1; i <= maxeod0; i++)
  {
    p.addstatus(1);
    prefix().set("");
    dir.get(i, _nolock, _nordir, _sysdirop);
    rec.get(i);
    bool create_now = dir.is_active();
    
    prefix().set_codditta(codditta);
    dir.put(i, _nordir, _sysdirop);
    rec.put(i);
    dir.flags() = 0L;
    create_now = create_now && (flagcom ? dir.is_com() : dir.is_firm());
    
    if (dir.is_valid() && create_now)
    {
      TSystemisamfile f(i);
      f.build(30);
    }
    else
    {
      dir.put(i, _nordir, _sysdirop);
      rec.put(i);
    }
  }
  
  TConfig c(CONFIG_STUDIO, "cg");
  if (c.get_bool("StiReg"))
  {
    TTable reg("REG");
    for (reg.first(_lock); reg.good(); reg.next(_lock))
    {
      reg.put("B9", "X");
      reg.rewrite();
    }
  } 
  ditte.reread(_unlock);
  
  prefix().set_codditta(codditta);
  set_autoload_new_files(TRUE);
  
  return TRUE;
}