#include <sys/stat.h>
#include <applicat.h>
#include <files.h>
#include <utility.h>
#include <expr.h>
#include <isam.h>
#include <mask.h>
#include <sheet.h>
#include <msksheet.h>
#include <urldefid.h>
#include <validate.h>
#include <utility.h>
#include <progind.h>

#if XVT_OS!=XVT_OS_SCOUNIX
#include <direct.h>
#endif

#include "ba1300.h"

extern "C" {
  void cgettime(char *);
}

TTestSheet::TTestSheet(const char* titolo, byte bottoni)
:TDir_sheet(titolo, bottoni, "@1|N.@5|Nome@20|EOD@7|EOX@7|Lung.  |Descrizione@43|Flags@7") 
{}

TTestSheet::~TTestSheet()
{}

void TTestSheet::page_build(long first, byte rows)

{
  TToken_string l(128);
  for (byte i = 0; i < rows; i++)
  {
    const int n = int(i+first+1);
    dir()->get (n,_nolock,_nordir,_sysdirop);
    l.cut(0);
    l.add("");
    l.add(format("%3d", n)); // Numero progressivo del file
    l.add(dir()->name());
    const TRecnotype eod = dir()->eod();
    l.add(format("%ld", eod));
    const TRecnotype eox = dir()->eox();
    l.add(format("%ld", eox));
    const word len = dir()->len();
    l.add(format("%u", len));
    l.add(dir()->des());
    l.add(format("%ld", dir()->flags()));
    set_row(l, i);
  }
}

void TTestSheet::sr_status(bool save)
{
  for (int i=0; i<items();i++)
    if (save)
      _oldstatus.set(i,checked(i));
    else
      check(i,_oldstatus[i]);
}

const char* TTestProc::fmttime(const long tm)
{
  TString st;
  int sec=(int) tm%60;
  int min=(int) tm/60;
  int hour=min/60;
  min%=60;
  st.format("%03d:%02d:%02d",hour,min,sec);
  return (const char*) st;
}

const char * TTestProc::get_base() 
{
  TTestFile fn(1);
  fn.open();
  const char * name =(const char *) fn.file_name();
  fn.close();
  char *com;
  com=strrchr(name,Separator);
  if (com!=NULL)
    *(++com)='\0';
  return name;
}

void TTestProc::filecopy(bool save)
{
  TString St0,St1,St2;
  TTestFile fn(_nfile);

  fn.open();
  St0=fn.file_name();
  fn.close();
  St1=St0;
  St0.rtrim(4);  // cut extension
  St2=St0 << ".sta";
  /// Save/Restore data files
    if (save)
      fcopy((const char*)St1,(const char*)St2);
    else
      fcopy((const char*)St2,(const char*)St1);
  St0.rtrim(4);
  St1=St0 << ".ndx";
  St0.rtrim(4);
  St2=St0 << ".sdx";
  if (save)
    fcopy((const char*)St1,(const char*)St2);
  else
    fcopy((const char*)St2,(const char*)St1);
}

void TTestProc::remove_copy(int n)
{
  TString base(get_base());                 
  TDir d;
  d.get(n,_nolock,_nordir,_sysdirop);
  TString nome(d.name());
  TString fpath;

  nome.ltrim(1);
  nome.rtrim(4);
  fpath << base << nome << ".sta";
  remove((const char*)fpath);
  fpath.rtrim(4);
  fpath << ".sdx";
  remove((const char*)fpath);
}

