/*
_____       _    _    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 "serchidx.h"
# include "piteratr.h"
# include "perror.h"
# include "express.h"
static char rcsid[] = "$Id: serchidx.cpp 5.5 2002/04/05 13:24:56 picoSoft Exp $";
static char rcsidh[] = serchidx_h;
# ifdef MSDOS
# define strcpy lstrcpy
# endif
                      
# include "stmtrel.h"
# include "where.h"
# include "perror.h" 
# include "trace.h"
extern "C" {
# include <string.h>
}
extern "C" {
# include "picoisam.h"
};
/* 
StartEnd::StartEnd(Expression *a, toknum t, Expression *c)
{
   Attr = a; 
   CondStart = CondEnd = LikeCfrt = 0;
   OpStart = OpEnd = (toknum) 0;
   AddCond (a, t, c);
}
*/
void
StartEnd::AndCond (StartEnd &se1, StartEnd &se2)
{
   Attr = se1.Attr;
   int st = 0;
                     
   if (se1.CondStart)
      if (se1.CondStart->getAttr()->getParent() == 0)
         st = 1;
      else
         st = 2;
   else if (se1.LikeCfrt)
      if (se1.LikeCfrt->getAttr()->getParent() == 0)
         st = 1;
      else
         st = 2;

   if (se2.CondStart)
      if (se2.CondStart->getAttr()->getParent() == 0)
         st += 4;
      else
         st += 8;
   else if (se2.LikeCfrt)
      if (se2.LikeCfrt->getAttr()->getParent() == 0)
         st = 4;
      else
         st = 8;

   switch (st) {
   case 0:
      if (se1.OpStart)
         OpStart = se1.OpStart;
      else if (se2.OpStart)
         OpStart = se2.OpStart;
      break;
   case 1: 
   case 2: 
   case 5: 
   case 9: 
   case 10: 
      CondStart = se1.CondStart;
      OpStart = se1.OpStart;
      if (se1.LikeCfrt)
         LikeCfrt = new Expression (*(se1.LikeCfrt));
      break;
   case 4: 
   case 6: 
   case 8: 
      CondStart = se2.CondStart;
      OpStart = se2.OpStart;
      if (se2.LikeCfrt)
         LikeCfrt = new Expression (*(se2.LikeCfrt));
      break;
   }
   if (LikeCfrt == 0) {
      st = 0;
      if (se1.CondEnd)
         if (se1.CondEnd->getAttr()->getParent() == 0)
            st = 1;
         else
            st = 2;
      if (se2.CondEnd)
         if (se2.CondEnd->getAttr()->getParent() == 0)
            st += 4;
         else
            st += 8;
      switch (st) {
      case 0:
         if (se1.OpEnd)
            OpEnd = se1.OpEnd;
         else if (se2.OpEnd)
            OpEnd = se2.OpEnd;
         break;
      case 1: 
      case 2: 
      case 5: 
      case 9: 
      case 10: 
         CondEnd = se1.CondEnd;
         OpEnd = se1.OpEnd;
         break;
      case 4: 
      case 6: 
      case 8: 
         CondEnd = se2.CondEnd;
         OpEnd = se2.OpEnd;
         break;
      }
   }
   if (CondStart && CondEnd)
      if (CondStart->getAttr()->getParent() == 0 &&
          CondEnd->getAttr()->getParent() != 0)
         CondEnd = 0;
      else if (CondStart->getAttr()->getParent() != 0 &&
          CondEnd->getAttr()->getParent() == 0)
         CondStart = 0;
}

