/*
_____       _    _    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 "conditn.h"
static char rcsid[] = "$Id: conditn.cpp 5.2 2000/08/22 14:55:57 picoSoft Exp $";
static char rcsdih[] = conditn_h;
# include "stmtrel.h" 
# include "database.h"
# include "where.h" 
# include "sqlpars.h" 

/* Nuovo ottimizzatore */
# include "piteratr.h"
# include "perror.h"
# include "serchidx.h"
 
PLIST(ConditionList); 
PITERATOR(Condition);
PLIST(ExpressionList);
inline toknum
Invert (toknum Operator)
{
   switch (Operator) {
   case '=':
      return _NE_;
   case '<':
      return _GE_;
   case '>':
      return _LE_;
   case _NE_:
      return (toknum)'=';
   case _GE_:
      return (toknum)'<';
   case _LE_:
      return (toknum)'>';
/* La outer join non dovrebbe mai essere negata
   case _OJ_:
      return _OJ_;
*/
   case AND:
      return OR; 
   case OR:
      return AND;
   case NOT_BETWEEN:
      return BETWEEN;
   case BETWEEN:
      return NOT_BETWEEN;
   case NOT_IN:
      return IN_;
   case IN_:
      return NOT_IN;
   case NOT_LIKE:
      return LIKE;
   case LIKE:
      return NOT_LIKE;
   case IS_NULL:
      return IS_NOT_NULL;
   case IS_NOT_NULL:
      return IS_NULL;
   case EXISTS:
      return NOT_EXISTS;
   case NOT_EXISTS:
      return EXISTS;
   default:
      PError::InternalError ("Unknown operator in Invert");
      return (toknum) 0;
   }
}

inline PBool
IsValidForIndex (toknum Operator)
{
   switch (Operator) {
   case '=':
   case '<':
   case '>':
   case _GE_:
   case _LE_:
   case _OJ_:
   case BETWEEN:
   case IN_:
   case LIKE:
   case IS_NULL:
      return PTrue;
   default:
      return PFalse;
   }
}
/* Fine nuovo ottimizzatore */ 

PCLASSID(Condition)

Condition::~Condition ()
{
   if (left)
      delete left;
   if (right)
      delete right;
   if (subQuery)
      delete subQuery;
}

Condition::Condition (toknum op, SqlParser *p, SqlParser *subq)
{
   parent = p;
   Operator = op;
   left = right = 0;
   subQuery = subq;
}

Condition::Condition (toknum op, Expression *ex, SqlParser *p)
{
   parent = p;
   Operator = op;
   left = ex;
   right = 0;
   subQuery = 0;
}

Condition::Condition (toknum op, Token *t, SqlParser *p)
{
   Expression *ex;
   
   parent = p;
   Operator = op;
   ex = new Expression (parent, new StmtAttr (*(parent->GetDB()), *t));
   left = ex;
   right = 0;
   subQuery = 0;
}

void
Condition::SetRight (Expression *ex)
{
   right = ex;
}

void
Condition::SetRight (Token *t)
{
   Expression *ex;
   
   ex = new Expression (parent, new StmtAttr (*(parent->GetDB()), *t));
   right = ex;
}

void
Condition::toString (PString &result)
{
   PString s1, s2;
   if (left)
      left->toString(s1);
   if (right)
      right->toString(s2);

   switch (Operator) {
   case '=':
   case '<':
   case '>':
   case _OJ_:
      result = s1 + (char)Operator + s2;
      break;
   case _GE_:
      result = s1 + ">=" + s2;
      break;
   case _NE_:
      result = s1 + "<>" + s2;
      break;
   case _LE_:
      result = s1 + "<=" + s2;
      break;
   case LIKE:
      result = s1 + " LIKE " + s2;
      break;
   case NOT_LIKE:
      result = " NOT " + s1 + " LIKE " + s2;
      break;
   case IS_NULL:
      result = s1 + " IS NULL ";
      break;
   case IS_NOT_NULL:
      result = s1 + " IS NOT NULL ";
      break;
/* Not implemented;
   case EXISTS:
   case NOT_EXISTS:
*/
   default:
      PError::InternalError ("Invalid operator in Condition::toString()");
      break;
   }
}