void  TTestProc::do_random(long eod)
{
  long i,j,com;
  unsigned int cbits,scale,parm,rnd;
  time_t  tloc;

  // Initialize array to mix
  for (i=1; i<=eod;i++)
    _randseq[i-1]=i;
  // Compute scale
  i=eod;
  cbits=0;
  while (i)
  {
    i=i>>1;
    cbits++;
  }
  scale=(unsigned int)0x8000;
  for (i=1;i<=(long)cbits;i++)
  {
    scale=scale>>1;
    scale |= 0x8000;
  }
  // Extracts "random" seed from time
  time(&tloc);
  parm=(unsigned int)(tloc & 0x0000FFFF);
  srand(parm);
  // Mix array...
  for (i=0;i<eod;i++)
  {
    rnd=rand();
    rnd&=scale;
    for (j=1;j<=(long)(16-cbits);j++)
      rnd=rnd>>1;
    if (((long) rnd)>=eod)    // Difficile che accada...
      rnd=(unsigned int) eod-1;
    com=_randseq[i];
    _randseq[i]=_randseq[(long)rnd];
    _randseq[(long)rnd]=com;
  }
}

int TTestProc::ctrl_keys()
{
  int err=1;

  for (int nk=1;nk<=_nkeys;nk++)
  {
    _workf->setkey(nk);
    if ((err=_workf->first())==NOERR)
      return NOERR;
  }
  return err;
}

void TTestProc::do_test_1()
{
  bool FoundError;
  long i,EOD;
  int  err;
  time_t inix,finix;

  _nfile=0;
  while ((_nfile++ <= get_isheet()->items()) && !_block)
  {
    EOD=get_isheet()->row(_nfile-1).get_long(3);   
    if (get_isheet()->checked(_nfile-1) && EOD > 0)      
    {
      get_win()->WriteW(33,7,(const char*)TString(18).format("%-18s",get_isheet()->row(_nfile-1).get(2)));
      get_win()->WriteW(33,12,"000:00:00");
      get_win()->WriteW(33,13,"000:00:00");
      if (_ntest == 1)
      {
        TSystemisamfile f(_nfile);
        get_win()->WriteW(33,10,"COMPATTAMENTO              ");
        if ((f.packfile(FALSE)!=NOERR)||(f.packindex(FALSE) != NOERR))
          error_box("Errore in compattamento file %d",_nfile);
        get_win()->WriteW(33,10,"COPIA DI SICUREZZA         ");
        filecopy();
        set_rmc(TRUE);
      } // If it was test # 1
      _workf = new TTestFile(_nfile);
      time(&inix);
      if (_workf->open(_excllock) == NOERR)
      {
        TTrec r;
        r.get(_nfile);
        _nkeys=r.keys();
        FoundError=FALSE;
        i=0;
        EOD=_workf->eod();
        get_win()->WriteW(33,9,(const char*)TString(14).format("     0/%-7ld",EOD));
        get_win()->WriteW(33,10,"TEST1 (cisreadrec-cisdelete)");
        _randseq = new long[EOD];
        do_random(EOD);
        while (!FoundError && i<EOD && !_block)
        {
          if ((err=_workf->readat(number(i++)))==NOERR)
            err=_workf->remove();
          if (err!=NOERR)
            FoundError=TRUE;
          if (get_win()->iscancelled())
            _block=TRUE;
          if ((i%20)==0)
            get_win()->WriteW(33,9,(const char*)TString(20).format("%7ld/%-7ld",i,EOD));
          time(&finix);
          get_win()->WriteW(33,12,(const char*)TString(10).format("%s",fmttime(finix-inix)));
          if (((finix-inix)%3)==0)
          {
            long com=((finix-inix)*EOD)/(i+1);
            get_win()->WriteW(33,13,(const char*)TString(10).format("%s",fmttime(com)));
          }
        }
        if (!_block && ((ctrl_keys()==NOERR) || FoundError))
          do_test_2();  
        _workf->close();
        get_win()->WriteW(33,10,"RIPRISTINO ARCHIVI              ");
        filecopy(FALSE);  //restore copies
        if (_randseq!=NULL)
          delete _randseq;
      }  // If it was open
      if (_workf != NULL)
        delete _workf;
    }    // If it was selected
  }        // While test is in progress
}

