///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoAttrCond.cc
// ---------------
// Cego database table field implementation
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoAttrCond
//
// Description: Attribute condition description used for index cursors
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// CEGO INCLUDES
#include "CegoAttrCond.h"

// POSIX INCLUDES
#include <stdlib.h>
#include <string.h>

CegoAttrCond::CegoAttrCond()
{
    _isAnd=true;
}

CegoAttrCond::~CegoAttrCond()
{
}

Chain CegoAttrCond::getId() const
{
    Chain s;
    
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {	
	s += pAC->getId();
	pAC = _attrCompSet.Next();
	if ( pAC )
	    s += Chain("&");
    }
    return s;
}

void CegoAttrCond::add(const CegoAttrComp& attrComp)
{
    CegoAttrComp *pAC;
    if ( _isAnd )
    {
	if ( (pAC = _attrCompSet.Find(attrComp)) == 0 )
	{	
	    _attrCompSet.Insert(attrComp);
	}
    }
    else
    {
	_attrCompSet.Insert(attrComp);
    }
}

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

unsigned 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;
}

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

void CegoAttrCond::clean()
{
    _attrCompSet.Empty();
}

bool CegoAttrCond::hasIndexCandidate()
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {
	if ( pAC->getCompMode() == CegoAttrComp::VAL
	     || pAC->getCompMode() == CegoAttrComp::ATTR
	     || pAC->getCompMode() == CegoAttrComp::BTWN )
	    return true;
	
	pAC = _attrCompSet.Next();
    }
    return false;
}

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

    ac.setAnd(_isAnd);
    
    CegoField *pF = fl.First();
    while ( pF)
    {	
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{
	    if ( pF->getTableAlias() == pAC->getTableName() && pF->getAttrName() == pAC->getAttrName() )
	    {
		if ( ! ( ignoreNullComp && pAC->isNullComp() ) )
		    ac.add(*pAC);
		/* else ignore comp */
	    }
	    pAC = _attrCompSet.Next();
	}

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

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

    ac.setAnd(_isAnd);
    
    unsigned pos = 0;
    CegoField *pF = fl.First();
 
    while ( pF  )
    {
        CegoAttrComp *pAC = _attrCompSet.First();
        while ( pAC )
        {
            if ( pF->getAttrName() == pAC->getAttrName()
                 && ( pAC->getCompMode() == CegoAttrComp::VAL
                      || pAC->getCompMode() == CegoAttrComp::ATTR
                      || pAC->getCompMode() == CegoAttrComp::BTWN ) )
            {
                pAC->setPos(pos);
                ac.add(*pAC);
            }
            pAC = _attrCompSet.Next();
        }
        pF = fl.Next();
        pos++;
    }
    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>** pJoinBuf, unsigned offset)
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {
	pAC->reset();
	if ( pAC->getCompMode() == CegoAttrComp::ATTR || pAC->getCompMode() == CegoAttrComp::BTWN )
	{	  	    
	    if ( pAC->setup(pJoinBuf, offset) == false )
	    {
		return false;
	    }	   
	}
	pAC = _attrCompSet.Next();
    }
    return true;
}

bool CegoAttrCond::setup(ListT<CegoField>** pParentJoinBuf, unsigned parentOffset, ListT<CegoField>** pJoinBuf, unsigned offset)
{
    CegoAttrComp *pAC = _attrCompSet.First();
    while ( pAC )
    {
	pAC->reset();
	if ( pAC->getCompMode() == CegoAttrComp::ATTR || pAC->getCompMode() == CegoAttrComp::BTWN )
	{
	    if ( pAC->isParentSetup() )
	    {
		if ( pAC->setup(pParentJoinBuf, parentOffset) == false )
		{
		    return false;
		}
	    }
	    else
	    {
		if ( pAC->setup(pJoinBuf, offset) == false )
		{
		    if ( pParentJoinBuf )
		    {
			if ( pAC->setup(pParentJoinBuf, parentOffset) == false )
			{
			    return false;
			}
			else
			{
			    pAC->setParentSetup();	    
			}
		    }
		    else
		    {
			return false;
		    }
		}
	    }
	}	
	pAC = _attrCompSet.Next();
    }
    return true;
}

