///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoCaseCond.cc
// ---------------
// Cego case condition
//     
// 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: CegoCaseCond
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include "CegoCaseCond.h"
#include "CegoQueryHelper.h"
#include "CegoXMLdef.h"

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

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

CegoCaseCond::CegoCaseCond(Element* pCaseElement, CegoDistManager *pGTM)
{
    _elseExpr = 0;
    fromElement(pCaseElement, pGTM);
}

CegoCaseCond::~CegoCaseCond()
{

    CegoPredDesc **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::addPred(CegoPredDesc *pPredDesc, CegoExpr* pExpr)
{
    _predList.Insert(pPredDesc);
    _exprList.Insert(pExpr);
}

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


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

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

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

void CegoCaseCond::setBlock(CegoProcBlock *pBlock)
{
    
    CegoPredDesc **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	(*pPredDesc)->setBlock(pBlock);
	pPredDesc = _predList.Next();
    }
    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	(*pExpr)->setBlock(pBlock);
	pExpr = _exprList.Next();
    }
    if ( _elseExpr )
	_elseExpr->setBlock(pBlock);

    _pBlock = pBlock;
}

void CegoCaseCond::setFieldListArray(ListT<CegoField> *fla, int flaSize)
{
    _fla = fla;
    _flaSize = flaSize;
}

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

    CegoPredDesc **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;
}

int CegoCaseCond::evalReferences(CegoContentObject *pCO, const ListT<CegoField>& fl)
{

    int refCount = 0;

    CegoPredDesc **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;
}

void CegoCaseCond::clearAttrCache()
{

    CegoPredDesc **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();
}

ListT<CegoField> CegoCaseCond::getFieldList() const
{
    ListT<CegoField> fl;

    CegoPredDesc **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	fl += (*pPredDesc)->getFieldList();
	pPredDesc = _predList.Next();
    }

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

    return fl;
}

CegoField CegoCaseCond::evalField() const
{
    CegoField cf = _elseExpr->evalField();
    CegoField f = CegoField( Chain("CASE"), Chain("CASE"), Chain("case when ..."), cf.getType(), cf.getLength());
    return f;
}

CegoFieldValue CegoCaseCond::evalFieldValue() const
{

    CegoQueryHelper queryHelper;

    CegoPredDesc **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();

    while ( pPredDesc && pExpr) 
    {
	if ( queryHelper.evalPredicate(0, 0, 0, _fla, 0, _flaSize, *pPredDesc, _pBlock) )
	{
	    (*pExpr)->setFieldListArray(_fla, _flaSize);
	    return (*pExpr)->evalFieldValue();
	}
	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }
    CegoFieldValue fv;
    
    _elseExpr->setFieldListArray(_fla, _flaSize);
    return _elseExpr->evalFieldValue();
    
}

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

    CegoPredDesc **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;
    _fla = cc._fla;
    _flaSize = cc._flaSize;
    _pBlock = cc._pBlock;
    return (*this);
}
    
Chain CegoCaseCond::toChain() const
{
    Chain s;
    s = Chain("case");

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

    return s;
}

Element* CegoCaseCond::toElement() const
{
    Element* pCaseClauseElement = new Element(XML_CASECLAUSE_ELEMENT);

    CegoPredDesc **pPredDesc = _predList.First();
    CegoExpr **pExpr = _exprList.First();
    while ( pPredDesc && pExpr)
    {

	Element* pCaseElement = new Element(XML_CASE_ELEMENT);
	
	pCaseElement->addContent((*pPredDesc)->toElement());
	pCaseElement->addContent((*pExpr)->toElement());
	
	pCaseClauseElement->addContent(pCaseElement);

	pPredDesc = _predList.Next();
	pExpr = _exprList.Next();
    }

    pCaseClauseElement->addContent(_elseExpr->toElement());
    
    return pCaseClauseElement;
}

void CegoCaseCond::fromElement(Element *pCaseClauseElement, CegoDistManager *pGTM)
{

    CegoPredDesc **pPredDesc = _predList.First();
    while ( pPredDesc )
    {
	delete *pPredDesc;
	pPredDesc = _predList.Next();
    }
    _predList.Empty();

    CegoExpr **pExpr = _exprList.First();
    while ( pExpr) 
    {
	delete *pExpr;
	pExpr = _exprList.Next();
    }
    _exprList.Empty();
    
    if ( _elseExpr )
	delete _elseExpr;
    
    
    ListT<Element*> cl = pCaseClauseElement->getChildren(XML_CASE_ELEMENT);
    Element **pCE = cl.First();
    while ( pCE )
    {
	
	ListT<Element*> pl = (*pCE)->getChildren(XML_PRED_ELEMENT);
	ListT<Element*> el = (*pCE)->getChildren(XML_EXPR_ELEMENT);

	Element **pPE = pl.First();
	Element **pEE = el.First();
	if ( pPE && pEE )
	{
	    addPred( new CegoPredDesc(*pPE, pGTM), new CegoExpr(*pEE, pGTM));
	}

	pCE = cl.Next();
    }

    ListT<Element*> el = pCaseClauseElement->getChildren(XML_EXPR_ELEMENT);
    
    Element **pEE = el.First();
    if ( pEE )
    {
	_elseExpr = new CegoExpr(*pEE, pGTM);
    }

}

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

    

