#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define __PREFIX_CPP
#include <prefix.h>

#ifndef FOXPRO
#include <applicat.h>
#endif

#include <extcdecl.h>
#include <isam.h>
#include <scanner.h>
#include <utility.h>

#include <codeb.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 = ".";
  _dirfl = dirfl;
  _recfl = recfl;
  _fdir = fdir;
  _rdir = rdir;
  
  CGetPref();
  const TFilename dir(cprefix);

  const long primaditta = atol(dir.name());
  if (primaditta > 0 && !exist(primaditta))
  {
    ofstream out_pr(prefname());
    out_pr << "com" << endl;
  }  
}


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 && errno == EACCES)
          fatal_box("Can't reopen file n. %d : file in use", i + 1);
        isfd->fhnd = DB_open(isfd->d->SysName, excl[i]);
        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_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_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();
    _prefix = cprefix;
    const int l = strlen(__ptprf);
    if (l > 0) _prefix.ltrim(l);
  }
  else
  {      
    _prefix = name;
    if (*__ptprf && *name) strcpy(cprefix, __ptprf);
    else strcpy(cprefix, "");
    strcat(cprefix, name);
  }

  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 << "/dir.gen";

  if (fexist(s))
  {
    s = s.path();
    s << "/trc.gen";
    return fexist(s);
  }
  return FALSE;
}

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

  if ((_dirfl[0] > 1) || (_dirfl[1] > 1) ||
      (_recfl[0] > 1) || (_recfl[1] > 1))
    return error_box("Impossibile cambiare ditta (dir.gen o trc.gen in uso)");
  
  return TRUE;
}


void TPrefix::put()

{
  CPutPref((char*)(const char*)_prefix);
}


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


long TPrefix::get_codditta() const

{
  const long codditta = atol((const char*)_prefix);
  return codditta;
}


bool TPrefix::set_codditta(long codditta, bool force)
{
  if (force || test(codditta))
  {
    TString16 s; s.format("%05lda", codditta); 
    set(s, force);
    put();
    return TRUE;
  }
  return FALSE;
}

// @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.
{
  TString80 n(cod);

  if (n[0] == '%') 
    n.ltrim(1);
  
  const int logicnum = atoi(n);
  if (logicnum == 0)
  {
    TFilename name("batb"); 
    name << n << ".msk";
    if (fexist(name.lower()))
    {
      TScanner m(name);
      while (m.line().left(2) != "PA");      // Find PAGE
      const int apicia = m.token().find('"')+1;
      const int apicic = m.token().find('"', apicia);
      n = m.token().sub(apicia, apicic);
    } 
    else n.cut(0);
  }  
  else
  {
    if (logicnum > 0 && logicnum < items())
    { 
      const 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
{                  
  TFixed_string dir(__tmp_string, 256); 
  switch (codditta)
  {
  case -2:                                     // Dati generali campione
  case -1:                                     // Dati di studio
    dir = ""; break;
  case  0:                                     // Dati comuni
    dir = "com"; break;
  default:                                     // Dati ditta
    dir.format("%05lda", codditta); break;
  }    
  dir.insert(__ptprf, 0);
  return __tmp_string;
}