///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoExpr.cc
// -----------
// Cego expression class 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: CegoExpr
// 
// Description:
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include "CegoExpr.h"

#include "CegoXMLdef.h"

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

CegoExpr::CegoExpr(char* buf, CegoDistManager *pGTM)
{
    _pTerm=0;
    _pExpr=0;
    decode(buf, pGTM);
}

CegoExpr::CegoExpr()
{
    _pTerm=0;
    _pExpr=0;
}

CegoExpr::CegoExpr(Element* pExpElement, CegoDistManager *pGTM)
{
    _pTerm=0;
    _pExpr=0;
    fromElement(pExpElement, pGTM);
}

CegoExpr::CegoExpr(CegoExpr *pExpr, CegoTerm *pTerm, ExpType expType)
{
    _expType = expType;
    _pExpr=pExpr;
    _pTerm=pTerm;
    _alias = Chain();
}

CegoExpr::CegoExpr(CegoTerm *pTerm)
{
    _expType = TERM;
    _pTerm=pTerm;
    _pExpr = 0;
}

CegoExpr::~CegoExpr()
{
    if ( _pExpr )
	delete _pExpr;
    if ( _pTerm )
	delete _pTerm;
}

void CegoExpr::setAlias(const Chain& alias)
{
    _alias = alias;
}

const Chain& CegoExpr::getAlias() const 
{
    return _alias;
}

bool CegoExpr::getTableDesc(CegoTableDesc& td) const
{
    if ( _pExpr )
	return false;
    if ( _pTerm )
	return _pTerm->getTableDesc(td);
    return false;
}

bool CegoExpr::checkVar(Chain& var) const
{
    if ( _pTerm )
    {
	return _pTerm->checkVar(var);
    }
    return false;
}

CegoAttrDesc* CegoExpr::checkAttr() const
{
    if ( _pTerm && _expType == TERM)
    {
	return _pTerm->checkAttr();
    }
    return 0;
}

void CegoExpr::setFieldListArray(ListT<CegoField> *fla, int size)
{
    if ( _pExpr )
	_pExpr->setFieldListArray(fla, size);
    if ( _pTerm )
	_pTerm->setFieldListArray(fla, size);
}

void CegoExpr::setBlock(CegoProcBlock *pBlock)
{
    if ( _pTerm )
    {
	_pTerm->setBlock(pBlock);
    }
    if ( _pExpr )
    {
	_pExpr->setBlock(pBlock);
    }
}

void CegoExpr::getSelectQueryList(ListT<CegoSelect*>& queryList)
{
    if ( _pTerm )
    {
	_pTerm->getSelectQueryList(queryList);
    }
    if ( _pExpr )
    {
	_pExpr->getSelectQueryList(queryList);
    }
}


void CegoExpr::clearAttrCache()
{
    if ( _pTerm )
    {
	_pTerm->clearAttrCache();
    }
    if ( _pExpr )
    {
	_pExpr->clearAttrCache();
    }
}

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

    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	fl = _pExpr->getFieldList() + _pTerm->getFieldList();
	break;
    }
    case TERM:
    {
	fl = _pTerm->getFieldList();
	break;
    }
    }
    return fl;
}

ListT<CegoAttrDesc*> CegoExpr::getAttrRefList() const
{
    ListT<CegoAttrDesc*> attrList;
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	attrList = _pExpr->getAttrRefList() + _pTerm->getAttrRefList();
	break;
    }
    case TERM:
    {
	attrList = _pTerm->getAttrRefList();
	break;
    }
    }
    return attrList;
}

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

    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	refCount += _pExpr->evalReferences(pCO, fl);
	refCount += _pTerm->evalReferences(pCO, fl);
	break;
    }
    case TERM:
    {
	refCount+= _pTerm->evalReferences(pCO, fl);
	break;
    }
    }

    return refCount;
}