void
StartEnd::AddCond (Expression *a, toknum op, Expression *cfrt)
{
   switch (op) {
   case _OJ_:
   case '=':
      if (Attr == a->getValue())
         CondStart = CondEnd = cfrt;
      else if (Attr == cfrt->getValue())
         CondStart = CondEnd = a;
      else
         PError::InternalError ("Bad Addcond");
      OpStart = _GE_;
      OpEnd = _LE_;
      break;
   case '<':
      if (Attr == a->getValue()) {
         CondEnd = cfrt;
         OpEnd = op;
      } else if (Attr == cfrt->getValue()) {
         CondStart = a;
         OpStart = (toknum)'>';
      } else
         PError::InternalError ("Bad Addcond");
      break;
   case _LE_:
      if (Attr == a->getValue()) {
         CondEnd = cfrt;
         OpEnd = op;
      } else if (Attr == cfrt->getValue()) {
         CondStart = a;
         OpStart = _GE_;
      } else
         PError::InternalError ("Bad Addcond");
      break;
   case '>':
      if (Attr == a->getValue()) {
         CondStart = cfrt;
         OpStart = op;
      } else if (Attr == cfrt->getValue()) {
         CondEnd = a;
         OpEnd = (toknum) '<';
      } else
         PError::InternalError ("Bad Addcond");
      break;
   case _GE_:
      if (Attr == a->getValue()) {
         CondStart = cfrt;
         OpStart = op;
      } else if (Attr == cfrt->getValue()) {
         CondEnd = a;
         OpEnd = (toknum) _LE_;
      } else
         PError::InternalError ("Bad Addcond");
      break;
   case LIKE:
      if (Attr == a->getValue())
         LikeCfrt = new Expression (*cfrt);
      else
         PError::InternalError ("Bad Addcond");
      break;
   case IS_NULL:
      OpStart = IS_NULL;
      OpEnd = IS_NULL;
      break;
   default:
      break;
   } 
}

void
StartEnd::BuildLike ()
{
   char *pnt;
   int len;
   Token t;
   
   if (LikeCfrt) {
      CondEnd = CondStart = 0;
      pnt = LikeCfrt->getValueUpdated()->GetBuffer();
      if (pnt != 0 &&
          pnt[0] != '%' &&
          pnt[0] != '_') {
         len = LikeCfrt->getValue()->getAttr()->GetLen();
         t.word = '\'';
         t.word.concat(pnt, len);
         t.word += '\'';
         t.token = STR_LITERAL;
         CondStart = new Expression (LikeCfrt->getValue()->getDB(), t);
         pnt = CondStart->getValue()->GetBuffer();
         register int i;
         for (i = 0; i < len && pnt[i] != '%' && pnt[i] != '_';  i++)
            if (pnt[i] == '\\') {
               len--;
               for (int j = i; j < len; j++)
                  pnt[j] = pnt[j + 1];
               i++;
            }
         if (i < len) {
            if (CondStart->getAttr()->GetType() == T_CSTRING) {
               pnt[i] = '\0';
               CondStart->getValue()->getAttr()->SetLen(i);
               CondStart->getValue()->getAttr()->SetSize(i + 1);
            } else {
               CondStart->getValue()->getAttr()->SetLen(i);
               CondStart->getValue()->getAttr()->SetSize(i);
            } 
            t.word = '\'';
            t.word.concat(pnt, i);
            t.word += '\'';
            CondEnd = new Expression (LikeCfrt->getValue()->getDB(), t);
            pnt = CondEnd->getValue()->GetBuffer();
            pnt[i - 1] += 1;
            OpStart = _GE_;
            OpEnd = (toknum)'<';
         } else { // Corrisponde a = e quindi si siusa lo stesso Token
            CondEnd = new Expression (LikeCfrt->getValue()->getDB(), t);
            OpEnd = OpStart = (toknum)'=';
         }
      }
   }
}


PCLASSID (SearchIndex)

void
SearchIndex::AddAttr (StmtAttr *a)
{
   StartEnd *se;
   StartEndList *sel;
   if (a != 0) {
      se = new StartEnd;
      se->Attr = a;
      Rel = a->getAttr()->getParent();
      sel = AttrsAndCond.GetFirst();
      if (sel == 0) {
         FirstAttr = a;
         sel = new StartEndList;
         sel->AddItem (se);
         AttrsAndCond.AddItem (sel);
      } else
         sel->AddItem (se);
   }
}

