///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoAggregation.cc
// -----------------
// Cego aggregation structure 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: CegoAggregation
// 
// Description: Aggregation container class to store an aggregation definition 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// INCLUDES
#include "CegoAggregation.h"
#include "CegoExpr.h"
#include "CegoXMLdef.h"

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

CegoAggregation::CegoAggregation()
{
    _type = COUNT;
    _pExpr = 0;
}

CegoAggregation::CegoAggregation(char* buf, CegoDistManager *pGTM)
{
    decode(buf, pGTM);
}

CegoAggregation::CegoAggregation(Element* pAE, CegoDistManager *pGTM)
{
    _pExpr=0;
    fromElement(pAE, pGTM);
}

CegoAggregation::CegoAggregation(AggType type, CegoExpr* pExpr, bool isDistinct)
{
    _type = type;
    _pExpr = pExpr;
    _isDistinct = isDistinct;
}

CegoAggregation::~CegoAggregation()
{
    if ( _pExpr )
	delete _pExpr;
}

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

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

void CegoAggregation::setAggregationId(int aid)
{
    _aid = aid;
}

const int CegoAggregation::getAggregationId() const
{
    return _aid;
}

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

CegoFieldValue& CegoAggregation::getFieldValue()
{
    return _fv;
}

void CegoAggregation::setFieldValue(const CegoFieldValue& fv)
{
    _fv = fv;
}

CegoAggregation& CegoAggregation::operator = ( const CegoAggregation& ag)
{
    _type = ag._type;
    _pExpr = ag._pExpr;
    _aid = ag._aid;
    return (*this);
}
   
const CegoAggregation::AggType CegoAggregation::getType() const
{
    return _type;
}

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

CegoAggregation* CegoAggregation::clone(bool isAttrRef)
{
    CegoAggregation *pAgg;
    if ( _pExpr )
	pAgg = new  CegoAggregation(_type, _pExpr->clone(isAttrRef), _isDistinct );
    else
	pAgg = new  CegoAggregation();

    pAgg->setAggregationId ( _aid );
    return ( pAgg ) ; 
}

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

    if ( _pExpr )
    {
	attrList = _pExpr->getAttrRefList();
    }
    
    return attrList;
}

void CegoAggregation::encode(char *buf)
{
    char* pBuf = (char*)buf;
    
    memcpy( pBuf, &_type, sizeof(CegoAggregation::AggType));
    pBuf = pBuf + sizeof(CegoAggregation::AggType);

    char c = 0;
    if ( _pExpr )
    {
	c = 1;
    }
    memcpy( pBuf, &c, sizeof(char));
    pBuf = pBuf + sizeof(char);
    
    if ( _pExpr )
    {
	_pExpr->encode(pBuf);
	pBuf = pBuf + _pExpr->getEncodingLength();
    }
}

void CegoAggregation::decode(char *buf, CegoDistManager *pGTM)
{
    
    if ( _pExpr )
    {
	delete _pExpr;
	_pExpr = 0;
    }

    char* pBuf = (char*)buf;
    
    memcpy( &_type, pBuf,  sizeof(CegoAggregation::AggType));
    pBuf = pBuf + sizeof(CegoAggregation::AggType);
    
    char c;
    memcpy( pBuf, &c, sizeof(char));
    pBuf = pBuf + sizeof(char);
    
    if ( c == 1 )
    {
	_pExpr = new CegoExpr(pBuf, pGTM);	
    }

}

int CegoAggregation::getEncodingLength() const
{
    int size = sizeof(CegoAggregation::AggType);
    size += sizeof(char);
    if ( _pExpr )
	size += _pExpr->getEncodingLength();
    return size;
}

Element* CegoAggregation::toElement() const
{
    Element *pAggElement = new Element( XML_AGGREGATION_ELEMENT ); 

    switch (_type)
    {
    case CegoAggregation::MIN:
	pAggElement->setAttribute(XML_AGG_ATTR, XML_MIN_VALUE);
	break;
    case CegoAggregation::MAX:
	pAggElement->setAttribute(XML_AGG_ATTR, XML_MAX_VALUE);
	break;
    case CegoAggregation::AVG:
	pAggElement->setAttribute(XML_AGG_ATTR, XML_AVG_VALUE);
	break;
    case CegoAggregation::SUM:
	pAggElement->setAttribute(XML_AGG_ATTR, XML_SUM_VALUE);
	break;
    case CegoAggregation::COUNT:
	pAggElement->setAttribute(XML_AGG_ATTR, XML_COUNT_VALUE);
	break;
    }

    if ( _pExpr )
    {
	pAggElement->addContent(_pExpr->toElement());
    }

    return pAggElement;
}

void CegoAggregation::fromElement(Element *pAggElement, CegoDistManager *pGTM)
{
    
    if ( _pExpr )
	delete _pExpr;
    _pExpr = 0;

    ListT<Element*> fl = pAggElement->getChildren(XML_FACTOR_ELEMENT);
    Element **pFE = fl.First();
    if ( pFE )
    {
	_pExpr = new CegoExpr(*pFE, pGTM);
    }
   
    Chain aggString = pAggElement->getAttributeValue( XML_AGG_ATTR );

    if ( aggString == Chain(XML_MIN_VALUE) )
	_type = CegoAggregation::MIN;
    else if ( aggString == Chain(XML_MAX_VALUE) )
	_type = CegoAggregation::MAX;
    else if ( aggString == Chain(XML_AVG_VALUE) )
	_type = CegoAggregation::AVG;
    else if ( aggString == Chain(XML_SUM_VALUE) )
	_type = CegoAggregation::SUM;
    else if ( aggString == Chain(XML_COUNT_VALUE) )
	_type = CegoAggregation::COUNT;

}

Chain CegoAggregation::toChain() const
{
    Chain s;
    switch (_type)
    {
    case CegoAggregation::MIN:
	s =  Chain("min(") + _pExpr->toChain() + Chain(")");
	break;
    case CegoAggregation::MAX:
	s =  Chain("max(") + _pExpr->toChain() + Chain(")");
	break;
    case CegoAggregation::AVG:
	s =  Chain("avg(") + _pExpr->toChain() + Chain(")");
	break;
    case CegoAggregation::SUM:
	s =  Chain("sum(") + _pExpr->toChain() + Chain(")");
	break;
    case CegoAggregation::COUNT:
    {       
	if ( _pExpr )
	{ 
	    Chain distOpt;
	    if ( _isDistinct )
		distOpt = Chain(" distinct ");	    
	    s = Chain("count(") + distOpt + _pExpr->toChain() + Chain(")");
	}
	else
	    s = Chain("count(*)");
    }
    }

    return s;
}

ostream& operator << (ostream& s, const CegoAggregation& ag)
{
    s << ag.toChain();
    return s;
}
