/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include "relation.h"
static char rcsidh[] = relation_h;
static char rcsid[] = "$Id: relation.cpp 5.5 2001/04/19 09:37:14 picoSoft Exp Marco $";
# include "constant.h"
# include "stmtrel.h" 
# include "database.h" 
# include "sqltype.h" 
# include "perror.h"

extern "C" {
# include "picoisam.h"
};
extern "C" {
# include <stdlib.h> 
# ifdef MSDOS
# include <dos.h>
# include <process.h>
# include <io.h>
# ifdef WIN32
# define open(a,b,c) _lopen(a, OF_READWRITE|OF_SHARE_COMPAT)
# else
# define open(a,b,c) _lopen(a, READ_WRITE|OF_SHARE_COMPAT)
# endif
# define lseek       _llseek
# define read        _lread
# define write       _lwrite
# define close       _lclose 
# else
# include <unistd.h>
# endif
} 
extern void remSpacesToUpper(char *s, long i);

// array per ocuupazione di memoria per tipi BINARY a seconda
// della lunghezza

PCLASSID(RelBase)
PCLASSID(Relation)

RelBase::RelBase (const char *s, DataBase &p) :
RelObject (s, p)
{
   RelationType = Standard;         
   FileType = bst;
   BufferLen = 0;
   Permission = REL_READ; 
   relCond = 0; 
   relCondWhere = "";
   PhisicFile = PF_NEVER_OPEN;
   Collating = (char *)0;
}

RelBase::RelBase (RelBase &rb) :
RelObject (rb)
{
   RelationType = rb.RelationType;
   FsDirName = rb.FsDirName;
   FsDataName = rb.FsDataName; 
   FileType = rb.FileType;
   MinRecordLen = rb.MinRecordLen;
   MaxRecordLen = rb.MaxRecordLen;
   RealRecordLen = rb.RealRecordLen;
   BufferLen = rb.BufferLen;
   FsKeyNum = rb.FsKeyNum; 
   Permission = rb.Permission;
   relCond = rb.relCond;
   relCondWhere = rb.relCondWhere;
   PhisicFile = rb.PhisicFile;
   if (rb.Collating) {
      Collating = new char[256];
      memcpy (Collating, rb.Collating, 256);
   } else
      Collating = (char *) 0;
}

RelBase::~RelBase()
{
   setCollating((char *) 0);
}

void
RelBase::setCollating(char *coll)
{
   if (Collating) {
      delete Collating;
      Collating = (char *)0;
   }
   if (coll) {
      Collating = new char[256];
      /*per debug  da levare
      for (int j = 0; j < 256; j++)
         Collating[j] = (unsigned char) j;
      Collating['A'] = 'H';
      Collating['H'] = 'A';
      fino qui */
      for (register int i = 0; i < 256; i++)
         Collating[(unsigned char)coll[i]] = (unsigned char) i;
      // O forse basta la memcpy
      // memcpy (Collating, coll, 256);
   }
}

PBool
RelBase::FileOpenExists ( accessType *fd )
{ 
   return parent->FileOpenExists (FsDirName, FsDataName, this, fd);
}

Relation::Relation (Relation &r) :
RelBase (r)
{
   Attribute *av;
   Attribute *aa;
   Attribute *an;
   
   base = r.base;
   aa = r.AllAttrs.GetFirst();

   for (av = r.VisAttrs.GetFirst (); av != 0; av = r.VisAttrs.GetNext ())
      for ( ; aa != 0; aa = r.AllAttrs.GetNext ()) {
         AllAttrs.AddItem (an = new Attribute (*aa));
         if (aa == av) {
            VisAttrs.AddItem (an);
            aa = r.AllAttrs.GetNext();
            break;
         }
      }
   for ( ; aa != 0; aa = r.AllAttrs.GetNext ())
      AllAttrs.AddItem (an = new Attribute (*aa));
}

Relation::Relation (StmtAttrList &l, DataBase &p) :
RelBase ("sort", p)
{
   StmtAttr  *sa;
   Attribute *an;

   base = 0;

   FileType = sort;
   FsDataName = Name; 
   RelationType = SortRel;
   for (sa = l.GetFirst (); sa != 0; sa = l.GetNext ()) {
      an = AddInvis (sa);
      VisAttrs.AddItem (an);
      an->DelVisibility(ATTR_HIDDEN);
   }
   BufferLen = 0;  
   relCond = 0; 
   relCondWhere = "";
}

