///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTerm.cc
// ------------
// Cego procedure term 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: CegoTerm
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoTerm.h"
#include "CegoXMLdef.h"

#include <string.h>

CegoTerm::CegoTerm()
{
    _pTerm=0;
    _pFactor=0;
}

CegoTerm::CegoTerm(char* buf, CegoDistManager *pGTM)
{
    _pTerm = 0;
    _pFactor = 0;

    decode(buf, pGTM);
}

CegoTerm::CegoTerm(Element* pTermElement, CegoDistManager *pGTM)
{
    _pTerm = 0;
    _pFactor = 0;

    fromElement(pTermElement, pGTM);
}

CegoTerm::CegoTerm(CegoTerm *pTerm, CegoFactor *pFactor, TermType termType)
{
    _termType = termType;
    _pTerm=pTerm;
    _pFactor=pFactor;
}

CegoTerm::CegoTerm(CegoFactor *pFactor)
{
    _termType = FACTOR;
    _pFactor=pFactor;
    _pTerm = 0;
}

CegoTerm::~CegoTerm()
{
    if ( _pTerm )
	delete _pTerm;
    if ( _pFactor )
	delete _pFactor;
}

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

CegoAttrDesc* CegoTerm::checkAttr() const
{
    if ( _pFactor && _termType == FACTOR )
    {
	return _pFactor->checkAttr();
    }
    return 0;
}

CegoTerm::TermType CegoTerm::getType() const
{
    return _termType;
}

CegoFactor* CegoTerm::getFactor()
{
    return _pFactor;
}

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


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

void CegoTerm::setBlock(CegoProcBlock *pBlock)
{
    if ( _pTerm )
	_pTerm->setBlock(pBlock);
    if ( _pFactor )
	_pFactor->setBlock(pBlock);
}

void CegoTerm::clearAttrCache()
{
    if ( _pTerm )
	_pTerm->clearAttrCache();
    if ( _pFactor )
	_pFactor->clearAttrCache();
}

void CegoTerm::getSelectQueryList(ListT<CegoSelect*>& queryList)
{
    if ( _pTerm )
	_pTerm->getSelectQueryList(queryList);
    if ( _pFactor )
    {
	if ( _pFactor->getSelect() )
	{
	    queryList.Insert(_pFactor->getSelect());
	}
    }
}

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

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

    switch ( _termType )
    {
    case MUL:
    case DIV:
    {
	fl = _pTerm->getFieldList() + _pFactor->getFieldList();
	break;
    }
    case FACTOR:
    {
	fl = _pFactor->getFieldList();
	break;
    }
    }
    return fl;
}

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

    switch ( _termType )
    {
    case MUL:
    case DIV:
    {
	attrList = _pTerm->getAttrRefList() + _pFactor->getAttrRefList();
	break;
    }
    case FACTOR:
    {
	attrList = _pFactor->getAttrRefList();
	break;
    }
    }
    return attrList;
}

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

    switch ( _termType )
    {
    case MUL:
    case DIV:
    {
	refCount += _pTerm->evalReferences(pCO, fl);
	refCount += _pFactor->evalReferences(pCO, fl);
	break;
    }
    case FACTOR:
    {
	refCount += _pFactor->evalReferences(pCO, fl);
	break;
    }
    }

    return refCount;

}

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

    switch ( _termType )
    {
    case MUL:
    case DIV:
	f = CegoField( Chain("TERM"), Chain("TERM"), toChain(), VARCHAR_TYPE, 20 );
	break;
    case FACTOR:
	f =  _pFactor->evalField();
	break;
    }
    
    return f;
}

CegoFieldValue CegoTerm::evalFieldValue() const
{
    switch ( _termType )
    {
    case MUL:
	return ( _pTerm->evalFieldValue() * _pFactor->evalFieldValue() );	
    case DIV:
	return ( _pTerm->evalFieldValue() / _pFactor->evalFieldValue() );
    case FACTOR:
	return ( _pFactor->evalFieldValue() );
    }

}

ListT<CegoAggregation*> CegoTerm::getAggregationList()
{
    ListT<CegoAggregation*> aggList;
    switch ( _termType )
    {
    case MUL:
    case DIV:
    {
	aggList =  _pTerm->getAggregationList();
	if ( _pFactor->getType() == CegoFactor::AGGREGATION )
	    aggList.Insert ( _pFactor->getAggregation() );
	break;
    }    
    case FACTOR:
    {
	if ( _pFactor->getType() == CegoFactor::AGGREGATION )
	    aggList.Insert ( _pFactor->getAggregation() );
	break;
    }
    }
    
    return aggList;
}

CegoTerm& CegoTerm::operator = ( const CegoTerm& e)
{
    _termType = e._termType;
    _pTerm = e._pTerm;
    _pFactor = e._pFactor;
    return (*this);
}