Error
Condition::Eval (StmtRel *sr, PBool &result)
{
   Error Return = SQL_SUCCESS;

   if (left && sr->getOrder() < left->getHighestStmtRelOrder())
      result = PTrue;
   else
      switch (Operator) {
      case _OJ_:
         result = PTrue;
         break;
      case '=':
      case '<':
      case '>':
      case _GE_:
      case _NE_:
      case _LE_:
      case LIKE:
      case NOT_LIKE:
         if (sr->getOrder() < right->getHighestStmtRelOrder())
            result = PTrue;
         else
            result = left->Compare (right, Operator, PFalse);
         break;
      case IS_NULL:
            result = left->getValueUpdated()->isNull ();
         break;
      case IS_NOT_NULL:
            result = !left->getValueUpdated()->isNull ();
         break;
      case EXISTS:
      case NOT_EXISTS:
         if (subQuery) {
            if (subQuery->Exec() != SQL_ERROR) {
               switch (subQuery->Fetch()) {
               case SQL_NO_DATA_FOUND:
                  result = PFalse;
                  Return = SQL_SUCCESS;
                  break;
               case SQL_SUCCESS:
                  result = PTrue;
                  Return = SQL_SUCCESS;
                  break;
               default:
                  Return = SQL_ERROR;
                  break;
               }
               result = Operator == NOT_EXISTS ? !result : result;
            } else {
               Return = SQL_ERROR;
            }
         } else {
            PrintError (E_SYNTAX_ERROR_000, "EXISTS without SubQuery");
            Return = SQL_ERROR;
         }
         break;
      default:
         PError::InternalError ("Invalid operator in Eval");
         break;
      }
   return Return;
}