/*
  asConjunctionList returns for the given instance a list of predicates, which are needed for 
  CegoSelect to build up joins.
  For this, each comparison is checked
  From cego serion 2.52.23 and up, _isAnd flag is treated. If set to false, this is a disjunction and we have to return false
 */

bool CegoAttrCond::asConjunctionList(const ListT<CegoExpr*>& exprList, ListT<CegoPredicate*>& conjunctionList) const
{
    if ( _isAnd == true )
    {	
	bool isComplete = true;
		
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{
	    CegoPredicate *pPred = getPred4Comp(exprList, pAC);

	    if ( pPred )		
		conjunctionList.Insert(pPred);
	    else
		isComplete=false;
	    
	    pAC = _attrCompSet.Next();
	}
	
	return isComplete;
    }
    else
    {

	/* in case of _isAnd = false, we expect a straight list of compares for the same attribute
	   as constructed for CegoPredicate::CompMode = IN
	   So we construct in a reverse way the corresponding predicate for the view
	 */
	
	CegoPredicate* pPred;
	CegoExpr *pExpr = 0;
	ListT<CegoExpr*> inExprList;
	
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{
	    if ( pExpr == 0 )
	    {
		pExpr = getExpressionForAlias(exprList, pAC->getAttrName());		
		if ( pExpr == 0 ) 
		    pExpr = new CegoExpr(new CegoTerm(new CegoFactor( new CegoAttrDesc(pAC->getAttrName()))));
	    }

	    CegoExpr* pInExpr =  new CegoExpr(new CegoTerm(new CegoFactor( pAC->getFieldValue())));
	    inExprList.Insert(pInExpr);

	    pAC = _attrCompSet.Next();
	}

	pPred = new CegoPredicate(pExpr, inExprList, false);

	conjunctionList.Insert(pPred);
	
	return true;
    }
}


CegoPredicate* CegoAttrCond::getPred4Comp(const ListT<CegoExpr*> exprList, CegoAttrComp* pAC) const
{
    
    // Get an expression clone for the view condition attribute alias 
    CegoExpr *pExpr = getExpressionForAlias(exprList, pAC->getAttrName() );
    
    if ( pExpr == 0 )
    {
	// if there is no alias, we assume this a an outer attribute reference from the view caller
	pExpr = new CegoExpr(new CegoTerm(new CegoFactor( new CegoAttrDesc(pAC->getAttrName()))));
    }
    
    // we have to check for an aggregation in expression
    // if so we have to skip this compare and return complete = false
    if ( pExpr->getAggregationList().isEmpty() == false )
    {
	return 0;
	// isComplete = false;
    }
    else
    {
	
	// Depending on the attribute comparison mode, an appropriate predicate is
	// build up
	CegoPredicate *pP = 0;	    
	
	if ( pAC->getCompMode() == CegoAttrComp::ATTR && pAC->isSetup() == false )   
	{
	    CegoExpr* pExternExpr = new CegoExpr(new CegoTerm(new CegoFactor( pAC->getAttrDesc().clone())));
	    pP = new CegoPredicate(pExpr,
				   pExternExpr,
				   pAC->getComparison());		
	}
	else if ( pAC->getCompMode() == CegoAttrComp::VAL || 
		  ( pAC->getCompMode() == CegoAttrComp::ATTR && pAC->isSetup() ) )
	{
	    pP = new CegoPredicate(pExpr,
				   new CegoExpr(new CegoTerm(new CegoFactor( pAC->getFieldValue()))),
				   pAC->getComparison());
	}
	else if ( pAC->getCompMode() == CegoAttrComp::BTWN )
	{
	    pP = new CegoPredicate(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 CegoPredicate(pExpr,
				   pAC->getPattern(),
				   false, true);
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNOTLIKE )
	{
	    pP = new CegoPredicate(pExpr,
				   pAC->getPattern(),
				   true, true);
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNCLIKE )
	{
	    pP = new CegoPredicate(pExpr,
				   pAC->getPattern(),
				   false, false);
	}
	else if ( pAC->getCompMode() == CegoAttrComp::ISNOTNCLIKE )
	{
	    pP = new CegoPredicate(pExpr,
				   pAC->getPattern(),
				   true, false);
	}

	return pP;
    }
}	
	
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
{
    unsigned numFound=0;

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

    while ( pF && hasLeak == false )
    {
	bool isFound=false;	
	CegoAttrComp *pComp = _attrCompSet.First();	
	while ( pComp ) 
	{
	    if ( pComp->getAttrName() == pF->getAttrName() 
		 && ( pComp->getCompMode() == CegoAttrComp::VAL 
		      || pComp->getCompMode() == CegoAttrComp::ATTR
		      || pComp->getCompMode() == CegoAttrComp::BTWN ) )
	    {		
		numFound++;
		isFound=true;
		pComp = _attrCompSet.Next();
	    }
	    else
	    {		
		pComp = _attrCompSet.Next();
	    }
	}
	
	if ( isFound == true )
	    hasLeak=false;
	else
	    hasLeak=true;

	pF = schema.Next();
    }

    if ( numFound == (unsigned)_attrCompSet.Size() )
	return FULL;

    if ( numFound < (unsigned)_attrCompSet.Size() && numFound > 0 )
	return PART;
    
    return INAPP;
}

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 = findComp(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.findComp(pAC);
	if ( pAC2  )
	{
	    if ( pAC->getFieldValue() != pAC2->getFieldValue() ) 
		return false;
	}
	else
	{
	    return false;
	}
	pAC = _attrCompSet.Next();
    }
    return true;   
}