void
SearchIndex::Init (short indNum)
{
   Clear();
   IndexNum = indNum;
   IsamKey.k_part[0].kp_start = 0;
   IsamKey.k_part[0].kp_leng  = 0;
   IsamKey.k_part[0].kp_type = CHARTYPE;
   IsamKey.k_nparts = 0;
   IsamKey.k_flags = ISDUPS;
}

void
SearchIndex::Clear ()
{
   StartEndList *sel;
   StartEnd *se;
   
   for (sel = AttrsAndCond.GetFirst(); sel != 0;
        sel = AttrsAndCond.DeleteCurrent()) {
      for (se = sel->GetFirst(); se != 0; se = sel->DeleteCurrent())
         delete se;
      delete sel;
   }
   JoinIndex = PFalse;
}

PITERATOR(StartEndList);
 
void
SearchIndex::SortSELL ()
{
  StartEndListList newList;
  StartEndList *selold, *selnew;
  StartEnd *seold, *senew;

  for (selold = AttrsAndCond.GetFirst(); selold; selold = AttrsAndCond.GetNext())
     for (seold = selold->GetFirst(); seold; seold = selold->GetNext())
        seold->BuildLike();

  if ( AttrsAndCond.GetItemNum() == 1)
     return;
     
  while ( AttrsAndCond.GetItemNum() > 0) {
     selnew = 0;
     for (selold = AttrsAndCond.GetFirst(); selold; selold = AttrsAndCond.GetNext()) {
        if (selnew == 0)
           selnew = selold;
        else {
           for (seold = selold->GetFirst(), senew = selnew->GetFirst();
                senew && seold;
                seold = selold->GetNext(), senew = selnew->GetNext()) {   
              if (senew->CondStart != 0) {
                 if (seold->CondStart != 0) {
                    if (senew->CondStart->getAttr()->getParent() != 0 ||
                        seold->CondStart->getAttr()->getParent() != 0) {
                       if (seold->CondStart->getAttr()->getParent() != 
                           senew->CondStart->getAttr()->getParent())
                          PError::InternalError ("sortSELL error");
                       continue;
                    }
                    if (senew->CondStart->Compare(
                        seold->CondStart,(toknum)'<', PFalse)) {
                       break;
                    } else if (senew->CondStart->Compare(
                             seold->CondStart,(toknum)'>', PFalse)) {
                       selnew = selold;
                       break;
                    } else {
                       if (seold->OpStart != (toknum)'>' &&
                           senew->OpStart == (toknum)'>') {
                          selnew = selold;
                          break;
                       } else if (seold->OpStart == (toknum)'>' &&
                                  senew->OpStart != (toknum)'>') {
                          break;
                       } else
                          continue;
                    }
                 } else {
                    selnew = selold;
                    break;
                 }
              } else {
                  if (seold->CondStart != 0) {
                     break;
                  } else {
                     continue;
                  }
              }
           }
        }
     }
     for (selold = AttrsAndCond.GetFirst(); selold != selnew;
          selold = AttrsAndCond.GetNext())
        ;
     AttrsAndCond.DeleteCurrent ();
     newList.AddItem (selnew);
   }
   for (selnew = newList.GetFirst(); selnew;
        selnew = newList.GetNext())
      AttrsAndCond.AddItem (selnew);
}                     
                
 
void
SearchIndex::And (Where &w, ConditionList &cl)
{                                             
   StartEndListIterator seli1i (AttrsAndCond); 
   StartEndListIterator seli2i (AttrsAndCond);
   StartEndListIterator seli1 (AttrsAndCond);
   StartEndListIterator seli2 (AttrsAndCond);
   StartEnd *se0, *se1, *se2;
   StartEndList *selold, *selnew;
   StartEndList *sel1 = AttrsAndCond.GetLast ();
   StartEndList *sel2 = new StartEndList;
   StartEndListList *sell = new StartEndListList;
   
   for (se1 = sel1->GetFirst(); se1 != 0; se1 = sel1->GetNext()) {
      se2 = new StartEnd;
      se2->Attr = se1->Attr;
      sel2->AddItem (se2);
   }
   
   seli1.SetCurrList();
   w.GetPrevious();
   NextCond (w, cl);
   
   AttrsAndCond.AddItem (sel2);
   seli2.SetCurrList();
   w.GetPrevious();
   NextCond (w, cl);  
                  
   for ( seli1i = seli1; *seli1i && *seli1i != *seli2; seli1i++) {
      for (seli2i = seli2; *seli2i; seli2i++) {
         selnew = new StartEndList;
         for (se1 = (*seli1i)->GetFirst(), se0 = (*seli2i)->GetFirst();
              se1 != 0 && se0 != 0;
              se1 = (*seli1i)->GetNext(), se0 = (*seli2i)->GetNext()) {
            se2 = new StartEnd;
            se2->AndCond (*se0, *se1);
            selnew->AddItem(se2);
         } 
         sell->AddItem(selnew);
      }
   }
   
   seli1.GetList();
   for (selold = AttrsAndCond.GetCurrent (), selnew = sell->GetFirst ();
        selold && selnew;
        selold = AttrsAndCond.GetNext(),     selnew = sell->GetNext ()) {
      for (se0 = selold->GetFirst(); se0 != 0; se0 = selold->DeleteCurrent())
         delete se0;
      for (se0 = selnew->GetFirst(); se0 != 0; se0 = selnew->GetNext())
         selold->AddItem (se0);
   }
   if (selold != 0)
      for ( ;selold; selold = AttrsAndCond.DeleteCurrent()) { //sj
         for (se0 = selold->GetFirst(); se0 != 0; se0 = selold->DeleteCurrent())
             delete se0;
         delete selold; //sj
      }    
   else if (selnew != 0)
      for (  ; selnew; selnew = sell->DeleteCurrent ())
          AttrsAndCond.AddItem (selnew);
   for (selnew = sell->GetFirst(); selnew;
        selnew = sell->DeleteCurrent())
      delete selnew;
   delete sell;
}
 