void TTestProc::do_test_2()
{
  time_t alpha,omega;
  long i=0,j;
  int err=NOERR;

  get_win()->WriteW(33,10,"TEST2: RIPRISTINO ARCHIVI       ");
  _workf->close();
  filecopy(FALSE); 
  if (_workf->open(_excllock) == NOERR)
  {
    long EOD=_workf->eod();
    long optot=sumop(EOD);
    long optr=1;

    get_win()->WriteW(33,10,"TEST2 (cisreadrec-cisdelete)");
    time(&alpha);
    while (!_block && i<EOD && err==NOERR)
    {
      _workf->readat(number(i));
      err=_workf->remove();
      j=i+1;
      get_win()->WriteW(33,10,"TEST2: CONTROLLO CHIAVI        ");
      get_win()->WriteW(33,9,(const char*)TString(20).format("%7ld/%-7ld",j,EOD));
      while (!_block && j<EOD && err==NOERR)
      {
        if (get_win()->iscancelled()) _block=TRUE;
        time(&omega);
        get_win()->WriteW(33,12,(const char*)TString(10).format("%s",fmttime(omega-alpha)));
        if ((omega-alpha)%3==0)
          get_win()->WriteW(33,13,(const char*)TString(10).format("%s",fmttime(((omega-alpha)*optot)/optr)));
        err=_workf->readat(number(j));
        optr++;
        int k=1;   
        while (k<=_nkeys && err==NOERR)
        {
          TTrec r;
          r.get(_nfile);
          TToken_string def(r.keydef(k-1));

          _workf->setkey(k);
          err=_workf->read();
          if (def.get_char(1)=='X') // if keys are duplicated
          {
            TString ksaved(_workf->curr().key(k));
            bool Fnd=FALSE;

            while (err==NOERR && !Fnd && ksaved==_workf->curr().key(k))
              if (_workf->recno()==number(j)) 
                Fnd=TRUE;
              else
                err=_workf->next();
          }
          if (err!=NOERR || number(j)!=_workf->recno()) 
          {
            get_win()->WriteW(33,10,"Trovati errori scrittura report");
            DumpReport(err,i,j,k);
            if (err==NOERR)
              err=999;   //errore fittizio, tanto per farlo uscire
          }
          k++;
        }
        j++;
      }
      get_win()->WriteW(33,10,"TEST2 (cisreadrec-cisdelete)         ");
      i++;
    }
  }
}

void TTestProc::DumpReport(int err, long i, long x, int key)
{
  static rnum=0;
  char ora[12] = "08-08-1883";
  TString RandomFile,ReportFile;
  FILE * fp;

  get_win()->WriteW(33,15,(const char*)TString(35).format("File nr. %d   Test nr. %ld",_nfile,_ntest));
  
  if (!fexist("test"))   // create directory
#if XVT_OS==XVT_OS_SCOUNIX 
    mkdir("test",0777);
#else
#ifdef __WATCOMC__
  mkdir("test");
#else
  _mkdir("test");
#endif
#endif
  RandomFile.format("test%crnd%d.dat",Separator,rnum++); 
  if ((fp=fopen((const char*)RandomFile,"w"))!=NULL)
  {
    for (int j=0;j<_workf->eod();j++)
      if (j==i)
        fprintf(fp,"%9ld*\n",number(j));
      else
        fprintf(fp,"%9ld \n",number(j));
    fclose(fp);
  }
  ReportFile.format("test%creport.txt",Separator);
  if ((fp=fopen((const char*)ReportFile,"a"))!=NULL)
  {
    fprintf(fp,"REPORT sulla verifica della stabilita' dell'ISAM\n");
    fprintf(fp,"------\n");
    fprintf(fp,"\n");
    fprintf(fp,"Eseguito il : %s        Alle : %s\n",(const char*)TDate(TODAY),ora);
    fprintf(fp,"\n");
    fprintf(fp,"File nr.    : %4d    %s\n",_nfile,get_isheet()->row(_nfile-1).get(2));
    fprintf(fp,"File Random : %s\n",(const char*)RandomFile);
    fprintf(fp,"Nr. Record cancellato: %5ld    Nr. Record letto: %5ld\n",number(i),number(x));
    fprintf(fp,"Nr. Chiave elaborata: %3d\n",key);
    if (err!=NOERR)
      fprintf(fp,"Errore generato dal record letto: %d\n",err);
    else
      fprintf(fp,"Errore generato dal record letto:  Numero di record non corretto.\n\n");
    fprintf(fp,"------\n\n\n");
    fclose(fp);
  }
}

