///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoAttrCond.cc
// ---------------
// Cego database table field implementation
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2010 Bjoern Lemke
//
// 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; see the file COPYING.  If not, write to
// the Free Software Foundation, 59 Temple Place - Suite 330,
// Boston, MA 02111-1307, USA.
//
// IMPLEMENTATION MODULE
//
// Class: CegoAttrCond
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include "CegoAttrCond.h"

#include <stdlib.h>
#include <string.h>

CegoAttrCond::CegoAttrCond()
{
}

CegoAttrCond::~CegoAttrCond()
{
}

void CegoAttrCond::add(const CegoAttrComp& attrComp)
{
    _attrCompSet.Insert(attrComp);
}

int CegoAttrCond::numComp() const
{
    return _attrCompSet.Size();
}

int CegoAttrCond::getStrength() const
{
    CegoAttrComp *pAC = _attrCompSet.First();
    if ( pAC )
    {	
	if ( pAC->getCompMode() == CegoAttrComp::BTWN )
	    return 4;
	switch ( pAC->getComparison() )
	{
	case EQUAL:
	    return 5;
	case NOT_EQUAL:
	    return 1;
	case LESS_THAN:
	    return 3;
	case MORE_THAN:
	    return 3;
	case LESS_EQUAL_THAN:
	    return 2;
	case MORE_EQUAL_THAN:
	    return 2;
	}
    }
    return 0;
}

SetT<CegoAttrComp>& CegoAttrCond::getAttrCompSet()
{
    return _attrCompSet;
}

CegoAttrCond CegoAttrCond::getFilterCond(const ListT<CegoField>& fl) const
{
    CegoAttrCond ac;

    CegoField *pF = fl.First();
    while ( pF)
    {	
	bool attrFound=false;
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{
	    if ( pF->getAttrName() == pAC->getAttrName() )
	    {
		ac.add(*pAC);
	    }
	    pAC = _attrCompSet.Next();
	}

	pF = fl.Next();
    }
    return ac;
}

CegoAttrCond CegoAttrCond::getIndexCond(const ListT<CegoField>& fl) const
{
    CegoAttrCond ac;

    bool isPrimary=true;
    bool goOn=true;
    CegoField *pF = fl.First();
    while ( pF && goOn )
    {	
	bool attrFound=false;
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{
	    
	    if ( pF->getAttrName() == pAC->getAttrName() )
	    {
		if ( isPrimary || pAC->getComparison() == EQUAL )
		{
		    ac.add(*pAC);
		    attrFound=true;
		}
	    }
	    pAC = _attrCompSet.Next();
	}

	isPrimary = false;

	if ( attrFound )
	    pF = fl.Next();
	else
	    goOn=false;
	    
    }
    return ac;
}


bool CegoAttrCond::setup(const ListT<CegoField>& fl)
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {
	if ( pAC->getCompMode() == CegoAttrComp::ATTR )
	    if ( pAC->setup(fl) == false )
		return false;
	pAC = _attrCompSet.Next();
    }
    return true;
}

bool CegoAttrCond::setup(ListT<CegoField>* joinBuf, int offset, int size)
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {
	if ( pAC->getCompMode() == CegoAttrComp::ATTR )
	    if ( pAC->setup(joinBuf, offset, size) == false )
		return false;
	pAC = _attrCompSet.Next();
    }
    return true;
}

void CegoAttrCond::asConjunctionList(const ListT<CegoExpr*>& exprList, ListT<CegoPredDesc*>& conjunctionList) const
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {

	CegoPredDesc *pP;

	CegoExpr *pExpr = getExpressionForAlias(exprList, pAC->getAttrName() );

	if ( pExpr == 0 )
	{
	    pExpr = new CegoExpr(new CegoTerm(new CegoFactor( new CegoAttrDesc(pAC->getAttrName()))));
	}

	if ( pAC->getCompMode() == CegoAttrComp::ATTR && pAC->isSetup() == false )   
	{	   
	    pP = new CegoPredDesc(pExpr,
				  new CegoExpr(new CegoTerm(new CegoFactor( pAC->getAttrDesc().clone()))),
				  pAC->getComparison());
	}
	else if ( pAC->getCompMode() == CegoAttrComp::VAL || 
		  ( pAC->getCompMode() == CegoAttrComp::ATTR && pAC->isSetup() ) )
	{
	    pP = new CegoPredDesc(pExpr,
				  new CegoExpr(new CegoTerm(new CegoFactor( pAC->getFieldValue()))),
				  pAC->getComparison());
	}
	else if ( pAC->getCompMode() == CegoAttrComp::BTWN )
	{
	    pP = new CegoPredDesc(pExpr,
				  new CegoExpr(new CegoTerm(new CegoFactor( pAC->getFieldValue()))),
				  new CegoExpr(new CegoTerm(new CegoFactor( pAC->getFieldValue2()))));
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISLIKE )
	{
	    pP = new CegoPredDesc(pExpr,
				  pAC->getPattern(),
				  false);
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNOTLIKE )
	{
	    pP = new CegoPredDesc(pExpr,
				  pAC->getPattern(),
				  true);
	}


       
	conjunctionList.Insert(pP);
	
	pAC = _attrCompSet.Next();
    }	
}

CegoExpr* CegoAttrCond::getExpressionForAlias(const ListT<CegoExpr*>& exprList, const Chain& alias) const
{
    CegoExpr **pExpr = exprList.First();
    while ( pExpr )
    {
	if ( (*pExpr)->getAlias() == alias )
	    return (*pExpr)->clone();
	pExpr = exprList.Next();
    }
    return 0;
}

