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

#include <nditte.h>

extern int get_error(int);

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

HIDDEN TString16 _user;
HIDDEN TPrefix* _prefhndl = NULL;

// @doc EXTERNAL

// @func Ritorna il nome dell'utente attuale
//
// @rdesc Ritorno il nome dell'utente attuale
TString& user()
{
  return _user;
}
 
// @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 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)
  {
    delete _prefhndl;
    _prefhndl = NULL;
  }  
}

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

HIDDEN const char* const glockname = "xx";

TPrefix::TPrefix() : _filelevel(0), _items(0)
{
  _prefix = ".";
  CGetPref();

  const TFilename dir(cprefix);
  const long primaditta = atol(dir.name());
  if (primaditta > 0 && !exist(primaditta))
    set("com", TRUE);
}


TPrefix::~TPrefix()
{
  set();
}


HIDDEN int closeall(bool changestudy, TBit_array& excl, TBit_array& toclose)
{
  if (!openf) return 0;
  TDir    d;
  d.get(1);
  const int max = (int) d.eod();

  int err = NOERR;

  for (int i = 1; i < max; i++)
    if (openf[i] != NULL)
    {
      isfdptr isfd = openf[i];
      d.get(i + 1, _nolock, _nordir, _sysdirop);
      if (toclose[i + 1] || changestudy || !d.is_com())
      {
        excl.set(i, DB_file_locked(isfd->fhnd) != 0);
        err=DB_close(isfd->fhnd);
        if (err != NOERR) err=get_error(err);
        if (err != NOERR) fatal_box("Can't close file %d. Error n. %d",isfd->ln,err);
        exclunlock(CInsPref((char*) glockname, _nordir), FALSE);
      }
    }
  return max;
}


HIDDEN void openall(bool changestudy, TBit_array& excl, int oldmax, TBit_array& toopen) 
{
  if (!openf) 
    return ;
  TDir    d;
  d.get(1);
  const int max = (int) d.eod();

  for (int i = max; i < oldmax; i++)
    if (openf[i] != NULL)
    {
      d.get(i + 1, _nolock, _nordir, _sysdirop);
      if (changestudy || !d.is_com())
        fatal_box("Can't reopen file n. %d", i + 1);
    }

  int err = NOERR;

  for (i = 1; i < max; i++)
    if (openf[i] != NULL)
    {
      isfdptr isfd = openf[i];
      d.get(i + 1, _nolock, _nordir, _sysdirop);
      const bool com = d.is_com();
      if (toopen[i + 1] || changestudy || !com)
      {
        isfd->ft = com ? _comdir : _nordir;
        d.get(i + 1, _nolock, (TDirtype) isfd->ft);
        *isfd->d = *d.filedesc();

        TTrec r;
        r.get(i + 1, (TDirtype) isfd->ft);
        *isfd->r = *r.rec();
        if (excllock(CInsPref((char*) glockname, NORDIR), FALSE) == -1)
          fatal_box("Can't reopen file n. %d : file in use", i + 1);
        isfd->fhnd = DB_open(isfd->d->SysName, excl[i],TRUE);
        if (isfd->fhnd < 0 ) err=get_error(isfd->fhnd);
        else err = NOERR;
        if (err == _islocked)   
          fatal_box("Can't reopen file n. %d : file in use", i + 1);
        if (err != NOERR) 
          fatal_box("Can't reopen file n. %d : error n. %d", i + 1, err);
      }
    }
}         

// @doc EXTERNAL

// @mfunc Riapre tutti gli archivi della ditta attiva
void TPrefix::reopen() const

{
  if (_prefix != ".")
  {
    TBit_array excl, comfiles;
    comfiles.set(LF_PCON);
    comfiles.set(LF_CLIFO);
    comfiles.set(LF_INDSP);
    comfiles.set(LF_CAUSALI);
    comfiles.set(LF_RCAUSALI);
    int max = closeall(FALSE, excl, comfiles);

    openall(FALSE, excl, max, comfiles);
  }
}

// @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 esclusico (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)
  {
    CCloseDir(NORDIR);
    CCloseDir(COMDIR);
    CCloseRecDir(NORDIR);
    CCloseRecDir(COMDIR);
    _prefix = ".";
    return;
  }

  if (_prefix == name) return;

  if (!force && !test(name)) return;

  TBit_array excl, comfiles;
  int     max = 0;
  comfiles.set(LF_PCON);
  comfiles.set(LF_CLIFO);
  comfiles.set(LF_INDSP);
  comfiles.set(LF_CAUSALI);
  comfiles.set(LF_RCAUSALI);

  if (_prefix != ".")
  {
    max = closeall(FALSE, excl, comfiles);
    CCloseDir(NORDIR);
    CCloseDir(COMDIR);
    CCloseRecDir(NORDIR);
    CCloseRecDir(COMDIR);
  }

  if (strcmp(name, "DEF") == 0)
  {
    CGetPref();
    _splitpath(cprefix, NULL, NULL, _prefix.get_buffer(), NULL);
  }
  else
  {      
    _prefix = name;
   if (*name) 
     _makepath(cprefix, NULL, __ptprf, name, 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;
    openall(FALSE, excl, max, comfiles);
  }
}

bool TPrefix::exist(long codditta) const
{
  CHECKD(codditta > 0, "Solo le ditte con codice maggiore di zero possono esistere : codice ", codditta);
  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("Impossibile trovare il file '%s'", (const char*)s1);
  }

  return TRUE;
}

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);
    return TRUE;
  }
  return FALSE;
}

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

bool TPrefix::set_studio(const char* study, long ditta)
{             
  TFilename dirtest(study);
  dirtest.add("com/dir.gen");
  if (!dirtest.exist())
    return FALSE;

  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.
{
  TFilename 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 strcpy(__tmp_string, n);
}

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


// 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
{                  
  TString16 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;
  }    
  _makepath(__tmp_string, NULL, __ptprf, firm, NULL);
  return __tmp_string;
}                      

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 << ndir;
  bool exist = s.exist();
  
  if (!exist)
  {
    s = s.path(); s.rtrim(1); s << 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 << ndir;
  if (!fcopy(&ndir[1], s))
    return error_box("Impossibile copiare il file %s della ditta %ld",
                     &ndir[1], codditta);
  s = s.path(); s << ntrc;
  if (!fcopy(&ntrc[1], 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, 70);

  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);
    const char* name = dir.name();
    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;
}