///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoJoinObject.cc 
// ------------------
// Cego join object entry 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: CegoJoinObject
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// base includes
#include <lfc/Tokenizer.h>

// cego includes
#include "CegoJoinObject.h"
#include "CegoXMLHelper.h"
#include "CegoXMLdef.h"
#include "CegoTypeConverter.h"
#include "CegoTableObject.h"
#include "CegoViewObject.h"

#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(Element *pJO) : CegoContentObject( 0, CegoObject::JOIN, Chain("join"))
{
    putElement(pJO);
}

CegoJoinObject::CegoJoinObject( JoinType joinType, CegoContentObject *pLeftObject, CegoContentObject *pRightObject, CegoPredDesc *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 )
    {
	
	// CegoJoinObject *pJCO = (CegoJoinObject*)pLeftObject;
	CegoContentObject **pCO = pLeftObject->getSubCOList().First();
	while ( pCO )
	{
	    _subCOList.Insert(*pCO);
	    pCO = pLeftObject->getSubCOList().Next();
	}
    }
    else
    {
	_subCOList.Insert(pLeftObject);
    }


    // UNDER INVESTIGATION

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

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

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

    entrySize += sizeof(JoinType);
    entrySize += _pLeftObject->getEntrySize();
    entrySize += _pRightObject->getEntrySize();
    entrySize += _pPred->getEncodingLength();
    
    return entrySize;
}

void CegoJoinObject::encode(char *buf)
{
    char* bufPtr = buf;
    int 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();
    
    _pPred->encode(bufPtr);
    bufPtr += _pPred->getEncodingLength();
    
}

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

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


    CegoObject obj;
    
    obj.decodeBase(bufPtr, size);   
    
    CegoContentObject *pLeftObject;

    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();

    // no support for subselects in Join objects ( pGTM required as argument )
    _pPred = new CegoPredDesc(bufPtr, 0);

}

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

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

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

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

void CegoJoinObject::getPredList(ListT<CegoPredDesc*>& predList)
{

    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::toChain() const
{
    Chain s;

    switch ( _joinType )
    {
    case INNER:
    {
	s += _pLeftObject->toChain() + Chain(" inner join ") + _pRightObject->toChain() + Chain(" on ") + _pPred->toChain();
	break;
    }
    case LEFTOUTER:
    {
	s += _pLeftObject->toChain() + Chain(" left outer join ") + _pRightObject->toChain() + Chain(" on ") + _pPred->toChain();
	break;
    }
    case RIGHTOUTER:
    {
	s += _pLeftObject->toChain() + Chain(" right outer join ") + _pRightObject->toChain() + Chain(" on ") + _pPred->toChain();
	break;
    }
    }

    return s;
}

Element* CegoJoinObject::getElement() const
{
    Element* pRoot = new Element(XML_OBJ_ELEMENT);

    pRoot->setAttribute(XML_TSID_ATTR, Chain(getTabSetId()));
    pRoot->setAttribute(XML_OBJTYPE_ATTR, XML_JOINOBJ_VALUE);	
    pRoot->setAttribute(XML_OBJNAME_ATTR, getName());

    pRoot->addContent(_pLeftObject->getElement());
    pRoot->addContent(_pRightObject->getElement());
    pRoot->addContent(_pPred->toElement());
    
    return pRoot;
}

void CegoJoinObject::putElement(Element* pElement)
{

    Element *pRoot = pElement;
    
    if ( pRoot )
    {
	
	Chain objName = pRoot->getAttributeValue(XML_OBJNAME_ATTR); 
	int tabSetId = pRoot->getAttributeValue(XML_TSID_ATTR).asInteger();
	
	setName(objName);
	setTabName(objName);
	setTabSetId(tabSetId);

	setType(CegoObject::JOIN);
	
	ListT<Element*> subList = pRoot->getChildren(XML_OBJ_ELEMENT);
	Element **pLeft = subList.First();

	Chain objType;

	objType = (*pLeft)->getAttributeValue(XML_OBJTYPE_ATTR);
	if ( objType == Chain(XML_JOINOBJ_VALUE))
	    _pLeftObject = new CegoJoinObject(*pLeft);
	else if ( objType == Chain(XML_TABOBJ_VALUE))
	    _pLeftObject = new CegoTableObject(*pLeft);
	else if ( objType == Chain(XML_VIEWOBJ_VALUE))
	    _pLeftObject = new CegoViewObject(*pLeft);

	Element **pRight = subList.Next();
	objType = (*pRight)->getAttributeValue(XML_OBJTYPE_ATTR);
	if ( objType == Chain(XML_JOINOBJ_VALUE))
	    _pRightObject = new CegoJoinObject(*pRight);
	else if ( objType == Chain(XML_TABOBJ_VALUE))
	    _pRightObject = new CegoTableObject(*pRight);
	else if ( objType == Chain(XML_VIEWOBJ_VALUE))
	    _pRightObject = new CegoViewObject(*pRight);
	
	ListT<Element*> predList = pRoot->getChildren(XML_PRED_ELEMENT);
	Element **pPred = predList.First();
	// for predicates containing subqueries, the GTM should be set up to an appropriate value
	// for now we set to zero
	_pPred = new CegoPredDesc(*pPred, 0);

    }    
}

Chain CegoJoinObject::getFormatted() const
{

    Chain s;
	
    int maxAttrLen = 12;
    CegoField* pF = _schema.First();
    while (pF)
    {
	if (maxAttrLen < pF->getAttrName().length())
	    maxAttrLen = pF->getAttrName().length();
	pF = _schema.Next();
    }

    int maxFillLen = maxAttrLen + 28;

    return s;

}

Chain CegoJoinObject::fill(const Chain& s, int num) const
{
    
    Chain fs = Chain("");
    while (num > 0)
    {
	fs = fs + s;
	num--;
    }

    return fs;
}

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