///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTerm.cc
// -----------
// Cego procedure term class implementation
//
// Design and Implementation by Bjoern Lemke
//               
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoTerm
// 
// Description: Term data struture
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

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

#include <string.h>

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

CegoTerm::CegoTerm(char* buf)
{
    _pTerm = 0;
    _pFactor = 0;
    decode(buf);
}

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

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

void CegoTerm::setQueryManager(CegoQueryManager* pTabMng)
{
    if ( _pTerm )
	_pTerm->setQueryManager(pTabMng);
    if ( _pFactor )
	_pFactor->setQueryManager(pTabMng);
}

void CegoTerm::setTabSetId(unsigned tabSetId)
{
    if ( _pTerm )
	_pTerm->setTabSetId(tabSetId);
    if ( _pFactor )
	_pFactor->setTabSetId(tabSetId);
}


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

bool CegoTerm::checkConst() const
{
    if ( _termType == FACTOR)
    {
	return _pFactor->checkConst();
    }
    else
    {
	return _pTerm->checkConst() && _pFactor->checkConst();
    }
}

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

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

bool CegoTerm::checkLob(CegoFieldValue& fv, CegoProcBlock *pBlock)
{
    if ( _pFactor && _termType == FACTOR )
    {
	return _pFactor->checkLob(fv, pBlock);
    }
    return false;
}

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

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

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

void CegoTerm::getFunctionList(SetT<CegoObject>& funcList)
{
    if ( _pTerm )
	_pTerm->getFunctionList(funcList);
    if ( _pFactor )
	_pFactor->getFunctionList(funcList);
}

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

void CegoTerm::getFieldList(ListT<CegoField>& fl, ListT<CegoField> **pFLA) const
{
    switch ( _termType )
    {
    case MUL:
    case DIV:
    {
	_pTerm->getFieldList(fl, pFLA);
	_pFactor->getFieldList(fl, pFLA);
	break;
    }
    case FACTOR:
    {
	_pFactor->getFieldList(fl, pFLA);
	break;
    }
    }
}

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

unsigned CegoTerm::evalReferences(CegoContentObject *pCO, const ListT<CegoField>& fl)
{
    unsigned 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 ListT<CegoField>& fl, bool graceful) const
{
    CegoField f;

    switch ( _termType )
    {
    case MUL:
    {
	CegoField f1 =  _pTerm->evalField(fl, graceful);
	CegoField f2 =  _pFactor->evalField(fl, graceful);

	// the type conversion refers to method : CegoFieldValue operator * ( const CegoFieldValue& fv1, const CegoFieldValue& fv2 )
	CegoDataType dt;
	unsigned len;
	unsigned dim;
	
	if ( f1.getType() == DECIMAL_TYPE )
	{
	    dt = f1.getType();
	    len = f1.getLength();
	    dim = f1.getDim();	   
	}
	else if ( f2.getType() == DECIMAL_TYPE )
	{
	    dt = f2.getType();
	    len = f2.getLength();
	    dim = f2.getDim();	  
	}
	else
	{	    	   
	    dt =  f1.getLength() > f2.getLength() ? f1.getType() : f2.getType();
	    len = f1.getLength() > f2.getLength() ? f1.getLength() : f2.getLength();
	    dim = f1.getLength() > f2.getLength() ? f1.getDim() : f2.getDim();
	}
	
	f = CegoField( Chain("TERM"),
		       Chain("TERM"),
		       f1.getAttrName() + Chain("*") + f2.getAttrName(),
		       dt, len, dim);
		       
	break;	
    }
    case DIV:
    {
	CegoField f1 =  _pTerm->evalField(fl, graceful);
	CegoField f2 =  _pFactor->evalField(fl, graceful);

	// the type conversion refers to method : CegoFieldValue operator / ( const CegoFieldValue& fv1, const CegoFieldValue& fv2 )
	CegoDataType dt;
	unsigned len;
	unsigned dim;
	
	if ( f1.getType() == DECIMAL_TYPE )
	{
	    dt = f1.getType();
	    len = f1.getLength();
	    dim = f1.getDim();
	}
	else if ( f2.getType() == DECIMAL_TYPE )
	{
	    dt = f2.getType();
	    len = f2.getLength();
	    dim = f2.getDim();
	}
	else
	{
	    dt =  f1.getLength() > f2.getLength() ? f1.getType() : f2.getType();
	    len = f1.getLength() > f2.getLength() ? f1.getLength() : f2.getLength();
	    dim = f1.getLength() > f2.getLength() ? f1.getDim() : f2.getDim();
	}
	
	f = CegoField( Chain("TERM"),
		       Chain("TERM"),
		       f1.getAttrName() + Chain("/") + f2.getAttrName(),
		       dt, len, dim );     
	break;
    }
    case FACTOR:
    {
	f =  _pFactor->evalField(fl, graceful);
	break;
    }
    }
    return f;
}