CegoAttrCond::IndexMatch CegoAttrCond::checkIndex(const ListT<CegoField>& schema) const
{

    int numFound=0;

    bool isPrimary = true;
    bool hasLeak=false;
    CegoField *pF = schema.First();


    while ( pF  && hasLeak == false )
    {
	bool isFound=false;	
	CegoAttrComp *pComp = _attrCompSet.First();	
	while ( pComp ) // isFound == false )
	{	    
	    if ( pComp->getAttrName() == pF->getAttrName() 
		 && ( pComp->getCompMode() == CegoAttrComp::VAL 
		      || pComp->getCompMode() == CegoAttrComp::ATTR
		      || pComp->getCompMode() == CegoAttrComp::BTWN )
		 && ( isPrimary || pComp->getComparison() == EQUAL  ) )
	    {

		numFound++;
		isFound=true;
		pComp = _attrCompSet.Next();
	    }
	    else
	    {
		pComp = _attrCompSet.Next();
	    }
	}
	
	isPrimary = false;
	
	if ( isFound == true )
	    hasLeak=false;
	else
	    hasLeak=true;

	pF = schema.Next();
    }

    if ( numFound == _attrCompSet.Size() )
	return FULL;
    
    if ( numFound < _attrCompSet.Size() && numFound > 0 )
	return PART;
    
    return INAPP;

}
    
void CegoAttrCond::setIdxSchema(ListT<CegoField>& schema)
{
    _idxSchema = schema;
}

void CegoAttrCond::setPrimaryComparison(CegoComparison comp)
{
    CegoAttrComp* pAC = _attrCompSet.First();
    if ( pAC )
    {
	pAC->setComparison(comp); 
    }
    else
    {
	throw Exception(EXLOC, "Cannot set primary comparison");
    }
}

CegoAttrComp::CompMode CegoAttrCond::getPrimaryCompMode() const
{
    CegoAttrComp* pAC = _attrCompSet.First();
    if ( pAC )
    {
	return pAC->getCompMode();
    }
    else
    {
	throw Exception(EXLOC, "Cannot get primary comp mode");
    }
    
}

CegoComparison CegoAttrCond::getPrimaryComparison() const
{
    CegoAttrComp* pAC = _attrCompSet.First();
    if ( pAC )
    {
	return pAC->getComparison();
    }
    else
    {
	throw Exception(EXLOC, "Cannot get primary comparison");
    }
}

bool CegoAttrCond::diff( const CegoAttrCond& ac) const
{
    
    if ( ac._attrCompSet.Size() != _attrCompSet.Size() )
	return false;

    CegoAttrComp* pAC = ac._attrCompSet.First();
    while ( pAC  )
    {
	CegoAttrComp* pAC2 = _attrCompSet.Find(*pAC);
	if ( pAC2  )
	{
	    if ( pAC->getFieldValue() != pAC2->getFieldValue() ) 
		return false;
	}
	else
	{
	    return false;
	}
	pAC = ac._attrCompSet.Next();
    }

    pAC = _attrCompSet.First();
    while ( pAC  )
    {
	CegoAttrComp* pAC2 = ac._attrCompSet.Find(*pAC);
	if ( pAC2  )
	{
	    if ( pAC->getFieldValue() != pAC2->getFieldValue() ) 
		return false;
	}
	else
	{
	    return false;
	}
	pAC = _attrCompSet.Next();
    }

    return true;
    
}

CegoAttrCond& CegoAttrCond::operator = ( const CegoAttrCond& ac)
{
    _idxSchema = ac._idxSchema;
    _attrCompSet = ac._attrCompSet;
    return (*this);
}

bool CegoAttrCond::operator == ( const CegoAttrCond& ac) const
{
    
    if ( ac._attrCompSet.Size() != _attrCompSet.Size() )
	return false;

    CegoAttrComp* pAC = ac._attrCompSet.First();
    while ( pAC  )
    {
	if ( _attrCompSet.Find(*pAC) == false )
	    return false;
	pAC = ac._attrCompSet.Next();
    }

    pAC = _attrCompSet.First();
    while ( pAC  )
    {
	if ( ac._attrCompSet.Find(*pAC) == false )
	    return false;	
	pAC = _attrCompSet.Next();
    }

    return true;
    
}

bool CegoAttrCond::operator != ( const CegoAttrCond& ac) const
{
    if ( *this == ac )
	return false;
    return true;
}


CegoAttrCond operator + ( const CegoAttrCond& ac1, const CegoAttrCond& ac2 )
{

    CegoAttrCond ac;
    CegoAttrComp *pComp;
    pComp = ac1._attrCompSet.First();    
    while ( pComp )
    {
	ac.add(*pComp);
	pComp = ac1._attrCompSet.Next();
    }
    pComp = ac2._attrCompSet.First();    
    while ( pComp )
    {
	ac.add(*pComp);
	pComp = ac2._attrCompSet.Next();
    }
    return ac;
}

Chain CegoAttrCond::toChain() const
{
    Chain s;

    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {	
	s += pAC->toChain();
	pAC = _attrCompSet.Next();
	if ( pAC )
	    s += Chain(" and ");
    }
    return s;

}

ostream& operator << (ostream& s, const CegoAttrCond& ac)
{
    s << ac.toChain();
    return s;
}

    