void TTestProc::load_random(const TString& st)
{
  FILE *fp;
  char c;

  _kriminal=0;
  if ((fp=fopen((const char*)st,"r"))!=NULL)
  {
    TDir d;
    d.get(_nfile,_nolock,_nordir,_sysdirop);
    _randseq = new long[d.eod()];
    for (long i=0;i<d.eod() && !feof(fp);i++)
    {
      fscanf(fp,"%9ld%c",&_randseq[i],&c);
      if (c=='*')
        _kriminal=_randseq[i];
      if (_randseq[i]>d.eod())
      {
        _block=TRUE;
        break;
      }
    }
  }
}

void TTestProc::do_debug()
{
  TDir d;

  d.get(_nfile,_nolock,_nordir,_sysdirop);
  get_win()->set_color(COLOR_BLACK,COLOR_WHITE);
  get_win()->clear(COLOR_WHITE);
  get_win()->set_caption("Ricerca dell'errore");
  get_win()->WriteW(8,5,(const char*)TString(25).format("File numero         : %4d\t\t\t%s",_nfile,(const char*)d.name()));
  get_win()->WriteW(8,7,"Record Cancellati   :");
  get_win()->WriteW(8,9,"Operazione in corso :  COPIA DI SICUREZZA");
  get_win()->WriteW(8,11,"Record cancellato   :");
  get_win()->WriteW(8,12,"Record letto        :");
  get_win()->WriteW(8,14,"Errore rilevato     :");

  filecopy();            // Backup copies. Must be placed here 
  // otherwise I get a "File already open error"
  _workf = new TTestFile(_nfile);
  if (_workf->open(_excllock) == NOERR)
  {
    TTrec r;
    r.get(_nfile);
    _nkeys=r.keys();
    long i=0,j;
    int k,err=NOERR;
    bool step=FALSE;
    get_win()->WriteW(30,9,"Cancello fino al Kriminale...");
    while (!stopped() && i<_workf->eod() && err==NOERR)
    {
      if (number(i)==_kriminal)
        step=TRUE;   // Put here a Break Point to debug step by step
      err=_workf->readat(number(i));
      if (err!=NOERR || step)
        _block=show_mess(1,err,number(i),number(i),0);
      err=_workf->remove();
      if (err!=NOERR || step)
        _block=show_mess(2,err,number(i),number(i),0);
      j=i+1;
      get_win()->WriteW(30,9,"TEST 2: CONTROLLO CHIAVI       ");
      get_win()->WriteW(30,7,(const char*)TString(20).format("%5ld/%-5ld",j,_workf->eod()));
      get_win()->WriteW(30,11,(const char*)TString(10).format("%5ld",number(i)));
      while (!stopped() && j<_workf->eod() && err==NOERR)
      {
        if (get_win()->iscancelled())
          _block=TRUE;
        err=_workf->readat(number(j));
        if (err!=NOERR || step)
          _block=show_mess(3,err,number(j),number(j),0);
        get_win()->WriteW(30,12,(const char*)TString(10).format("%5ld",number(j)));
        k=1;
        while (k<=_nkeys && err==NOERR && !stopped())
        {
          TToken_string def(r.keydef(k-1));

          _workf->setkey(k);
          err=_workf->read();
          if (err!=NOERR || step)
            _block=show_mess(4,err,number(j),_workf->recno(),k);
          if (def.get_char(1)=='X') // if keys are duplicated
          {
            TString ksaved(_workf->curr().key(k));
            bool Fnd=FALSE;

            while (!stopped() && err==NOERR && !Fnd && ksaved==_workf->curr().key(k))
              if (_workf->recno()==number(j)) 
                Fnd=TRUE;
              else
              {
                err=_workf->next();
                if (err!=NOERR || step)
                  _block=show_mess(5,err,number(j),_workf->recno(),k);
              }                                          
          }   // if keys are duplicated
          if (err!=NOERR || number(j)!=_workf->recno()) 
          {
            _block=show_mess(6,err,number(j),number(j),0);
            err=999;   // So it will exit
          }
          k++;
        }
        j++;
      }
      get_win()->WriteW(30,9,"Cisreadrec -- Cisdelete                  ");
      i++;
    }
    _workf->close();
  }     
  get_win()->WriteW(30,9,"RIPRISTINO ARCHIVIO                          ");
  filecopy(FALSE);       // Restore copies
  get_win()->WriteW(30,9,"CANCELLAZIONE COPIE                          ");
  remove_copy(_nfile);   // Delete backup copies

  // Free pointers
  if (_workf!=NULL)
    delete _workf;
  if (_randseq != NULL)
    delete _randseq;
}