CegoFieldValue CegoTerm::evalFieldValue(ListT<CegoField> **pFLA, CegoProcBlock *pBlock) const
{	
    switch ( _termType )
    {
    case MUL:
	return ( _pTerm->evalFieldValue(pFLA, pBlock) * _pFactor->evalFieldValue(pFLA, pBlock) );
    case DIV:
	return ( _pTerm->evalFieldValue(pFLA, pBlock) / _pFactor->evalFieldValue(pFLA, pBlock) );
    case FACTOR:
	return ( _pFactor->evalFieldValue(pFLA, pBlock) );
    default:
        throw Exception(EXLOC, Chain("Unknown term type"));
    }
}

ListT<CegoAggregation*> CegoTerm::getAggregationList()
{
    switch ( _termType )
    {
    case MUL:
    case DIV:
	return ( _pTerm->getAggregationList() + _pFactor->getAggregationList() );	 
    case FACTOR:
	return ( _pFactor->getAggregationList() );
    default:
        throw Exception(EXLOC, Chain("Unknown term type"));
    }
}

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) ) );
    default:
        throw Exception(EXLOC, Chain("Unknown term type"));
    }   
}

void CegoTerm::encode(char *buf, CegoProcBlock* pBlock)
{
    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, pBlock);
	pE = pE + _pTerm->getEncodingLength(pBlock);
	_pFactor->encode(pE, pBlock);
	pE = pE + _pFactor->getEncodingLength(pBlock);
	break;
    }
    case CegoTerm::FACTOR:
    {
	_pFactor->encode(pE, pBlock);
	pE = pE + _pFactor->getEncodingLength(pBlock);
	break;
    }
    }
}

void CegoTerm::decode(char *buf)
{
    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);
	pE = pE + _pTerm->getEncodingLength();
	_pFactor = new CegoFactor(pE);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    case CegoTerm::FACTOR:
    {
	_pTerm=0;
	_pFactor = new CegoFactor(pE);
	pE = pE + _pFactor->getEncodingLength();
	break;
    }
    }
}

unsigned CegoTerm::getEncodingLength(CegoProcBlock* pBlock) const
{
    unsigned len = 0;

    len += sizeof(CegoTerm::TermType);

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

    return len;
}

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

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

Chain CegoTerm::dbFormat(CegoDatabaseFormater *pForm) const
{
    return pForm->formatTerm(_termType, _pTerm, _pFactor);
}

void CegoTerm::getPlanList(ListT<Element*>& planList)
{
    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;
}

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

    CegoTerm* pMapTerm = 0;
    CegoFactor* pMapFactor = 0;

    bool doMap = true;
    
    if ( _pTerm )
    {
	pMapTerm = _pTerm->map(viewAlias, exprList);
	if ( pMapTerm == 0 )
	    doMap=false;
    }
    if ( _pFactor )
    {
	pMapFactor = _pFactor->map(viewAlias, exprList);
	if ( pMapFactor == 0 )
	    doMap=false;
    }
    
    if ( doMap == false )
    {
	if ( pMapTerm )
	    delete pMapTerm;
	if ( pMapFactor )
	    delete pMapFactor;
	
	return 0;
    }
	     
    switch ( _termType )
    {
    case MUL:
    case DIV:
	return ( new CegoTerm ( pMapTerm, pMapFactor, _termType ) );
    case FACTOR:
	return ( new CegoTerm ( pMapFactor ) );
    default:
        throw Exception(EXLOC, Chain("Unknown term type"));
    }   
}