CegoAttrComp* CegoAttrCond::findComp(CegoAttrComp* pAttrComp) const
{
    // for searching attribute comparison, we use a dedicated loop, since for TreeT, just < and > operators are used.
    // for comparison we have to use the = operator
    
    CegoAttrComp* pAC = _attrCompSet.First();
    while ( pAC )
    {
	if ( *pAC == *pAttrComp )
	    return pAC;
	pAC = _attrCompSet.Next();
    }
    return 0;
}

void CegoAttrCond::update( const CegoAttrCond& ac )
{    
    CegoAttrComp* pAC = _attrCompSet.First();
    while ( pAC  )
    {
	CegoAttrComp* pAC2 = ac.findComp(pAC);
	if ( pAC2  )
	{
	    pAC->setFieldValue(pAC2->getFieldValue());
	    if ( pAC->getCompMode() == CegoAttrComp::BTWN )
		pAC->setFieldValue2(pAC2->getFieldValue2());
	}
	else
	{
	    throw Exception(EXLOC, Chain("Cannot set up diff for attribute condition"));
	}
	pAC = _attrCompSet.Next();
    }
}

void CegoAttrCond::setAnd(bool isAnd)
{
    _isAnd = isAnd;
}

bool CegoAttrCond::isAnd() const
{
    return _isAnd;
}
	
ListT<CegoAttrCond> CegoAttrCond::getAndCondList() const
{
    ListT<CegoAttrCond> condList;

    if ( _isAnd == false )
    {		
	CegoAttrComp *pAC = _attrCompSet.First();
	while ( pAC )
	{	
	    CegoAttrCond ac;
	    ac.add(*pAC);
	    condList.Insert(ac);
	    pAC = _attrCompSet.Next();
	}
	return condList;
    }
    else
    {
	throw Exception(EXLOC, Chain("Condition list is already and list"));
    }
}

CegoAttrCond& CegoAttrCond::operator = ( const CegoAttrCond& ac)
{
    // _idxSchema = ac._idxSchema;
    _attrCompSet = ac._attrCompSet;
    _isAnd = ac._isAnd;
    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 ( findComp(pAC) == 0 )
	    return false;
	pAC = ac._attrCompSet.Next();
    }

    pAC = _attrCompSet.First();
    while ( pAC  )
    {
	if ( ac.findComp(pAC) == 0 )
	    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 )
	{
	    if ( _isAnd )
		s += Chain(" and ");
	    else
		s += Chain(" or ");
	}
    }
    return s;
}

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