Relation::Relation (DataBase &p) :
RelBase ("sort", p)
{
   base = 0;

   FileType = sort;
   FsDataName = Name; 
   RelationType = SortRel;
   BufferLen = 0;  
   relCond = 0; 
   relCondWhere = "";
}


Relation::~Relation ()
{
   Attribute *a;

   for (a = AllAttrs.GetFirst (); a != 0; a = AllAttrs.DeleteCurrent())
      delete a;
   for (a = VisAttrs.GetFirst (); a != 0; a = VisAttrs.DeleteCurrent())
      ;
}

Attribute *
Relation::AddVisible (StmtAttr *sa)
{
   Attribute *Return = AddInvis (sa->getAttr());
   Return->setName (sa->getName().gets());
   VisAttrs.AddItem (Return);
   Return->DelVisibility(ATTR_HIDDEN);
   return Return;
}

Attribute *
Relation::AddInvis (StmtAttr *sa)
{
   Attribute *Return = AddInvis (sa->getAttr());
   Return->setName (sa->getName().gets());
/*
   if (sa->IsCalculated()) {
      Return->setType (T_DOUBLE, 0, 0);
      Return->setSignature(Return);
   }
*/
   return Return;
}

Attribute *
Relation::AddInvis (Attribute *a)
{
   Attribute *Return;
  
   AllAttrs.AddItem (Return = new Attribute (*a));
   Return->setParent(this);
   Return->setPos(0);
   Return->AddVisibility(ATTR_HIDDEN);
   return Return;
}

void
Relation::SetInvis (Attribute *a)
{
   Attribute *ao;
   for (ao = VisAttrs.GetFirst(); ao != 0; ao = VisAttrs.GetNext())
      if (ao->getSignature() == a->getSignature()) {
         ao->AddVisibility (ATTR_HIDDEN);
         VisAttrs.DeleteCurrent();
         break;
      }
}

void
Relation::BufferCalc ()
{
   Attribute *a;

   if (BufferLen == 0)  
      for (a = AllAttrs.GetFirst (); a != 0; a = AllAttrs.GetNext()) {
         a->setPos (BufferLen);
         BufferLen += a->getFldSize();
      }
}

int
Relation::GetNumRecords (long *numRecords)
{
   int Return = 0;
   struct dictinfo dictInfo;
   StmtRel sr(*this);
   *numRecords = NULLLONG;

   if (sr.Open() == SQL_SUCCESS) {  
      switch (FileType) {  
      case sort:
         PError::InternalError ("GetNumRecords for sort file");
         break;  
      case bst:
         long curpos;

         curpos = lseek(sr.getFsId().id_bst, 0L,SEEK_CUR);
         *numRecords = lseek(sr.getFsId().id_bst, 0L, SEEK_END) / BufferLen;
         lseek(sr.getFsId().id_bst, curpos, SEEK_SET);
         break;
      case  pIsam:
         pIsindexinfo (sr.getFsId().id_isam, &dictInfo, 0);
         if (pIsGetErrno() != 0)
            Return = pIsGetErrno();
         else
            *numRecords = dictInfo.di_nrecords;
         break;             
      default:
         PError::InternalError ("Invalid type in GetNumRecords");
         break;
      }
      sr.Close();
   } else
      Return = sr.getStatus();
   return Return;
}
# define DF_CRIPT  0
# define DF_NAME   1
# define DF_CONUM  2
# define DF_CONAM  3
# define DF_DTYPE  4
# define DF_LENGT  5
# define DF_SCALE  6
# define DF_SIGN   7
# define DF_NULL   8
# define DF_INCR   9
# define DF_PROG  10
# define DF_REMAR 11

