///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoCaseCond.cc
// ---------------
// Cego case condition
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoCaseCond
// 
// Description: Query case condition container class
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// CEGO INCLUDES
#include "CegoCaseCond.h"
#include "CegoQueryHelper.h"
#include "CegoXMLdef.h"
#include "CegoDatabaseFormater.h"
#include "CegoTypeConverter.h"

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

CegoCaseCond::CegoCaseCond()
{
    _elseExpr = 0;
}

CegoCaseCond::CegoCaseCond(char* buf)
{
    _elseExpr = 0;
    decode(buf);
}

CegoCaseCond::~CegoCaseCond()
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	delete *pPredDesc;
	pPredDesc = _predList.Next();
    }
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	delete *pExpr;
	pExpr = _exprList.Next();
    }
    
    if ( _elseExpr )
	delete _elseExpr;
}

void CegoCaseCond::cleanUp()
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->cleanUp();
	pPredDesc = _predList.Next();
    }
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->cleanUp();
	pExpr = _exprList.Next();
    }
    
    if ( _elseExpr )
	_elseExpr->cleanUp();
}

void CegoCaseCond::setQueryManager(CegoQueryManager* pTabMng)
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->setQueryManager(pTabMng);
	pPredDesc = _predList.Next();
    }
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->setQueryManager(pTabMng);
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	_elseExpr->setQueryManager(pTabMng);
}

void CegoCaseCond::setTabSetId(unsigned tabSetId)
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->setTabSetId(tabSetId);
	pPredDesc = _predList.Next();
    }
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->setTabSetId(tabSetId);
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	_elseExpr->setTabSetId(tabSetId);
}

void CegoCaseCond::addPred(CegoPredicate *pPredDesc, CegoExpr* pExpr)
{
    _predList.Insert(pPredDesc);
    _exprList.Insert(pExpr);
}

void CegoCaseCond::setElseExpr(CegoExpr *pExpr)
{
    _elseExpr = pExpr;
}

ListT<CegoPredicate*>& CegoCaseCond::getPredList()
{
    return _predList;
}

ListT<CegoExpr*>& CegoCaseCond::getExprList()
{
    return _exprList;
}

CegoExpr* CegoCaseCond::getElseExpr()
{
    return _elseExpr;
}

ListT<CegoAttrDesc*> CegoCaseCond::getAttrRefList() const
{
    ListT<CegoAttrDesc*> attrList;

    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	attrList += (*pPredDesc)->getAttrRefList();
	pPredDesc = _predList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	attrList += (*pExpr)->getAttrRefList();
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	attrList += _elseExpr->getAttrRefList();

    return attrList;
}

unsigned CegoCaseCond::evalReferences(CegoContentObject *pCO, const ListT<CegoField>& fl)
{
    unsigned refCount = 0;

    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	refCount += (*pPredDesc)->evalReferences(pCO, fl);
	pPredDesc = _predList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	refCount += (*pExpr)->evalReferences(pCO, fl);
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	refCount+= _elseExpr->evalReferences(pCO, fl);
    
    return refCount;
}

ListT<CegoAggregation*> CegoCaseCond::getAggregationList()
{
    ListT<CegoAggregation*> aggList;	

    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	aggList = aggList + (*pPredDesc)->getAggregationList();
	pPredDesc = _predList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr ) 
    {
	aggList = aggList + (*pExpr)->getAggregationList();		    
	pExpr = _exprList.Next();
    }
    
    if ( _elseExpr )
	aggList = aggList + _elseExpr->getAggregationList();

    return aggList;
}


void CegoCaseCond::clearAttrCache()
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->clearAttrCache();
	pPredDesc = _predList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->clearAttrCache();
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	_elseExpr->clearAttrCache();
}

void CegoCaseCond::getFieldList(ListT<CegoField>& fl, ListT<CegoField>** pFLA) const
{
    CegoPredicate **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->getFieldList(fl, pFLA);
	pPredDesc = _predList.Next();
    }

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->getFieldList(fl, pFLA);
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	_elseExpr->getFieldList(fl, pFLA);
}

CegoField CegoCaseCond::evalField(const ListT<CegoField>& fl, bool graceful) const
{
    ListT<CegoField> cfList;
    CegoExpr **pExpr = _exprList.First();
    
    CegoField elsecf = _elseExpr->evalField(fl);

    CegoField refcf = elsecf;
    while ( pExpr)
    {
	CegoField cf = (*pExpr)->evalField(fl);
	cfList.Insert(cf);
	
	if ( refcf.getType() == NULL_TYPE && cf.getType() != NULL_TYPE )
	    refcf = cf;
	pExpr = _exprList.Next();
    }

    // type match is required for all cases

    if ( graceful == false )
    {
	CegoField* pF = cfList.First();
	while ( pF )
	{
	    if ( pF->getType() != NULL_TYPE && elsecf.getType() != NULL_TYPE
		 && ( pF->getType() != elsecf.getType() || pF->getDim() != elsecf.getDim() ) )
	    {
		Chain msg = Chain("Case condition type mismatch for type ")
		    + CegoTypeConverter::getTypeString( pF->getType()) + Chain("(") + Chain(pF->getDim()) + Chain(")")
		    + Chain(" != ")
		    + CegoTypeConverter::getTypeString( elsecf.getType()) + Chain("(") + Chain(elsecf.getDim()) + Chain(")");
		    
		throw Exception(EXLOC, msg);
	    }
	    pF = cfList.Next();
	}
    }

    CegoField f = CegoField( Chain("CASE"), Chain("CASE"), Chain("case when ..."), refcf.getType(), refcf.getLength(), refcf.getDim());
    return f;
}