void
SearchIndex::Or (Where &w, ConditionList &cl)
{   
   StartEnd *se1, *se2;
   StartEndList *sel1 = AttrsAndCond.GetLast ();
   StartEndList *sel2 = new StartEndList;
   
   for (se1 = sel1->GetFirst(); se1 != 0; se1 = sel1->GetNext()) {
      se2 = new StartEnd;
      se2->Attr = se1->Attr;
      sel2->AddItem (se2);
   } 
   w.GetPrevious();              
   NextCond (w, cl);
                       
   AttrsAndCond.AddItem (sel2);
   w.GetPrevious();
   NextCond (w, cl);
}

void
SearchIndex::NextCond (Where &w, ConditionList &cl)
{                 
   StartEndList *sel;
   StartEnd *se;
   Condition *c, *c1;
   ConditionList AndCond;
   StartEndListList sell;
                         
   c = w.GetCurrent();
   switch (c->Operator) {
   case OR:
      Or (w, cl); 
      break;
   case AND:
      And (w, cl);
      break;
   default:
      for (c1 = cl.GetFirst(); c1 != 0; c1 = cl.GetNext())
         if (c == c1) {
            sel = AttrsAndCond.GetLast();
            for (se = sel->GetFirst(); se != 0;
                 se = sel->GetNext()) {
               if (se->Attr == c->left->getValue() ||
                   (c->right && se->Attr == c->right->getValue())) { // O^O
                  se->AddCond (c->left, c->Operator, c->right); // O^O
                  // break;
               }
            }
         }
      break;
   }
}

inline static const char*
tostring (toknum Operator)
{
   switch (Operator) {
   case '=':
      return "=";
   case '<':
      return "<";
   case '>':
      return ">";
   case _GE_:
      return ">=";
   case _LE_:
      return "<=";
   case _OJ_:
      return "_=";
   case IS_NULL:
      return "=_";
   default:
      return "?";
   }
}