# ifdef _DEBUG
void
# else
inline void
# endif
LoadIndexPart (int nkey, struct keydesc &key, AttributeList &AllAttrs)
{
   IndexPart ip;
   Attribute * a = AllAttrs.GetFirst ();
   register int i;
   int start, len;
   PBool loop;
   int npart = 1;
   
   for (i = 0; i < key.k_nparts && a; i++) {
      start = key.k_part[i].kp_start;
      len = key.k_part[i].kp_leng;
      do {
         loop = PFalse;
         for (a = AllAttrs.GetFirst (); a; a = AllAttrs.GetNext ())
            if (a->getFldPos () == start) {
               if (a->getFldSize() > len)
                  a = 0;
               else {
                  ip.setNumber (nkey);
                  ip.setPart (npart++);
                  ip.setDups ((key.k_flags & ISDUPS) != 0);
                  a->AddIndex (ip);
                  if (a->getFldSize() < len) {
                     start += a->getFldSize();
                     len -= a->getFldSize();
                     loop = PTrue;
                  }
               }
               break;
            }
      } while (loop);
   }
}
Error
Relation::ReadFields(StmtRel *relFields)
{
   StmtAttr  *sa1, *sa2;
   Attribute *f; 
   char * valChar;
   double valNum;
   double dim;
   double dec;
   long len;
   short numCol = 0;
   short valCriptType = 0; 
   PString msg;
   char *key;
   union uCript {
      INT4 valCript;
      char chrCript[4];
   } cript;
   PString nomeTabella;
   Error Return = SQL_SUCCESS;
   IndexPart ip;

   Return = relFields->Open();
   if (Return != SQL_SUCCESS) {
      PrintError (E_CONN_EXCEPT_001, "Error opening __COLUMNS", relFields->getStatus());
      return Return; 
   }

   if (relFields->getCurrIndex() != 0) {
      StartEnd *se;
      StartEndList *sel = relFields->getCurrIndex()->AttrsAndCond.GetCurrent();
      se = sel->GetFirst();
      if (se->CondStart == 0) {   
         sa1 = relFields->GetFieldAt(DF_NAME); 
/*  O^O
         se->AddCond (sa1, (toknum)'=', new StmtAttr (*sa1));
*/
         // Token tk;
         // tk.token = STR_LITERAL;
         // tk.word = "\"" + Name + "\"";
         se->AddCondToDelete (new Expression (0, sa1), (toknum)'=',
                              new StmtAttr (*sa1));
      }
      se->CondStart->getValue()->FromString (Name.gets()); 
      se->CondEnd->getValue()->FromString (Name.gets());
   }
   Return = relFields->Start();
   if (Return == EENDFILE || Return == ENOREC)
      Return = SQL_SUCCESS;
   if (Return != SQL_SUCCESS) {
      PrintError (E_CONN_EXCEPT_001, "Error reading __COLUMNS", Return);
      return Return; 
   }
   while (relFields->ReadNext(PFalse) == SQL_SUCCESS) {
      sa1 = relFields->GetFieldAt(DF_NAME);
      sa1->ToChar(&valChar, len);
      remSpacesToUpper(valChar, sa1->getAttr()->GetLen() - 1);
      if (Name == (const char *)valChar) { 
         sa2 = relFields->GetFieldAt(DF_CONAM);
         sa2->ToChar(&valChar, len);
         remSpacesToUpper(valChar, sa2->getAttr()->GetLen() - 1);
         if (valChar[0] == '@') { // Data dict nidificato O^O
            RelObjectIterator rbi = getParent()->getRelBases();
            PString saveName = Name;
            for (rbi.reset(); *rbi; rbi++)
               if ((*rbi)->isName(valChar)) {
                  StmtRel dd(*relFields->getRel());
                  SearchIndex *i = new SearchIndex (1);
                  sa1 = dd.GetFieldAt(DF_NAME); 
                  i->AddAttr (sa1);
                  i->initKey ();
                  dd.AddIndex (i);
                  dd.SetCurrIndex (i);
                  Name = valChar;
                  if (this->ReadFields(&dd) == SQL_SUCCESS)
                     break;
                  else
                     return SQL_ERROR;
               }
            Name = saveName;
            if (*rbi == 0) {
               PrintError (E_CONN_EXCEPT_001,
                           "Unknown inner table <" + PString(&valChar[1]) + ">");
               return SQL_ERROR;
            } else
               continue;
         }
         f = new Attribute (valChar, this);
         sa2 = relFields->GetFieldAt(DF_LENGT);
         sa2->ToDouble(dim, len);
         sa2 = relFields->GetFieldAt(DF_SCALE);
         sa2->ToDouble(dec, len); 
         sa2 = relFields->GetFieldAt(DF_SIGN);
         sa2->ToDouble(valNum, len);
         switch ((int)valNum) {
         case L_SIGNED:
            f->setSign (INTERNAL_TRAILING);
            break;
         case L_UNSIGNED:
            f->setSign (UNSIGNED);
            break;
         case L_INT_TRAIL:
            f->setSign (INTERNAL_TRAILING);
            break;
         case L_INT_LEAD :
            f->setSign (INTERNAL_LEADING);
            break;
         case L_EXT_TRAIL :
            f->setSign (EXTERNAL_TRAILING);
            break;
         case L_EXT_LEAD:
            f->setSign (EXTERNAL_LEADING);
            break;
         default: 
            break;
         }  
         sa2 = relFields->GetFieldAt(DF_DTYPE);
         sa2->ToDouble(valNum, len);
         valCriptType = (short)valNum;
         switch((int)valNum) {
         case L_CSTRING:
            f->setType (T_CSTRING, (long) dim, 0);
            break;
         case L_SHORT:
            f->setType (T_SHORT, 0, 0);
            break;
         case L_LONG:
            f->setType (T_LONG, 0, 0);
            break;
         case L_FLOAT:
            f->setType (T_FLOAT, 0, 0);
            break;
         case L_DOUBLE:
            f->setType (T_DOUBLE, 0, 0);
            break;
         case L_BLOB:
            f->setType (T_BLOB, (long) dim, (long) dec);
            break;
         case L_CLOB:
            f->setType (T_CLOB, (long) dim, (long) dec);
            break;
         case L_PACKED_ORDERED:
            sa2 = relFields->GetFieldAt(DF_INCR);
            sa2->ToDouble(valNum, len);
            if (valNum != 0)
               f->setType (T_PACKED_ORDERED, (long) dim, (long) dec, PTrue);
            else
               f->setType (T_PACKED_ORDERED, (long) dim, (long) dec, PFalse);
            break;
         case L_TIMESTAMP:
            f->setType (T_PACKED_ORDERED, (long) dim, (long) dec);
            f->setDateFmt (TIMEST_INTFMT);
            break;
         case L_TIME:
            f->setType (T_PACKED_ORDERED, (long) dim, (long) dec);
            f->setDateFmt (TIME_INTFMT);
            break;
         case L_DATE:
            f->setType (T_PACKED_ORDERED, (long) dim, (long) dec);
            f->setDateFmt (DATE_INTFMT);
            break;
         default:
            PrintError (E_CONN_EXCEPT_001, "Unknown type"); 
            delete f; 
            return SQL_ERROR;
            break;
         }
/*
 // tolta per mettere la gestione automatica degli indici         
         sa2 = relFields->GetFieldAt(DF_KEYS);
         ip.Clear ();
         sa2->ToDouble(valNum, len);
         ip.setNumber (valNum);
         sa2 = relFields->GetFieldAt(DF_OFFST);
         sa2->ToDouble(valNum, len);
         ip.setPart (valNum);
         sa2 = relFields->GetFieldAt(DF_DUPS);
         sa2->ToDouble(valNum, len);
         if (valNum == L_DUPS )
            ip.setDups (PTrue);
         else
            ip.setDups (PFalse);
         sa2 = relFields->GetFieldAt(DF_ORD);
         sa2->ToDouble(valNum, len);

//       if (valNum == L_DESC )
//          f->setIndexDesc ();

         f->AddIndex (ip);
*/
         f->setPos (BufferLen);
         BufferLen += f->getFldSize();
         sa2 = relFields->GetFieldAt(DF_CONUM);
         sa2->ToDouble(valNum, len);
         msg.prints("%s%s%d%d%d",
                            (char *)Name.gets(),
                            f->getName().gets(),
                            (int)valNum,
                            (int)f->GetLen(),
                            (int)valCriptType);
         if (f->getName().size() == 0 || msg.size() == 0)  {
            msg.prints("Column %d of file %s is corrupted in datadictionary",
                            (int)valNum,
                            (char *)Name.gets());
            PrintError (E_WARNING_000, msg.gets());
            f->setName ("FILLER");
         }

         sa2 = relFields->GetFieldAt(DF_CRIPT);
         sa2->ToLong(cript.valCript, len);
         AllAttrs.AddItem (f);
         if (!(f->GetVisibility() & ATTR_HIDDEN))
            VisAttrs.AddItem (f);
      } else {
         Return = (Error) EENDFILE;
         break;
      }
   }
   if (relFields->getStatus() != EENDFILE && relFields->getStatus() != ENOREC) {
      PrintError (E_CONN_EXCEPT_001, "Error reading __COLUMNS", relFields->getStatus());
      Return = SQL_ERROR; 
   } else {
// Inizio gestione automatica degli indici
      accessType fd;
      if (Name[0] != '@' && FileOpenExists (&fd)) {
         struct dictinfo dict;
         struct keydesc key;
         int i;
         switch (FileType) {
         case pIsam:
            if (pIsindexinfo(fd.id_isam, &dict, 0) == 0)
               for (i = 1; i <= dict.di_nkeys; i++)
                   if (pIsindexinfo(fd.id_isam, &key, i) == 0)
                      LoadIndexPart (i, key, AllAttrs);
            break;
         }
         parent->Close (this, fd);
      }
// Fine gestione automatica degli indici
      Return = SQL_SUCCESS;
   }
   relFields->Close(); 
   return Return;
}
