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

#include <files.h>

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

#ifndef __EXTCDECL_H
#include <extcdecl.h>
#endif

#include <strings.h>
#include <utility.h>

#define BLOCKLEN 512
#define INVFLD    255

HIDDEN char _files_tmp_string[128];

TFile ::TFile (int lenrec, int base)

{
  _file = new SecDef;
  if (_file == NULL)
    fatal_box ("Can't allocate record file ");
  _file->IOR = NOERR;
  _len = lenrec;
  _base = base;
  _file->LockMode = _manulock;
  _file->lpos = -1;
  _file->name[0] = '\0';
}

TFile ::~TFile ()

{
  delete _file;
}

void TFile ::
open (const char *name, TFilelock lockmode)

{
  COpen (_file, (char *) name, _len, _base, lockmode);
}

bool TFile ::
verify (const char *name)

{
  CVerify (_file, (char *) name);
  return _file->IOR == NOERR;
}

void TFile ::
create (const char *name, TRecnotype nrecord)

{
  CCreate (_file, (char *) name, _len, _base, (nrecord * _len) / BLOCKLEN + 1);
}

void TFile ::
chsize (const char *name, TRecnotype nrecord)

{
  CChsize (_file, (char *) name, _len, _base, (nrecord * _len) / BLOCKLEN + 1);
}

void TFile ::
close ()

{
  CClose (_file);
}

void TFile ::
unlink (const char *name)

{
  CDelete (_file, (char *) name);
}

void TFile ::
read (char *record, TRecnotype recnum, TReclock lock)

{
  _file->LenRec = _len;
  _file->BaseFil = _base;
  CRead (_file, record, recnum, lock);
}

void TFile ::
write (char *record, TRecnotype recnum, TReclock lock)

{
  _file->LenRec = _len;
  _file->BaseFil = _base;
  CWrite (_file, record, recnum, lock);
}

int TFile ::
status ()
  const

{
  return _file->IOR;
}

TDir ::
TDir ()

{
  _dir = new FileDes;
  if (_dir == NULL)
    fatal_box ("Can't allocate dir");
  zero ();
  _num = -1;
}

TDir ::~TDir ()

{
  delete _dir;
}

const char *TDir ::
name ()
  const

{
  return _dir->SysName;
}

const char *TDir ::des ()
  const

{
  return _dir->Des;
}

const char *TDir ::expr ()
  const

{
  return _dir->FCalc;
}

TRecnotype & TDir ::
eod ()
  const

{
  return _dir->EOD;
}

TRecnotype & TDir ::
eox ()
  const

{
  return _dir->EOX;
}

TRecnotype & TDir ::
flags ()
  const

{
  return _dir->Flags;
}

word & TDir ::
len ()

{
  return (word &) _dir->LenR;
}

int TDir ::
status (TDirtype dirop)
  const

{
  return fdir[dirop].IOR;
}

void TDir ::set_len (const UINT16 len)
{
  _dir->LenR = len;
}

void TDir ::
set_eox (const RecNoType eox)
{
  _dir->EOX = eox;
}

void TDir ::
set (const char *name, const RecNoType eod, const RecNoType flag, const char *des, const char *calc)
{
  strncpy (_dir->SysName, name, sizeof (_dir->SysName));
  _dir->EOD = eod;
  _dir->Flags = flag;
  strncpy (_dir->Des, des, sizeof (_dir->Des));
  strncpy (_dir->FCalc, calc, sizeof (_dir->FCalc));
}

void TDir ::
get (int nfile, TReclock lock, TDirtype dirtype, TDirop op)

{
  int _whichdir = (dirtype == _nordir ? NORDIR : COMDIR);

  if (op == _nordirop)
    COpenFile (nfile, _dir, int (lock), _whichdir);
  else
    CGetFile (nfile, _dir, int (lock), _whichdir);
  _num = nfile;
}

void TDir ::
put (int nfile, TDirtype dirtype, TDirop op)

{
  int _whichdir = (dirtype == _nordir ? NORDIR : COMDIR);

  if (nfile <= 0)
    fatal_box ("Bad file number %d", nfile);
  if (op == _nordirop)
    CCloseFile (nfile, _dir, _whichdir);
  else
    CPutFile (nfile, _dir, _whichdir);
}

void TDir ::
zero ()

{
  zerofdes (_dir);
}

int TDir ::
items (TDirtype dirtype)
  const

{
  FileDes f;
  int _whichdir = (dirtype == _nordir ? NORDIR : COMDIR);

  CGetFile (1, &f, _nolock, _whichdir);
  return (int) f.EOD;
}

TTrec ::
TTrec ()

{
  _rec = new RecDes;
  if (_rec == NULL)
    fatal_box ("Can't allocate record description");
  zero ();
  _num = -1;
}

TTrec ::~TTrec ()

{
  delete _rec;
}

int TTrec ::
compare (const TSortable & a)
  const

{
  const TTrec & r = (const TTrec &) a;
  const int res = memcmp ((const void *) _rec, (const void *) r._rec, sizeof (*_rec));
  return res;
}

void TTrec ::rehash ()

{
  setrdes (_rec);
}

TTrec & TTrec ::operator = (const TTrec & b)

{
  _num = b._num;
  memcpy (_rec, b._rec, sizeof (*_rec));
  return *this;
}

void TTrec ::
get (int nfile, TDirtype dirtype)