CegoTerm* CegoTerm::clone(bool isAttrRef)
{
    
    switch ( _termType )
    {
    case MUL:
    case DIV:
	return ( new CegoTerm ( _pTerm->clone(isAttrRef), _pFactor->clone(isAttrRef), _termType ) );
    case FACTOR:
	return ( new CegoTerm ( _pFactor->clone(isAttrRef) ) );
    }
    
}

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

    memcpy( pE, &_termType, sizeof(CegoTerm::TermType));
    pE = pE + sizeof(CegoTerm::TermType);

    switch (_termType )
    {
    case CegoTerm::MUL:
    case CegoTerm::DIV:
    {
	_pTerm->encode(pE);
	pE = pE + _pTerm->getEncodingLength();
	_pFactor->encode(pE);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    case CegoTerm::FACTOR:
    {
	_pFactor->encode(pE);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    }
}

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

    char* pE = (char*)buf;

    memcpy( &_termType, pE, sizeof(CegoTerm::TermType));
    pE = pE + sizeof(CegoTerm::TermType);

    switch (_termType )
    {
    case CegoTerm::MUL:
    case CegoTerm::DIV:
    {
	_pTerm = new CegoTerm(pE, pGTM);
	pE = pE + _pTerm->getEncodingLength();
	_pFactor = new CegoFactor(pE, pGTM);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    case CegoTerm::FACTOR:
    {
	_pTerm=0;
	_pFactor = new CegoFactor(pE, pGTM);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    }
}

int CegoTerm::getEncodingLength() const
{

    int len = 0;

    len += sizeof(CegoTerm::TermType);

    switch (_termType )
    {
    case CegoTerm::MUL:
    case CegoTerm::DIV:
    {
	len += _pTerm->getEncodingLength();
	len += _pFactor->getEncodingLength();
	break;
    }
    case CegoTerm::FACTOR:
    {
	len += _pFactor->getEncodingLength();
	break;
    }
    }

    return len;
}

Chain CegoTerm::toChain() const
{
    Chain s;
    switch ( _termType )
    {
    case CegoTerm::MUL:
	s = _pTerm->toChain() + " * " + _pFactor->toChain();
	break;
    case CegoTerm::DIV:
	s = _pTerm->toChain() + " / " + _pFactor->toChain();
	break;
    case CegoTerm::FACTOR:
	s = _pFactor->toChain();
	break;
    }
    return s;    
}

Element* CegoTerm::toElement() const
{
    Element* pTermElement = new Element(XML_TERM_ELEMENT);

    switch ( _termType )
    {
    case CegoTerm::MUL:	
	pTermElement->setAttribute( XML_TERM_ATTR, XML_MUL_VALUE ); 
	pTermElement->addContent( _pTerm->toElement() );
	pTermElement->addContent( _pFactor->toElement() );
	break;
    case CegoTerm::DIV:
	pTermElement->setAttribute( XML_TERM_ATTR, XML_DIV_VALUE ); 
	pTermElement->addContent( _pTerm->toElement() );
	pTermElement->addContent( _pFactor->toElement() );
	break;
    case CegoTerm::FACTOR:
	pTermElement->setAttribute( XML_TERM_ATTR, XML_FACTOR_VALUE );
 	pTermElement->addContent( _pFactor->toElement() );
	break;
    }
    return pTermElement;    
}

void CegoTerm::fromElement(Element *pTermElement, CegoDistManager *pGTM)
{

    if ( _pTerm ) 
	delete _pTerm;
    if ( _pFactor )
	delete _pFactor;

    _pTerm = 0;
    _pFactor = 0;

    Chain termTypeString = pTermElement->getAttributeValue( XML_TERM_ATTR );

    if ( termTypeString == Chain(XML_MUL_VALUE) )
    {
	_termType = CegoTerm::MUL;
	ListT<Element*> tl = pTermElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
	ListT<Element*> fl = pTermElement->getChildren(XML_FACTOR_ELEMENT);
	Element **pFE = fl.First();
	if ( pFE )
	{
	    _pFactor = new CegoFactor(*pFE, pGTM);
	}
    } 
    else if ( termTypeString == Chain(XML_DIV_VALUE) )
    {
	_termType = CegoTerm::DIV;
	ListT<Element*> tl = pTermElement->getChildren(XML_TERM_ELEMENT);
	Element **pTE = tl.First();
	if ( pTE )
	{
	    _pTerm = new CegoTerm(*pTE, pGTM);
	}
	ListT<Element*> fl = pTermElement->getChildren(XML_FACTOR_ELEMENT);
	Element **pFE = fl.First();
	if ( pFE )
	{
	    _pFactor = new CegoFactor(*pFE, pGTM);
	}	
    }
    else if ( termTypeString == Chain(XML_FACTOR_VALUE) )
    {
	_termType = CegoTerm::FACTOR;
	ListT<Element*> fl = pTermElement->getChildren(XML_FACTOR_ELEMENT);
	Element **pFE = fl.First();
	if ( pFE )
	{
	    _pFactor = new CegoFactor(*pFE, pGTM);
	}
    }
}

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

    Element *pPlan = new Element(XML_PLAN_ELEMENT);

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

ostream& operator << (ostream& s, const CegoTerm& t)
{
    switch ( t._termType )
    {
    case CegoTerm::MUL:
	s << *t._pTerm << " * " << *t._pFactor;
	break;
    case CegoTerm::DIV:
	s << *t._pTerm << " / " << *t._pFactor;
	break;
    case CegoTerm::FACTOR:
	s << *t._pFactor;
	break;
    }
    return s;
}