void
SearchIndex::Build (Where &w, ConditionList &cl)
{                         
   StartEndList *sel;
   StartEnd *se;

   w.GetLast();
   NextCond (w, cl);
   initKey();
    
   sel = AttrsAndCond.GetFirst();
   se = sel->GetFirst();
   FirstAttr = se->Attr;
   FirstAttr->IndexPnt = this;
   FirstAttr->StmtParent->AddIndex (this);
   for ( ; se != 0; se = sel->GetNext())
      if (se->CondStart && se->CondStart->getAttr()->getParent() ||
          se->CondEnd && se->CondEnd->getAttr()->getParent()) {
         JoinIndex = PTrue;
         break;
      }
   if (Trace.isSet) {
      toknum opStart, opEnd;
      char startBuf[64];
      char endBuf[64];
      for (sel = AttrsAndCond.GetFirst(); sel != 0;
           sel = AttrsAndCond.GetNext())
          for (se = sel->GetFirst(); se != 0; se = sel->GetNext()) {
             Trace.Write ("--->Index[idx] %s, part=%d, num=%d, par=%s\n",
                             se->Attr->getAttr()->getName().gets(),
                             se->Attr->getAttr()->GetIndexPart(IndexNum),
                             IndexNum,
                             se->Attr->getAttr()->getParent()->getName().gets());
          opStart = opEnd = (toknum)'?';
          if (se->CondStart) { 
             opStart = se->OpStart; 
             if (se->CondStart->getAttr()->getParent() != 0)
                strcpy (startBuf, se->CondStart->getAttr()->getName().gets());
             else {
                // se->CondStart->getAttr()->ToChar(&out,len);
                strcpy (startBuf, "!");
             }
          } else
             startBuf[0] = 0;
          if (se->CondEnd) {
             opEnd = se->OpEnd;  
             if (se->CondEnd->getAttr()->getParent() != 0)
                strcpy (endBuf, se->CondEnd->getAttr()->getName().gets());
             else {
                // se->CondEnd->getAttr()->ToChar(&out,len);
                strcpy (endBuf, "!");
             }
          } else
             endBuf[0] = 0;
               
            Trace.Write ("--->Cond start ([%s],[%s]), end ([%s],[%s])\n",
                             tostring(opStart),
                             startBuf,
                             tostring(opEnd),
                             endBuf);
                            
         } 
     }  
/**/                    
}

void
SearchIndex::CutJoinFields (StartEndList *sel)
{
   StartEnd *se;
     
   for (se = sel->GetFirst(); se != 0; se = sel->GetNext())
      if (se->CondStart && se->CondStart->getAttr()->getParent() ||
          se->CondEnd && se->CondEnd->getAttr()->getParent()) {
         for ( ; se; se = sel->DeleteCurrent())
            delete se;
         break;
      }
}

PBool
SearchIndex::IsTrueCondEnd ()
{
   PBool Return = PTrue;
   StartEnd *se;
   StartEndList *sel = AttrsAndCond.GetCurrent();
   int rc;
   
   for (se = sel->GetFirst(); se; se = sel->GetNext())
      if (se->CondEnd) {
         rc = se->Attr->IndexCompare(se->CondEnd->getValueUpdated());
         if (se->OpEnd == '<') {
            if (rc >= 0)
               Return = PFalse;
            else
               Return = PTrue;
            break;
         } else {   // _LE_ || '=' 
            if (rc > 0) {
               Return = PFalse;
               break;
            } else if (rc < 0) {
               Return = PTrue;
               break;
            }
         }
/*      
         if (!se->Attr->Compare(se->CondEnd->getValueUpdated(),
                                           se->OpEnd, PFalse)) {
            Return = PFalse;
            break;
         } else if (!se->Attr->Compare(se->CondEnd->getValueUpdated(),
                                           (toknum) '=', PFalse)) {
            Return = PTrue;
            break;
         }
*/
      } else if (se->OpEnd == IS_NULL) {
         if (!se->Attr->isNull()) {
            Return = PFalse;
            break;
         }
      } else {
         Return = PTrue;
         break;
      }
   return Return;
}

