///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoOrderSpace.cc
// -----------------
// Cego order space implementation
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoOrderSpace
// 
// Description: Utility class for tuple sorting 
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

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

// CEGO INCLUDES
#include "CegoOrderSpace.h"
// #include "CegoDataType.h"
// #include "CegoTableManager.h"

CegoOrderSpace::CegoOrderSpace()
{
    _pAVL = new AVLTreeT<CegoOrderNode>;
}

CegoOrderSpace::~CegoOrderSpace()
{
    if ( _pAVL )
	delete _pAVL;
}

void CegoOrderSpace::initOrderSpace(ListT<CegoExpr*>* pOrderList, ListT<CegoOrderNode::Ordering>* pOrderOptList, unsigned long long maxOrderSize)
{
    _schemaSet = false;
    _pOrderList = pOrderList;
    _pOrderOptList = pOrderOptList;
    _maxOrderSize = maxOrderSize;
    _orderSize = 0;
    
    CegoExpr **pExpr = pOrderList->First();
    unsigned id=1;
    while ( pExpr )
    {
	ListT<CegoAggregation*> aggList = (*pExpr)->getAggregationList();
	if ( aggList.Size() == 0 )
	{
	    // if no aggregation, we take all used attributes in order schema

	    ListT<CegoAttrDesc*> attrRefList = (*pExpr)->getAttrRefList();
	    
	    CegoAttrDesc** pAttrRef = attrRefList.First();
	    
	    while ( pAttrRef )
	    {
		CegoField f((*pAttrRef)->getTableName(), (*pAttrRef)->getAttrName());
		f.setId(id);
		_orderSchema.Insert(f);
		id++;
		pAttrRef = attrRefList.Next();
	    }
	}
	else
	{
	    // if aggregation, this is a grouping order, we just take the aggregated values

	    CegoAggregation **pAgg = aggList.First();
	    while ( pAgg )
	    {	    
		CegoField f;
		f.setAttrName("AGG");
		f.setId((*pAgg)->getAggregationId());
		_orderSchema.Insert(f);
		
		pAgg = aggList.Next();
	    }
	}
	pExpr = pOrderList->Next();
    }    
}

void CegoOrderSpace::insertTuple(ListT<CegoField>& orderKey, ListT<CegoField>& orderTuple)
{    
    if ( _schemaSet == false )
    {
	_selectSchema = orderTuple;
	_schemaSet = true;
    }
    
    CegoField* pKF = orderKey.First();
    CegoField* pOF = _orderSchema.First();
    while (pKF && pOF )
    {
        pKF->setId(pOF->getId());
        pKF = orderKey.Next();
        pOF = _orderSchema.Next();        
    }
    
    ListT<CegoFieldValue> localTuple;

    unsigned orderEntryLen = 0;
    
    CegoField *pF = orderTuple.First();
    while (pF)
    {
	CegoFieldValue fv = pF->getValue().getLocalCopy();
	localTuple.Insert(fv);
	
	// calculate entry size
	orderEntryLen += fv.usedMemory(); 
	
	pF = orderTuple.Next();
    }

    ListT<CegoFieldValue> localKey;
    
    CegoExpr **pExpr = _pOrderList->First();
    while ( pExpr )
    {	
	setAggregationValue(*pExpr, orderKey);
	
	(*pExpr)->clearAttrCache();

	ListT<CegoField>* fl[2];
	fl[0] = &orderKey;
	fl[1] = 0;

	CegoFieldValue fv = (*pExpr)->evalFieldValue(fl, 0).getLocalCopy();
	
	localKey.Insert(fv);

	// calculate entry size
	orderEntryLen += fv.usedMemory();
       	
	pExpr = _pOrderList->Next();
    }
    
    CegoOrderNode n(localKey, localTuple, _pOrderOptList);
    _orderSize += orderEntryLen;

    if ( _orderSize > _maxOrderSize )
    {
	throw Exception(EXLOC, "Order size exceeded");
    }

    _pAVL->Insert(n);
}

void CegoOrderSpace::resetOrderSpace()
{    
    _pAVL->Empty();
    _orderSize = 0;
}

CegoOrderCursor* CegoOrderSpace::getCursor()
{
    CegoOrderCursor *pOC = new CegoOrderCursor(_pAVL, _selectSchema);
    return pOC;
}

unsigned long long CegoOrderSpace::numAllocated() const
{
    return _orderSize;
}

void CegoOrderSpace::setAggregationValue(CegoExpr *pExpr, ListT<CegoField>& fl)
{
    ListT<CegoAggregation*> aggList = pExpr->getAggregationList();
    
    CegoAggregation **pAgg = aggList.First();
    while ( pAgg )
    {
	CegoField *pF = fl.First();
	while ( pF )
	{
	    if ( pF->getId() == (*pAgg)->getAggregationId() )
	    {	
		(*pAgg)->setFieldValue(pF->getValue());
		pF = 0;
	    }
	    else
	    {
		pF = fl.Next();
	    }
	}
	pAgg = aggList.Next();
    }
}