void
WeighIndex (Where &l)
{
   Condition *c;
   ExpressionListList attrForIdx;
   Expression  *san[2];
   int i;
   SearchIndexList indexList;
   SearchIndex *si;
   ConditionList *cl1 = 0, *cl2 = 0, *cln;
   ConditionListList condForIdx;
   Condition *c1,*c2;
   PBool found;
   IndexPart *ip;
   
   for (c = l.GetFirst(); c != 0; c = l.GetNext())
      switch (c->Operator) {
      case OR:
         cl1 = condForIdx.GetLast();
         cl2 = condForIdx.GetPrevious();
         cln = new ConditionList;
         if (cl1 != 0 && cl2 != 0) {
            for (c1 = cl1->GetFirst(); c1 != 0; c1 = cl1->DeleteCurrent()) {
               found = PFalse;
               if (c1->left->getAttr()->getParent() != 0 &&
                   c1->right != 0 && c1->right->getAttr()->getParent() != 0)
                  continue;
               else {
                  for (c2 = cl2->GetFirst(); c2 != 0; ) {
                     if (c2->left->getAttr()->getParent() != 0 &&
                         c2->right != 0 && c2->right->getAttr()->getParent() != 0)
                        c2 = cl2->DeleteCurrent();
                     else {
                        if (c1->right && c2->right && //O^O
                            (c1->left->getAttr() == c2->left->getAttr() ||
                             c1->left->getAttr() == c2->right->getAttr() ||
                             c1->right->getAttr() == c2->left->getAttr() ||
                             c1->right->getAttr() == c2->right->getAttr())) {
                           found = PTrue;
                           cln->AddItem (c2);
                           c2 = cl2->DeleteCurrent();
                        } else if (c1->Operator == IS_NULL &&
                                   (c1->left->getAttr() == c2->left->getAttr() ||
                                    (c2->right &&
                                     c1->left->getAttr() == c2->right->getAttr()))) {
                           found = PTrue;
                           cln->AddItem (c2);
                           c2 = cl2->DeleteCurrent();
                        } else if (c2->Operator == IS_NULL &&
                                   (c1->left->getAttr() == c2->left->getAttr() ||
                                    (c1->right &&
                                     c2->left->getAttr() == c1->right->getAttr()))) {
                           found = PTrue;
                           cln->AddItem (c2);
                           c2 = cl2->DeleteCurrent();
                        } else 
                           c2 = cl2->GetNext();
                     }
                  }
                  if (!found)
                     for (c2 = cln->GetFirst(); c2 != 0; c2 = cln->GetNext()) {
                        if (c1->right && c2->right && //O^O
                            (c1->left->getAttr() == c2->left->getAttr() ||
                             c1->left->getAttr() == c2->right->getAttr() ||
                             c1->right->getAttr() == c2->left->getAttr() ||
                             c1->right->getAttr() == c2->right->getAttr())) {
                           found = PTrue;
                           break;
                        } else if (c1->Operator == IS_NULL &&
                                   (c1->left->getAttr() == c2->left->getAttr() ||
                                    (c2->right &&
                                     c1->left->getAttr() == c2->right->getAttr()))) {
                           found = PTrue;
                           break;
                        } else if (c2->Operator == IS_NULL &&
                                   (c1->left->getAttr() == c2->left->getAttr() ||
                                    (c1->right &&
                                     c2->left->getAttr() == c1->right->getAttr()))) {
                           found = PTrue;
                           break;
                        }
                     }
                  if (found)
                     cln->AddItem (c1);
               }
            }
            for (c2 = cl2->GetFirst(); c2 != 0; c2 = cl2->DeleteCurrent())
               ;
            delete cl1;
            delete cl2;
            condForIdx.DeleteCurrent();
            condForIdx.DeleteCurrent();
            condForIdx.AddItem(cln);
         } else                          
            PError::InternalError ("Invalid OR on optimizer");
         break;
      case AND:
         cl1 = condForIdx.GetLast();
         cl2 = condForIdx.GetPrevious();
         if (cl1 != 0 && cl2 != 0) {
            for (c2 = cl2->GetFirst(); c2 != 0; c2 = cl2->GetNext())
                cl1->AddItem (c2);
            delete cl2;
            condForIdx.DeleteCurrent();
         } else                          
            PError::InternalError ("Invalid AND on optimizer");
         break;
      default:
         cln = new ConditionList;
         if (c->left != 0) {
            if (IsValidForIndex (c->Operator)) {
               if (c->left->getAttr()->getParent() != 0)
                  cln->AddItem (c);
               else if (c->right != 0 && c->right->getAttr()->getParent() != 0)
                  cln->AddItem (c);
            }
         }/* else
            PError::InternalError ("Invalid condition on optimizer");*/
         condForIdx.AddItem (cln);
         break;
      }
      
   if (condForIdx.GetItemNum() > 1)
      PError::InternalError ("Optimizer stack Corrupted!");
   l.SetValidCond (cln = condForIdx.GetFirst());
   delete cln;

   cl1 = &(l.GetValidCond());
   if (cl1 != 0) {
      for (c1 = cl1->GetFirst(); c1 != 0; c1 = cl1->GetNext()) {
         san[0] = c1->left;
         san[1] = c1->right;
         for (i = 0; i <= 1; i++) {
            if (san[i]) {
               for (ip = san[i]->getAttr()->GetFirstIndex ();
                    ip != 0;
                    ip = san[i]->getAttr()->GetNextIndex ()) {
                  if (ip->getPart () != 1)
                     continue;
                  StmtRel *sRel = san[i]->GetStmtParent();
                  int part = 2;

                  for (si = indexList.GetFirst(); si != 0;
                       si = indexList.GetNext()) {
                     if (si->getFirstAttr() &&
                         si->getFirstAttr()->GetStmtParent() == sRel &&
                         si->getIndexNum() == ip->getNumber())
                        break;
                  }
                  if (si != 0)
                     continue;

                  ConditionIterator cI (cl1);
                  si = new SearchIndex (ip->getNumber());
                  si->AddAttr (san[i]->getValue());                       
                  do {
                     for ( cI.reset(); *cI != 0; cI++) {
                        if ((*cI)->left->GetStmtParent() == sRel &&
                            (*cI)->left->getAttr()->IsIndex(ip->getNumber(),
                                                                        part)){
                           si->AddAttr ((*cI)->left->getValue());    
                           part++;           
                           break; //
                        } else if ((*cI)->right &&
                                   (*cI)->right->GetStmtParent () == sRel &&
                                   (*cI)->right->getAttr()->IsIndex(
                                                        ip->getNumber(), part)){
                           si->AddAttr ((*cI)->right->getValue());    
                           part++;           
                           break; //
                        }
                     }
                  } while (*cI != 0);
                  indexList.AddItem (si);
               }
            }
         }
      }
   }
   for (si = indexList.GetFirst(); si != 0;
        si = indexList.GetNext())
      si->Build (l, l.GetValidCond());
}

void
Condition::Complement ()
{
   Operator = Invert(Operator);
}

void
Condition::Debug ()
{ 
/*

   StmtAttr *a;
   for (a = Operands.GetFirst (); a != 0; a = Operands.GetNext ())
      a->Debug();
   printf ("Operator = %d\n", Operator); 
*/
}
