///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoJoinObject.cc 
// ------------------
// Cego join object entry implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoJoinObject
// 
// Description: Cego join object container class
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// LFC INCLUDES
#include <lfcbase/Tokenizer.h>

// CEGO INCLUDES
#include "CegoJoinObject.h"
#include "CegoXMLdef.h"
#include "CegoTableObject.h"
#include "CegoViewObject.h"

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

CegoJoinObject::CegoJoinObject()
{
}

CegoJoinObject::CegoJoinObject(const CegoJoinObject& jo) : CegoContentObject(jo)
{
    _joinType = jo._joinType;
    _pLeftObject = jo._pLeftObject; 
    _pRightObject = jo._pRightObject; 
    _pPred = jo._pPred;
}

CegoJoinObject::CegoJoinObject( JoinType joinType, CegoContentObject *pLeftObject, CegoContentObject *pRightObject, CegoPredicate *pPred ) : CegoContentObject( 0, CegoObject::JOIN, Chain("join"))
{
    _joinType = joinType;
    _pLeftObject = pLeftObject;
    _pRightObject = pRightObject;
    _pPred = pPred;

    ListT<CegoField> mergeSchema = pLeftObject->getSchema() + pRightObject->getSchema();
    setSchema(mergeSchema);

    if ( pLeftObject->getType() == CegoObject::JOIN )
    {	
	CegoContentObject **pCO = pLeftObject->getSubCOList().First();
	while ( pCO )
	{
	    _subCOList.Insert(*pCO);
	    pCO = pLeftObject->getSubCOList().Next();
	}
    }
    else
    {
	_subCOList.Insert(pLeftObject);
    }

    if ( pRightObject->getType() == CegoObject::JOIN )
    {	
	CegoContentObject **pCO = pRightObject->getSubCOList().First();
	while ( pCO )
	{
	    _subCOList.Insert(*pCO);
	    pCO = pRightObject->getSubCOList().Next();
	}
    }
    else
    {
	_subCOList.Insert(pRightObject);
    }
}

CegoJoinObject::~CegoJoinObject()
{
    if ( _pLeftObject )
	delete _pLeftObject;
    if ( _pRightObject )
	delete _pRightObject;
    if ( _pPred )
	delete _pPred;
}

void CegoJoinObject::setSchema(const ListT<CegoField>& schema)
{
    _schema = schema;
}

unsigned CegoJoinObject::getEntrySize() const
{
    unsigned entrySize = CegoContentObject::getBaseContentSize();

    entrySize += sizeof(JoinType);
    entrySize += _pLeftObject->getEntrySize();
    entrySize += _pRightObject->getEntrySize();
    if ( _pPred )
	entrySize += _pPred->getEncodingLength();
    else
	entrySize += sizeof(char); // no pred indicator
    
    return entrySize;
}

void CegoJoinObject::encode(char *buf) const
{    
    char* bufPtr = buf;
    unsigned entrySize = getEntrySize();
    CegoContentObject::encodeBaseContent(bufPtr, entrySize);
    bufPtr += CegoContentObject::getBaseContentSize();
    
    memcpy (bufPtr, &_joinType , sizeof(JoinType));
    bufPtr += sizeof(JoinType);
    
    _pLeftObject->encode(bufPtr);
    bufPtr += _pLeftObject->getEntrySize();
    
    _pRightObject->encode(bufPtr);
    bufPtr += _pRightObject->getEntrySize();

    if ( _pPred )
    {
	_pPred->encode(bufPtr, 0);
	bufPtr += _pPred->getEncodingLength();
    }
    else
	*bufPtr = 0;
}

void CegoJoinObject::decode(char *buf)
{    
    char* bufPtr = buf;
    unsigned size;
    CegoContentObject::decodeBaseContent(bufPtr, size);
    bufPtr += CegoContentObject::getBaseContentSize();

    memcpy (&_joinType, bufPtr , sizeof(JoinType));
    bufPtr += sizeof(JoinType);

    CegoObject obj;    
    obj.decodeBase(bufPtr, size);   

    if (  obj.getType() == CegoObject::TABLE )
    {
	_pLeftObject = new CegoTableObject();
    }
    else if (  obj.getType() == CegoObject::VIEW )
    {
	_pLeftObject = new CegoViewObject();
    }
    else if (  obj.getType() == CegoObject::JOIN )
    {
	_pLeftObject = new CegoJoinObject();
    }
    else
    {
	throw Exception(EXLOC, "Object type not supported");
    }

    _pLeftObject->decode(bufPtr);

    bufPtr += _pLeftObject->getEntrySize();

    obj.decodeBase(bufPtr, size);   

    if (  obj.getType() == CegoObject::TABLE )
    {
	_pRightObject = new CegoTableObject();
    }
    else if (  obj.getType() == CegoObject::VIEW )
    {
	_pRightObject = new CegoViewObject();
    }
    else if (  obj.getType() == CegoObject::JOIN )
    {
	_pRightObject = new CegoJoinObject();
    }
    else
    {
	throw Exception(EXLOC, "Object type not supported");
    }

    _pRightObject->decode(bufPtr);
    bufPtr += _pLeftObject->getEntrySize();

    if ( *bufPtr != 0 )
    {
	_pPred = new CegoPredicate(bufPtr);
    }

    ListT<CegoField> mergeSchema = _pLeftObject->getSchema() + _pRightObject->getSchema();
    setSchema(mergeSchema);

    if ( _pLeftObject->getType() == CegoObject::JOIN )
    {	
	CegoContentObject **pCO = _pLeftObject->getSubCOList().First();
	while ( pCO )
	{
	    _subCOList.Insert(*pCO);
	    pCO = _pLeftObject->getSubCOList().Next();
	}
    }
    else
    {
	_subCOList.Insert(_pLeftObject);
    }

    if ( _pRightObject->getType() == CegoObject::JOIN )
    {	
	CegoContentObject **pCO = _pRightObject->getSubCOList().First();
	while ( pCO )
	{
	    _subCOList.Insert(*pCO);
	    pCO = _pRightObject->getSubCOList().Next();
	}
    }
    else
    {
	_subCOList.Insert(_pRightObject);
    }
}

