#include <applicat.h>
#include <isam.h>  
#include <date.h>
#include <prefix.h>
#include <progind.h>
#include <urldefid.h>
#include <utility.h>

#define DAYBIAS                                 36525L
#define DAYYEAR                                 365

extern "C" {
  int findfld(RecDes *, const char *);
};


byte            m1[12]                   = {31,28,31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int             m2[12]                   = {31,59,90,120,151,181,212,243,273,304,334,365};

HIDDEN  int CGetField(const char *fieldname,RecDes* recd,RecType recin,long* d) 

{
  int           p, i;
  unsigned int  off;
  byte          len;
  char          *s1, s[10];

  if ((p = findfld(recd, fieldname)) != -1)
  {
    off = recd->Fd[p].RecOff;
    len = recd->Fd[p].Len;
    if (recd->Fd[p].TypeF != _datefld)
    {
      while ((recin[off] == ' ') && (len))
      {
        off++;
        len--;
      }
    }
    if (len)
    {
      s1 = recin + off;
      for (i = 0; i < len; i++) s[i] = s1[i];
      s[len] = '\0';
      while ((len) && (s[len -  1] == ' ')) s[--len] = '\0';
    }
    else strcpy(s, "");
    if (recd->Fd[p].TypeF == _datefld)
    {
      if (strlen(s))
        *d=atol(s);
      else *d=0;
    }
    return (0);
  }
  else
  {
    fatal_box("Non esiste il campo %s", fieldname);
    return(-1);
  }
}


class TIsam_date_converter : public TApplication
{
  TFilename  _logfile;
  bool       _errors_found;
protected:
  virtual bool create () ;
  virtual bool destroy();
  void update();
  void update_dir();
  void convert_dir();
  int  convert_file(int logicnum);

public:
  TIsam_date_converter()  {}
  ~TIsam_date_converter() {}

};

bool TIsam_date_converter::create() // initvar e arrmask

{
  TApplication::create();
  update();
  return FALSE;
}

bool TIsam_date_converter::destroy() // releasev e arrmask

{
  return TApplication::destroy() ;
}

void TIsam_date_converter::update()

{ 
  _logfile.temp("cnvlog");
  FILE * fp = fopen(_logfile,"w");
  fclose(fp);             
  _errors_found = FALSE;

  long firm = get_firm();             
  TString pref;
  if (firm == 0) pref = prefix().name();
  
  do_events();
  begin_wait();
  // Converte i files in formato  DBIII, FOXPRO...
  prefix().set("");
  update_dir();
  set_autoload_new_files(FALSE);
  prefix().set("com");
  update_dir();
  convert_dir();

  TLocalisamfile ditte(LF_NDITTE);
  
  for (ditte.first(); !ditte.eof(); ditte.next())
  {
    const long codditta = ditte.get_long("CODDITTA");
    const TRecnotype rec = ditte.recno();
    
    if (prefix().exist(codditta))
    {
      set_firm(codditta);
      update_dir();
      convert_dir();
    }
  }    
  
  if (firm > 0) set_firm(firm);
  else prefix().set(pref);
  set_autoload_new_files(TRUE);
  end_wait(); 
  if (_errors_found)
  {
    TString80 s;
    s << "Esaminare il file di log " << _logfile << " per il riepilogo degli errori." ;
    message_box(s);
  } else 
    ::remove(_logfile);
}

void TIsam_date_converter::update_dir()

{
  TDir d;

  d.get(LF_DIR);                 
  const int items = (int)d.eod();
  TString80 s("Aggiornamento direttorio ");
  TString pref(prefix().name());

  if (pref.empty()) s << "standard";
  else
    if (pref == "com") s << "comune";
    else s << " della ditta " << atol (pref);
  
  TProgind p((long)(items ? items : 1), s, TRUE, TRUE, 70);
  
  p.setstatus(1L);
  for (int i = 2; i <= items; i++)
  { 
    p.addstatus(1L);
    d.get(i, _nolock, _nordir, _sysdirop);                 

    TFilename s(d.name());

    s.ext("");
    strcpy((char *) d.name(), s);
    d.put(i, _nordir, _sysdirop);
  }
} 

void TIsam_date_converter::convert_dir()

{         
  const TString pref(prefix().name());
  const bool is_com = prefix().is_com();  
  TDir d;
  
  d.get(LF_DIR);                 
  const int items = (int)d.eod();
  TString80 s("Aggiornamento archivi ");

  if (pref == "com") s << "comuni";
  else s << " della ditta " << atol (pref);
  
  TProgind p((long)(items ? items : 1), s, TRUE, TRUE, 70);

  p.setstatus(1L);
  
  for (int i = 2; i <= items; i++)
  {                   
    p.setstatus((long)(i+1));
    d.get(i, _nolock, _nordir, _sysdirop);                 

    const bool to_convert = (is_com ? d.is_com() : d.is_firm());
    
    if (to_convert)
      convert_file(i);     
  }
}

int TIsam_date_converter::convert_file(int logicnum)
  
{
  TDir  dir;
  TTrec r;
  int   err = NOERR;
  bool restore_original=FALSE, retry_file=FALSE;
  r.get(logicnum); 
  RecDes *rd=r.rec();
  TRecnotype i;
  const int nflds  = r.fields();
  const int nkeys  = r.keys();
  

  do                        // External loop creation file in case of a disk full error. 
  {                         // Appending records can easily reduce disk space!
    dir.get(logicnum);
    if (dir.len() == 0 || nflds < 1 || nkeys < 1) return 0;
    const char * fp = dir.name();
    TFilename old(fp);
    const TRecnotype nitems = dir.eod(); 
    const TRecnotype neox   = dir.eox();
    TFile f(dir.len());  
    old.ext("dta");
    if (fexist(old))
    {
      f.open(old);
    {
      TSystemisamfile newfile(logicnum);
      bool retry = FALSE;
      do                   // Internal loop creation file in case of a disk full error.
      {                    // The program asks to continue (suspending momentanely execution),
        newfile.build(10); // permitting the user to switch to a DOS session and free space on disk.
        switch (newfile.status())
        {
        case NOERR:
          retry=FALSE;
          break;
        case _isfilefull:
          if (!yesno_box("Il disco e' pieno. Non riesco a creare il file %s. Riprovo?", fp))
          {
            message_box("Liberare spazio sull'unita' interessata e rieseguire la conversione.");
            stop_run();
          }
          retry=TRUE;
          break;
        default:
          fatal_box("Non riesco a creare il file %s : errore n. %d", fp, newfile.status());
          break;
        }
      } while (retry);   
    }
      
      TString s(80);   

      s.format("Aggiornamento archivio %s", fp);
      TProgind p((long)(nitems ? nitems : 1), s, TRUE, TRUE, 70);
      TLocalisamfile newfile(logicnum);
      TToken_string ts(128);          // Build token_string with the names of date fields and logical fields
      TToken_string tslog(128);       

      for (int j=0; j<r.fields(); j++)  
        if (rd->Fd[j].TypeF == _datefld)  
          ts.add(rd->Fd[j].Name);                        
        else
          if (rd->Fd[j].TypeF == _boolfld)
            tslog.add(rd->Fd[j].Name);
      const int tslog_items = tslog.items();      
      const int ts_items = ts.items();      
      
      for (i = 1;  newfile.good() &&  i <= dir.eod(); i++)
      {                               
        f.read(newfile.curr().string(),i);
        newfile.curr().setdirty();
        if (newfile.curr().string()[0] == '\0')  
        {
          newfile.curr().recall();
          if (tslog_items > 0)
            for (const char * field_name = tslog.get(0); field_name != NULL; field_name = tslog.get())
            {
              const TString16 value(newfile.curr().get(field_name));
              
              newfile.put(field_name,value == "X");
            }                
          if (ts_items > 0)
            for (const char *field_name = ts.get(0); field_name != NULL; field_name = ts.get())
            {
              long wd;
              CGetField(field_name,rd,newfile.curr().string(),&wd);
              TDate d(wd);
              newfile.put(field_name,d);
            }
        }
        else
          newfile.curr().discard();       
        if ((i % 50) == 0) p.setstatus((long)(i + 1));
        if (!newfile.curr().isdeleted())
          newfile.write();  
      }
      p.setstatus((long)nitems);
      f.close();             
      err=newfile.status();
      if (newfile.good())
      {      
        old.ext("dta");
        ::remove(old);
        old.ext("ndx");
        ::remove(old);
        restore_original=FALSE;
        retry_file=FALSE;
      }
      else 
      {                                                                        
        _errors_found=TRUE;
        if (err == _isfilefull)
        {
          if (!yesno_box("Il disco e' pieno. Non riesco a scrivere sul file %s. Riprovo?", fp))
          {
            message_box("Liberare spazio sull'unita' interessata e rieseguire la conversione.");
            retry_file=FALSE;
          } else
            retry_file=TRUE;
        }
        else 
        {
          error_box("Errore n.ro %d nella conversione dell' archivio %s:\n  %ld records non convertiti.",
                    err, fp, nitems - i + 2);
          retry_file=FALSE;
        }
        restore_original=TRUE;
      }
    } 
    else
    {
      // Crea il file se non esiste il relativo .DTA.
      const TString pref(prefix().name());
      const bool is_com = prefix().is_com();  

      prefix().set(""); 
      dir.get(logicnum); 
      const int module = abs((int)dir.flags());
      prefix().set(pref);
      dir.get(logicnum, _nolock, _nordir, _sysdirop); 
      bool to_create = (is_com ? dir.is_com() : dir.is_firm());
      if (to_create && has_module(module, CHK_DONGLE))
      {
        dir.get(logicnum);
        TFilename s(dir.name());
        s.ext("dbf");
        if (!fexist(s))     // Crea il file solo se non esiste il .DBF
        {
          TSystemisamfile f(logicnum);
          bool retry = FALSE;
          do          
          {           
            f.build(10); 
            switch (f.status())
            {
            case NOERR:
              break;
            case _isfilefull:
              if (!yesno_box("Il disco e' pieno. Non riesco a creare il file %s. Riprovo?", fp))
              {
                message_box("Liberare spazio sull'unita' interessata e rieseguire la conversione.");
                stop_run();
              }
              retry=TRUE;
              break;
            default:
              fatal_box("Non riesco a creare il file %s : errore n. %d", fp, f.status());
              break;
            }
          } while (retry);   
        }
      }
    } 
    if (restore_original)
    {
      // Remove files partially converted
      old.ext("dbf");
      xvt_fsys_file_remove(old);
      TToken_string t(10);
      get_idx_names(logicnum,t);
      FOR_EACH_TOKEN(t, name)
        xvt_fsys_file_remove(name);
      if (t.items() > 1) // A .cgp must be removed too!
      {
        old.ext("cgp");
        xvt_fsys_file_remove(old);
      }
      // Put dir to restore original EOD and EOX.
      dir.get(logicnum,_lock,_nordir,_sysdirop);
      dir.eod()=nitems;
      dir.eox()=neox;
      dir.put(logicnum,_nordir,_sysdirop);
      
      if (!retry_file)
      {
        FILE *f = fopen((const char*)_logfile,"a");                       
        old.ext("dta");
        fprintf(f,"File: %s (%d). %ld records non convertiti. Errore %d.\n",
                (const char*) old, logicnum, nitems-i+2, err);
        fclose(f);
      }
    }         
  } while (retry_file && (err == _isfilefull)); 
  if (err == _isfilefull) // This happens only when the user decides to answer NO.
    stop_run(); 
  return err;
}

int main(int argc, char** argv)
{
  TIsam_date_converter a;

  a.run(argc, argv, "Conversione a CodeBase");
  return 0;
}