CegoFieldValue CegoCaseCond::evalFieldValue(ListT<CegoField> **pFLA, CegoProcBlock *pBlock) const
{    
    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();

    while ( pPredDesc && pExpr) 
    {
	if ( (*pPredDesc)->eval(0, 0, pFLA, 0, pBlock) )
	{
	    return (*pExpr)->evalFieldValue(pFLA, pBlock);
	}
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    CegoFieldValue fv;
   
    return _elseExpr->evalFieldValue(pFLA, pBlock);   
}

CegoCaseCond* CegoCaseCond::clone(bool isAttrRef)
{
    CegoCaseCond *pCaseCond = new CegoCaseCond();

    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	pCaseCond->addPred((*pPredDesc)->clone(isAttrRef), (*pExpr)->clone(isAttrRef));
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    pCaseCond->setElseExpr(_elseExpr->clone(isAttrRef));
    
    return pCaseCond;
}

CegoCaseCond& CegoCaseCond::operator = ( const CegoCaseCond& cc)
{
    _predList = cc._predList;
    _exprList = cc._exprList;
    _elseExpr = cc._elseExpr;
    return (*this);
}

void CegoCaseCond::getSelectQueryList(ListT<CegoSelect*>& queryList)
{
    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	(*pPredDesc)->getSelectQueryList(queryList);
	(*pExpr)->getSelectQueryList(queryList);
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }    
    _elseExpr->getSelectQueryList(queryList);
}

Chain CegoCaseCond::getId(CegoProcBlock *pBlock) const
{
    Chain s;
    s = Chain("case");

    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	s += Chain("w") + (*pPredDesc)->getId(pBlock) + Chain("t") + (*pExpr)->getId(pBlock);
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    s += Chain("e") + _elseExpr->getId(pBlock);

    return s;
}

Chain CegoCaseCond::toChain(unsigned defTabSetId) const
{
    Chain s;
    s = Chain("case");

    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	s += Chain(" when ") + (*pPredDesc)->toChain(defTabSetId) + Chain(" then ") + (*pExpr)->toChain(defTabSetId);
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    s += Chain(" else ") + _elseExpr->toChain(defTabSetId);
    s += Chain(" end ");

    return s;
}

Chain CegoCaseCond::dbFormat(CegoDatabaseFormater *pForm) const
{
    return pForm->formatCaseCond(_predList, _exprList, _elseExpr);
}

void CegoCaseCond::encode(char *buf, CegoProcBlock* pBlock)
{
    char* pE = (char*)buf;

    unsigned numPred = _predList.Size();
    memcpy( pE, &numPred, sizeof(unsigned));
    pE = pE + sizeof(unsigned);

    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	(*pPredDesc)->encode(pE, pBlock);
	pE = pE + (*pPredDesc)->getEncodingLength(pBlock);
	
	(*pExpr)->encode(pE, pBlock);
	pE = pE + (*pExpr)->getEncodingLength(pBlock);

	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    _elseExpr->encode(pE, pBlock);    
}

void CegoCaseCond::decode(char *buf)
{
    char* pE = (char*)buf;

    unsigned numPred;
    memcpy( &numPred, pE, sizeof(unsigned));
    pE = pE + sizeof(unsigned);

    unsigned i=0;
    while ( i < numPred )
    {
	CegoPredicate *pPred = new CegoPredicate(pE);
	pE = pE + pPred->getEncodingLength();
	_predList.Insert(pPred);
	
	CegoExpr *pExpr = new CegoExpr(pE);
	pE = pE + pExpr->getEncodingLength();
	_exprList.Insert(pExpr);
	i++;
    }
    
    _elseExpr = new CegoExpr(pE);
}

unsigned CegoCaseCond::getEncodingLength(CegoProcBlock* pBlock) const
{
    unsigned len = sizeof(unsigned);
    
    CegoPredicate **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {
	len += (*pPredDesc)->getEncodingLength(pBlock);
	len += (*pExpr)->getEncodingLength(pBlock);

	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    len += _elseExpr->getEncodingLength(pBlock);
    return len;
}

void CegoCaseCond::getPlanList(ListT<Element*>& planList)
{
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr)
    {
	(*pExpr)->getPlanList(planList);
	pExpr = _exprList.Next();
    }
    _elseExpr->getPlanList(planList);
}

ostream& operator << (ostream& s, const CegoCaseCond& cc)
{
    s << cc.toChain(0);
    return s;
}

CegoCaseCond* CegoCaseCond::map(const Chain& viewAlias, const ListT<CegoExpr*>& exprList) const
{

    bool doMap=true;

    
    CegoCaseCond *pCaseCond = new CegoCaseCond();
    
    CegoPredicate **pPred = _predList.First();
    CegoExpr **pExpr = _exprList.First();

    while ( pPred && pExpr && doMap)
    {	
	CegoExpr* pMapExpr = (*pExpr)->map(viewAlias, exprList);
	if ( pMapExpr == 0 )
	    doMap = false;

	if ( doMap )
	{
	    CegoPredicate* pMapPred = (*pPred)->map(viewAlias, exprList);
	    if ( pMapPred == 0 )
		doMap = false;

	    if ( doMap )
	    {
		pCaseCond->addPred(pMapPred, pMapExpr);	    
	    }
	}

	pPred = _predList.Next();
	pExpr = _exprList.Next();

    }

    if ( doMap )
    {
    	CegoExpr* pMapElse = _elseExpr->map(viewAlias, exprList);
	if ( pMapElse == 0)
	    doMap = false;
	if ( doMap )
	    pCaseCond->setElseExpr(pMapElse);	    
    }
    
    if ( doMap )
    {	
	return pCaseCond;
    }
    else
    {
	delete pCaseCond;	
	return 0;
    }
}