CegoJoinObject::JoinType CegoJoinObject::getJoinType()
{
    return _joinType;
}

CegoContentObject* CegoJoinObject::getLeftObject()
{
    return _pLeftObject;
}

CegoContentObject* CegoJoinObject::getRightObject()
{
    return _pRightObject;
}

CegoPredicate* CegoJoinObject::getPredDesc()
{
    return _pPred;
}

void CegoJoinObject::getPredList(ListT<CegoPredicate*>& predList)
{
    if ( _pPred )
	predList.Insert(_pPred);
    
    if ( _pLeftObject )
	if ( _pLeftObject->getType() == CegoObject::JOIN )
	    ((CegoJoinObject*)_pLeftObject)->getPredList(predList);

    if ( _pRightObject )
	if ( _pRightObject->getType() == CegoObject::JOIN )
	    ((CegoJoinObject*)_pRightObject)->getPredList(predList);
}

CegoJoinObject& CegoJoinObject::operator = ( const CegoJoinObject& jo)
{
    CegoContentObject::operator=(jo);
    
    _joinType = jo._joinType;
    _pLeftObject = jo._pLeftObject;
    _pRightObject = jo._pRightObject;
    _pPred = jo._pPred;
    return (*this);
}

bool CegoJoinObject::operator == ( const CegoJoinObject& jo)
{
    return CegoContentObject::operator==(jo);
}

Chain CegoJoinObject::getId(CegoProcBlock *pBlock) const
{
    Chain s;

    switch ( _joinType )
    {
    case INNER:
    {
	s += _pLeftObject->getId(pBlock) + Chain("ij") + _pRightObject->getId(pBlock);
	if ( _pPred )
	    s += Chain("o") + _pPred->getId(pBlock);
	break;
    }
    case LEFTOUTER:
    {
	s += _pLeftObject->getId(pBlock) + Chain("loj") + _pRightObject->getId(pBlock);
	if ( _pPred )
	    s += Chain("o") + _pPred->getId(pBlock);
	break;
    }
    case RIGHTOUTER:
    {
	s += _pLeftObject->getId(pBlock) + Chain("roj") + _pRightObject->getId(pBlock);
	if ( _pPred )
	    s += Chain("o") + _pPred->getId(pBlock);
	break;
    }
    }
    return s;
}

Chain CegoJoinObject::toChain(unsigned defTabSetId) const
{
    Chain s;

    switch ( _joinType )
    {
    case INNER:
    {
	s += _pLeftObject->toChain(defTabSetId) + Chain("\ninner join ") + _pRightObject->toChain(defTabSetId);
	if ( _pPred )
	    s += Chain(" on ") + _pPred->toChain(defTabSetId);
	break;
    }
    case LEFTOUTER:
    {
	s += _pLeftObject->toChain(defTabSetId) + Chain("\nleft outer join ") + _pRightObject->toChain(defTabSetId);
	if ( _pPred )
	    s += Chain(" on ") + _pPred->toChain(defTabSetId);
	break;
    }
    case RIGHTOUTER:
    {
	s += _pLeftObject->toChain(defTabSetId) + Chain("\nright outer join ") + _pRightObject->toChain(defTabSetId);
	if ( _pPred )
	    s += Chain(" on ") + _pPred->toChain(defTabSetId);
	break;
    }
    }

    return s;
}

Chain CegoJoinObject::getInfo() const
{
    Chain s("NA");
    return s;
}

ListT<Chain> CegoJoinObject::getSubAliasList() const
{

    ListT<Chain> subList;
    
    if ( _pLeftObject )
    {
	if ( _pLeftObject->getType() == CegoObject::JOIN )
	    subList += ((CegoJoinObject*)_pLeftObject)->getSubAliasList();
	else
	    subList.Insert(_pLeftObject->getTabAlias());
    }
    
    if ( _pRightObject )
    {
	if ( _pRightObject->getType() == CegoObject::JOIN )
	    subList += ((CegoJoinObject*)_pRightObject)->getSubAliasList();
	else
	    subList.Insert(_pRightObject->getTabAlias());
    }

    return subList;
}

CegoContentObject* CegoJoinObject::clone(bool isAttrRef)
{
    CegoJoinObject *pClone = new CegoJoinObject( _joinType, 
						 _pLeftObject->clone(isAttrRef),
						 _pRightObject->clone(isAttrRef),
						 _pPred == 0 ? 0 : _pPred->clone(isAttrRef) );
    return pClone;
}