CegoField CegoExpr::evalField() const
{
    CegoField f;

    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
    {
	f = CegoField( Chain("EXPR"), Chain("EXPR"), toChain(), VARCHAR_TYPE, 20 );
	break;
    }
    case TERM:
    {
	f = _pTerm->evalField();
	break;
    }
    }
    
    if ( _alias != Chain() )
    {	
	f.setAttrName(_alias);
    }
    return f;
	    
}

CegoFieldValue CegoExpr::evalFieldValue() const
{
    switch ( _expType )
    {
    case ADD:
    {
	return ( _pExpr->evalFieldValue() + _pTerm->evalFieldValue() );	
    }
    case SUB:
    {
	return ( _pExpr->evalFieldValue() - _pTerm->evalFieldValue() );
    }
    case CONCAT:
    {	
	Chain con = _pExpr->evalFieldValue().valAsChain() + _pTerm->evalFieldValue().valAsChain();
	CegoFieldValue  fv(VARCHAR_TYPE, con );
	return fv;
    }
    case TERM:
    {
	return ( _pTerm->evalFieldValue() );
    }
    }
}

CegoExpr::ExpType CegoExpr::getType() const
{
    return _expType;
}

CegoTerm *CegoExpr::getTerm() const
{
    return _pTerm;
}

CegoExpr *CegoExpr::getExpr() const
{
    return _pExpr;
}

ListT<CegoAggregation*> CegoExpr::getAggregationList()
{
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
	return ( _pExpr->getAggregationList() + _pTerm->getAggregationList() );	
    case TERM:
	return ( _pTerm->getAggregationList() );
    }
}

CegoExpr* CegoExpr::clone(bool isAttrRef)
{
    switch ( _expType )
    {
    case ADD:
    case SUB:
    case CONCAT:
	return ( new CegoExpr( _pExpr->clone(isAttrRef), _pTerm->clone(isAttrRef), _expType ) );	
    case TERM:
	return ( new CegoExpr( _pTerm->clone(isAttrRef) ) );
    }
    
}    

CegoExpr& CegoExpr::operator = ( const CegoExpr& e)
{
    _alias = e._alias;
    _expType = e._expType;
    _pExpr = e._pExpr;
    _pTerm = e._pTerm;
    return (*this);
}

Chain CegoExpr::toChain(const Chain& indent) const
{
    Chain s;
    switch ( _expType )
    {
    case CegoExpr::ADD:
	s = indent + _pExpr->toChain() + " + " + _pTerm->toChain();
	break;
    case CegoExpr::SUB:
	s = indent + _pExpr->toChain() + " - " + _pTerm->toChain();
	break;
    case CegoExpr::CONCAT:
	s = indent + _pExpr->toChain() + " | " + _pTerm->toChain();
	break;
    case CegoExpr::TERM:
	s = indent + _pTerm->toChain();
	break;
    }
    return s;
}

Element* CegoExpr::toElement() const
{
    Element* pExprElement = new Element(XML_EXPR_ELEMENT);

    switch ( _expType )
    {
    case CegoExpr::ADD:
	pExprElement->setAttribute( XML_EXPR_ATTR, XML_PLUS_VALUE ); 
	pExprElement->addContent( _pExpr->toElement() );
	pExprElement->addContent( _pTerm->toElement() );
	break;
    case CegoExpr::SUB:
	pExprElement->setAttribute( XML_EXPR_ATTR, XML_SUB_VALUE ); 
	pExprElement->addContent( _pExpr->toElement() );
	pExprElement->addContent( _pTerm->toElement() );
	break;
    case CegoExpr::CONCAT:
	pExprElement->setAttribute( XML_EXPR_ATTR, XML_CONCAT_VALUE ); 
	pExprElement->addContent( _pExpr->toElement() );
	pExprElement->addContent( _pTerm->toElement() );
	break;
    case CegoExpr::TERM:
	pExprElement->setAttribute( XML_EXPR_ATTR, XML_TERM_VALUE ); 
	pExprElement->addContent( _pTerm->toElement() );
	break;
    }
    return pExprElement;
}