bool TTestProc::show_mess(int act, int err, long n1, long n2, int k)
{
  switch (act)
  {
  case 1:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Eseguita la readrec per rec. # %ld",n1));
    get_win()->WriteW(30,11,(const char*)TString(40).spaces());
    break;
  case 2:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Eseguita la delete   per rec. # %ld",n1));
    get_win()->WriteW(30,11,(const char*)TString(10).format("%5ld",n1));
    break;
  case 3:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Eseguita la readrec per contr. chiavi"));
    get_win()->WriteW(30,12,(const char*)TString(10).format("%5ld         ",n1));
    break;
  case 4:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Eseguita la start con chiave  %d         ",k));
    get_win()->WriteW(30,12,(const char*)TString(15).format("%5ld (%ld)",n1,n2));
    break;
  case 5:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Eseguita la next per chiavi doppie  "));
    get_win()->WriteW(30,12,(const char*)TString(15).format("%5ld (%ld)",n1,n2));
    break;
  case 6:
    get_win()->WriteW(30,9,(const char*)TString(42).format("Record %ld non trovato nelle chiavi duplicate  ",n1));
    break;
  }
  get_win()->WriteW(30,14,(const char*)TString(5).format("%5d",err));
  KEY tasto=get_win()->run();
  return (tasto == K_ESC);
}

TTestProc::TTestProc()
: _block(FALSE), _ntest(0), _nfile(0), _randseq(NULL), _rmc(FALSE), _workf(NULL), _sheet(NULL), _win(NULL)
{
  _win = new TTestWin;
}

TTestProc::~TTestProc()
{
  if (_win != NULL)
    delete _win;
}

TTestWin::TTestWin()
:TIndwin(1L,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",TRUE,FALSE,70)
{}

void TTestWin::handler(WINDOW w, EVENT *e)
{
  switch (e->type)
  {
  case E_CHAR:
    if (e->v.chr.ch == K_ENTER)
      stop_run(K_ENTER);
    break;
  default: 
    break;
  }
  TIndwin::handler(w,e);
}

TTestReport::TTestReport(const char* title)
{
  WINDOW parent=TASK_WIN;
  long flags=WSF_CLOSE | WSF_SIZE | WSF_VSCROLL | WSF_HSCROLL;
  WIN_TYPE wt=W_DOC;
  create(6,2,68,20,title,flags,wt,parent);
  set_color(COLOR_BLACK,COLOR_WHITE);
}

void TTestReport::load_report()
{
  FILE *fp;
  char line[256];

  fp=fopen((const char *)TString(20).format("test%creport.txt",Separator),"r");
  while (fgets(line,256,fp)!=NULL)
  {
    TString element(line);
    _report.add(element);
  }
  fclose(fp);
  set_scroll_max(80,_report.items());
}

void TTestReport::update()
{
  for (int i=0; i<_report.items(); i++)
    stringat(1,i+1,(const char*)get_row(i));
}

bool TTestReport::on_key(KEY key)
{
  switch (key)
  {
  case K_ESC:
    stop_run(key);
    break;
  default:
    break;
  }
  return TScroll_window::on_key(key);
}