PBool
SearchIndex::IsTrueCondStart ()
{
   PBool Return = PTrue;
   StartEnd *se;
   StartEndList *sel = AttrsAndCond.GetCurrent();
   int rc;
   
   for (se = sel->GetFirst(); se; se = sel->GetNext())
      if (se->CondStart) {
         rc = se->Attr->IndexCompare(se->CondStart->getValueUpdated());
         if (se->OpStart == '>') {
            if (rc <= 0)
               Return = PFalse;
            else
               Return = PTrue;
            break;
         } else {   // _GE_ || '=' 
            if (rc < 0) {
               Return = PFalse;
               break;
            } else if (rc > 0) {
               Return = PTrue;
               break;
            }
         }
      }
/*
         if (!se->Attr->Compare(se->CondStart->getValueUpdated(), se->OpStart, PFalse)) {
            Return = PFalse;
            break;
         }
*/
   return Return;
}

SearchIndex::~SearchIndex ()
{
/*
   if (Attr != 0)
      Attr->IndexPnt = 0;
*/
   Clear();
}

void
SearchIndex::initKey ()
{  
   struct dictinfo dict;
   struct keydesc key;
   int rc, i;
   PBool loop = PTrue;
   Attribute *a;
   int part;
   StartEndList *sel;
   StartEnd *se;
   
   sel = AttrsAndCond.GetFirst();
   se = sel->GetFirst();
   if (se && se->Attr->getAttr()->IsIndexNumber (IndexNum) > 0) {
      if (se->Attr->GetStmtParent()->isOpen()) {
         switch (se->Attr->getAttr()->getParent()->getFileType ()) {
         default:
         case bst:
            break;
         case pIsam:
            pIsindexinfo(se->Attr->GetStmtParent()->FsId.id_isam, &dict, 0);
            for (i = 1; i <= dict.di_nkeys; i++) {
                rc = pIsindexinfo(se->Attr->GetStmtParent()->FsId.id_isam, &key, i);
                if (rc == 0 && key.k_part[0].kp_start == se->Attr->getAttr()->getFldPos()) {
                   IsamKey = key;
                   break;
                }   
            }
            break;
         }
      } else {
         part = 1;
         while (loop ) {
            loop = PFalse;
            for (a = se->Attr->getAttr()->getParent()->getAllAttrs().GetFirst();
                     part <= NPARTS && a != 0;
                     a = se->Attr->getAttr()->getParent()->getAllAttrs().GetNext()) {
               if (a->IsIndexNumber (IndexNum) &&
                   a->GetIndexPart (IndexNum) == part) {
                  IsamKey.k_part[part - 1].kp_start = (short)a->getFldPos();
                  IsamKey.k_part[part - 1].kp_leng  = (int)a->getFldSize();
                  switch (a->GetType()) {
                  default:
                     IsamKey.k_part[part - 1].kp_type = CHARTYPE;
                     break;
                  case T_SHORT:
                     IsamKey.k_part[part - 1].kp_type = INTTYPE;
                     break;
                  case T_LONG:
                     IsamKey.k_part[part - 1].kp_type = LONGTYPE;
                     break;
                  case T_DOUBLE:
                     IsamKey.k_part[part - 1].kp_type = DOUBLETYPE;
                     break;
                  case T_FLOAT:
                     IsamKey.k_part[part - 1].kp_type = FLOATTYPE;
                     break;
                  }
/*                  
                  if (a->IndexDesc)
                     IsamKey.k_part[part - 1].kp_type |= ISDESC;
*/                     
                  part ++;
                  loop = PTrue;
               }
            }
         }
         IsamKey.k_nparts = part - 1;
         if (se->Attr->getAttr()->IsIndexDups(IndexNum))
            IsamKey.k_flags = ISDUPS;
         else
            IsamKey.k_flags = ISNODUPS;
      }
   }
}