void CegoExpr::fromElement(Element *pExpElement, CegoDistManager *pGTM)
{

    if ( _pTerm ) 
	delete _pTerm;
    if ( _pExpr )
	delete _pExpr;

    _pTerm=0;
    _pExpr=0;

    Chain expTypeString = pExpElement->getAttributeValue( XML_EXPR_ATTR );

    if ( expTypeString == Chain(XML_PLUS_VALUE) )
    {
	_expType = CegoExpr::ADD;
	ListT<Element*> el = pExpElement->getChildren(XML_EXPR_ELEMENT);
	Element **pEE = el.First();
	if ( pEE )
	{
	    _pExpr = new CegoExpr(*pEE, pGTM);
	}
	ListT<Element*> tl = pExpElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
    } 
    else if ( expTypeString == Chain(XML_SUB_VALUE) )
    {
	_expType = CegoExpr::SUB;
	ListT<Element*> el = pExpElement->getChildren(XML_EXPR_ELEMENT);
	Element **pEE = el.First();
	if ( pEE )
	{
	    _pExpr = new CegoExpr(*pEE, pGTM);
	}
	ListT<Element*> tl = pExpElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
    }
    else if ( expTypeString == Chain(XML_CONCAT_VALUE) )
    {
	_expType = CegoExpr::CONCAT;
	ListT<Element*> el = pExpElement->getChildren(XML_EXPR_ELEMENT);
	Element **pEE = el.First();
	if ( pEE )
	{
	    _pExpr = new CegoExpr(*pEE, pGTM);
	}
	ListT<Element*> tl = pExpElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
    }
    else if ( expTypeString == Chain(XML_TERM_VALUE) )
    {
	_expType = CegoExpr::TERM;
	ListT<Element*> tl = pExpElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
    }
}

void CegoExpr::encode(char *buf)
{
    
    char* pE = (char*)buf;

    memcpy( pE, &_expType, sizeof(CegoExpr::ExpType));
    pE = pE + sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	_pExpr->encode(pE);
	pE = pE + _pExpr->getEncodingLength();
	_pTerm->encode(pE);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    case CegoExpr::TERM:
    {
	_pTerm->encode(pE);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    }
}

void CegoExpr::decode(char *buf, CegoDistManager *pGTM)
{

    char* pE = (char*)buf;

    memcpy( &_expType, pE,  sizeof(CegoExpr::ExpType));
    pE = pE + sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	_pExpr = new CegoExpr(pE, pGTM);
	pE = pE + _pExpr->getEncodingLength();
	_pTerm = new CegoTerm(pE, pGTM);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    case CegoExpr::TERM:
    {
	_pExpr=0;
	_pTerm = new CegoTerm(pE, pGTM);
	pE = pE + _pTerm->getEncodingLength();
	break;
    }
    }
}

int CegoExpr::getEncodingLength() const
{
    int len = 0;
    
    len += sizeof(CegoExpr::ExpType);

    switch (_expType )
    {
    case CegoExpr::ADD:
    case CegoExpr::SUB:
    case CegoExpr::CONCAT:
    {
	len +=_pExpr->getEncodingLength();
	len +=_pTerm->getEncodingLength();
	break;
    }
    case CegoExpr::TERM:
    {
	len +=_pTerm->getEncodingLength();
	break;
    }
    }

    return len;
}

void CegoExpr::getPlanList(ListT<Element*>& planList)
{

    Element *pPlan = new Element(XML_PLAN_ELEMENT);

    if ( _pTerm )
    {
	_pTerm->getPlanList(planList);
    }
    if ( _pExpr )
    {
	_pExpr->getPlanList(planList);
    }
}

ostream& operator << (ostream& s, const CegoExpr& e)
{

    s << e.toChain();
    return s;
}