{
  int _whichdir = (dirtype == _nordir ? NORDIR : COMDIR);

  CGetRec (nfile, _rec, _whichdir);
  _num = nfile;
}

void TTrec ::
put (int nfile, TDirtype dirtype)

{
  int _whichdir = (dirtype == _nordir ? NORDIR : COMDIR);

  if (nfile <= 0)
    fatal_box ("Bad file number %d", nfile);
  CPutRec (nfile, _rec, _whichdir);
}

void TTrec ::
zero ()

{
  zerordes (_rec);
}

int TTrec ::
status (TDirtype dirop)
  const

{
  return rdir[dirop].IOR;
}

int TTrec ::field (const char *name)
  const

{
  return findfld (_rec, (char *) name);

}

const char *TTrec ::fielddef (int fld)
  const

{
  sprintf (_files_tmp_string, "%s|%d|%d|%d", _rec->Fd[fld].Name,
           (int) _rec->Fd[fld].TypeF, (int) _rec->Fd[fld].Len,
           (int) _rec->Fd[fld].Dec);
  return _files_tmp_string;
}

const char *TTrec ::keydef (int key)
  const

{
  TFixed_string s (_files_tmp_string, 128);

  s = "";
  for (int j = 0; j < _rec->Ky[key].NkFields; j++)
  {
    const bool upper = _rec->Ky[key].FieldSeq[j] > MaxFields;
    const int nfld = _rec->Ky[key].FieldSeq[j] - (upper ? MaxFields : 0);

    if (j)
      s << "+";
    if (upper)
      s << "UPPER(";
    s << _rec->Fd[nfld].Name;
    if (_rec->Ky[key].FromCh[j] < INVFLD)
      s << format ("[%d,%d]", _rec->Ky[key].FromCh[j] + 1,
                   _rec->Ky[key].ToCh[j] + 1);
    if (upper)
      s << ")";
  }
  s << (_rec->Ky[key].DupKeys ? "|X" : "| ");
  return (const char *) s;
}


#ifndef FOXPRO

void TTrec ::
update_fielddef (int nfld, const char *desc)

{
  TToken_string s (desc);
  strcpy (_rec->Fd[nfld].Name, s.get ());
  _rec->Fd[nfld].TypeF = s.get_int ();
  _rec->Fd[nfld].Len = s.get_int ();
  _rec->Fd[nfld].Dec = s.get_int ();
}

void TTrec ::
update_keydef (int key, const char *desc)

{
  TExpression expr ("", _strexpr);
  TToken_string s (desc);
  TString ke (s.get ());

  if (expr.set ((const char *) ke, _strexpr))
  {
    _rec->Ky[key].DupKeys = (*s.get () != ' ');
    TCodearray & c = expr.code ();
    c.begin ();
    int n = 0;
    while (!c.end ())
    {
      TCode & inst = c.step ();
      TCodesym sym = inst.getsym ();

      if (sym == _variable)
      {
        const char *s = inst.string ();

        for (int i = 0; i < _rec->NFields; i++)
          if (strcmp (_rec->Fd[i].Name, s) == 0)
            break;

        _rec->Ky[key].FieldSeq[n] = i;
        _rec->Ky[key].FromCh[n] = INVFLD;
        _rec->Ky[key].FromCh[n] = INVFLD;
        inst = c.step ();
        sym = inst.getsym ();
        if (sym == _upper)
          _rec->Ky[key].FieldSeq[n] += MaxFields;
        else if (sym == _number)
        {
          _rec->Ky[key].FromCh[n] = (int)inst.number().integer() - 1;
          inst = c.step ();
          _rec->Ky[key].ToCh[n] = _rec->Ky[key].FromCh[n] + (int)inst.number ().integer () - 1;
          inst = c.step ();
        }
        else
          c.backtrace ();
        n++;
      }
    }
    _rec->Ky[key].NkFields = n;
  }
}

void TTrec ::
print_on (ostream & out)
  const

{
  out << num () << '\n';
  const int nfields = fields ();
  TToken_string s (80);

  out << nfields << '\n';
  for (int i = 0; i < nfields; i++)
  {
    s = fielddef (i);
    out << s << '\n';
  }
  const int nkeys = keys ();

  out << nkeys << '\n';
  for (i = 0; i < nkeys; i++)
  {
    s = keydef (i);
    out << s << '\n';
  }
}

void TTrec ::
read_from (istream & in)

{
  int ln;

  in.getline (_files_tmp_string, sizeof (_files_tmp_string), '\n');
  ln = atoi (_files_tmp_string);
  if (ln != num () && !yesno_box ("Descrizione relativa al file n.ro %d.\n Continuo ?", ln))
    return;
  int nfields;

  in.getline (_files_tmp_string, sizeof (_files_tmp_string), '\n');
  nfields = atoi (_files_tmp_string);
  set_fields (nfields);
  for (int i = 0; i < nfields; i++)
  {
    in.getline (_files_tmp_string, sizeof (_files_tmp_string), '\n');
    update_fielddef (i, _files_tmp_string);
  }
  int nkeys;

  in.getline (_files_tmp_string, sizeof (_files_tmp_string), '\n');
  nkeys = atoi (_files_tmp_string);
  set_keys (nkeys);
  for (i = 0; i < nkeys; i++)
  {
    in.getline (_files_tmp_string, sizeof (_files_tmp_string), '\n');
    update_keydef (i, _files_tmp_string);
  }
  rehash ();
}

#endif // FOXPRO
