///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTableCursor.cc
// ------------------
// Cego table cursor class implementation
//      
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoTableCursor
//
// Description: Basic table cursor implementation
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoTableCursor.h"

CegoTableCursor::CegoTableCursor()
{
    _pIC = 0;
    _pBTC = 0;
}

CegoTableCursor::CegoTableCursor(CegoTableManager* pTM, unsigned tabSetId, const Chain& tableName, bool ignoreTouched)
{
    _pTM = pTM;
    _tableName = tableName;
    _pIC = 0;
    _pBTC = 0;
    _pC = 0;
    _idxMatch = CegoAttrCond::INAPP;
    _tabSetId = tabSetId;
    _ignoreTouched = ignoreTouched;
    _idxSetup = false;
    _idxCached = false;
    
    _pDBMng = _pTM->getDBMng();
	
    _modId = _pTM->getDBMng()->getModId("CegoTableCursor");
}

CegoTableCursor::~CegoTableCursor()
{
    if (_pIC)
    {
	_pIC->abort();
	delete _pIC;
    }
    if (_pBTC)
    {
	_pBTC->abort();
	delete _pBTC;
    }

    if ( _pC )
    {
	_pC->abort();
	delete _pC;
    }
}

CegoAttrCond::IndexMatch CegoTableCursor::setup( const CegoAttrCond& attrCond )
{    
    if ( attrCond.isAnd()  )
	return setupAnd(attrCond);
    else
    {
	_orCondList = attrCond.getAndCondList();
	
	if ( _orCondList.Size() > 0 )
	{
	    CegoAttrCond* pAD = _orCondList.First();
	    return setupAnd(*pAD);
	}
	else
	{
	    throw Exception(EXLOC, Chain("Invalid condition set for or attr condition"));
	}
    }
}


CegoAttrCond::IndexMatch CegoTableCursor::setupAnd( const CegoAttrCond& attrCond )
{    
    if ( attrCond.numComp() == 0 )
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("No appropriate index found for  ") + _tableName);
#endif

	_idxSetup = false;
	_idxMatch = CegoAttrCond::INAPP;
	return _idxMatch;
    }
   
    if ( _idxSetup == false ) 
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Setting up tablecursor for " ) + _tableName + Chain (" with ") + attrCond.toChain());
#endif

	if ( _idxCached == false )
	{
	    ListT<CegoKeyObject> keyList;
	    ListT<CegoCheckObject> checkList;
	    ListT<CegoTriggerObject> triggerList;
	    ListT<CegoAliasObject> aliasList;
	    unsigned numInvalid;

	    _pTM->getObjectListByTable(_tabSetId, _tableName, _idxList, _btreeList, keyList, checkList, triggerList, aliasList, numInvalid);

	    _idxCached = true;
	}
    
	CegoTableObject *pIOE = _idxList.First();
	
	_idxMatch = CegoAttrCond::INAPP;
	
	// first check for AVL index objects
	
	unsigned strength = 0;
	unsigned relevance = 0;
	    
	while ( pIOE && _idxMatch != CegoAttrCond::FULL )
	{	    
	    if ( pIOE->isValid())
	    {
#ifdef CGDEBUG
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Checking index ") + pIOE->getName());
#endif

		CegoAttrCond::IndexMatch indexMatch = attrCond.checkIndex(pIOE->getSchema());

		if ( indexMatch == CegoAttrCond::FULL || indexMatch == CegoAttrCond::PART )
		{
		    CegoAttrCond checkCond = attrCond.getIndexCond(pIOE->getSchema());

		    if ( checkCond.getStrength() > strength )
		    {
			strength = checkCond.getStrength();
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" tops for ") + checkCond.toChain());
#endif			
			_attrCond = checkCond; 
			//_attrCond.setIdxSchema(pIOE->getSchema());
			
			_idxName = pIOE->getName();
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Using index ") + _idxName );
#endif			
			_idxSchema = pIOE->getSchema();
			_type = pIOE->getType();
			_idxMatch = indexMatch;
		    }
		    else
		    {
#ifdef CGDEBUG
			_pDBMng->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" to weak for ") + checkCond.toChain());
#endif
		    }
		}
	    }
	    pIOE = _idxList.Next();
	}
	
	if ( _idxMatch == CegoAttrCond::INAPP )
	{
	    // now check for btree index objects
	    
	    CegoBTreeObject *pBTO = _btreeList.First();
		    	    
	    while ( pBTO )
	    {
#ifdef CGDEBUG
		_pDBMng->log(_modId, Logger::DEBUG, Chain("Checking btree ") + pBTO->getName());
#endif
		if ( pBTO->isValid() )
		{
		    CegoAttrCond::IndexMatch indexMatch = attrCond.checkIndex(pBTO->getSchema());
		    
		    if ( indexMatch == CegoAttrCond::FULL || indexMatch == CegoAttrCond::PART )
		    {
			CegoAttrCond checkCond = attrCond.getIndexCond(pBTO->getSchema());
			
			if ( checkCond.getStrength() > strength )
			{			    
			    strength = checkCond.getStrength();
			    relevance = pBTO->getRelevance();
#ifdef CGDEBUG
			    _pDBMng->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" tops for ") + checkCond.toChain());
#endif
			    _attrCond = checkCond; 
			    //_attrCond.setIdxSchema(pBTO->getSchema());
			    
			    _idxName = pBTO->getName();
#ifdef CGDEBUG
			    _pDBMng->log(_modId, Logger::DEBUG, Chain("Using btree ") + _idxName );
#endif			    
			    _idxSchema = pBTO->getSchema();
			    _type = pBTO->getType();
			    _idxMatch = indexMatch;
			}
			else if ( checkCond.getStrength() == strength )
			{
#ifdef CGDEBUG
			    _pDBMng->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" equals, checking for btree relevance ") + checkCond.toChain());
#endif
			    
			    if ( relevance < pBTO->getRelevance() )
			    {
#ifdef CGDEBUG
				_pDBMng->log(_modId, Logger::DEBUG, Chain("Relevance ") + Chain(pBTO->getRelevance()) + Chain(" tops ") + Chain(relevance));
#endif
				_attrCond = checkCond; 
				//_attrCond.setIdxSchema(pBTO->getSchema());
				
				_idxName = pBTO->getName();
#ifdef CGDEBUG
				_pDBMng->log(_modId, Logger::DEBUG, Chain("Using btree ") + _idxName );
#endif
				
				_idxSchema = pBTO->getSchema();
				_type = pBTO->getType();
				_idxMatch = indexMatch;				
			    }
			}
			else
			{
#ifdef CGDEBUG
			    _pDBMng->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" to weak for ") + checkCond.toChain());
#endif
			}
		    }
		}
		pBTO = _btreeList.Next();
	    }
	}

	_idxSetup = true;
    }
    else
    {
#ifdef CGDEBUG
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Tablecursor already set up, just update ..."));
#endif
	_attrCond.update(attrCond);
    }

#ifdef CGDEBUG
    if ( _idxMatch == CegoAttrCond::FULL )
    {
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Index classified as full qualified"));
    }
    else if ( _idxMatch == CegoAttrCond::PART )
    {
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Index classified as part qualified"));
    }
    else
    {
	_pDBMng->log(_modId, Logger::DEBUG, Chain("Index classified as not qualified"));
    }
#endif

    return _idxMatch;
}

void CegoTableCursor::getIdxSchema(ListT<CegoField>& idxSchema)
{
    idxSchema = _idxSchema;
}

bool CegoTableCursor::getFirst(ListT<CegoField>& fl, CegoDataPointer& dp)
{		 
    if ( _pTM->getIsolationLevel() == CegoTableManager::READ_UNCOMMITTED )
	_readUncommitted = true;
    else
	_readUncommitted = false;
    
    if ( _idxMatch == CegoAttrCond::FULL || _idxMatch == CegoAttrCond::PART  )
    {
	if ( _type == CegoObject::AVLTREE 
	     || _type == CegoObject::UAVLTREE 
	     || _type == CegoObject::PAVLTREE )
	{
	    if (_pIC)
	    {
		_pIC->reset();
	    }
	    else
	    {
		_pIC = new CegoAVLIndexCursor(_pTM, _tabSetId, _idxName, _type, &_attrCond, _ignoreTouched, _readUncommitted);
	    }
	    return  _pIC->getFirst(fl, dp);
	}
	else if ( _type == CegoObject::BTREE
	     || _type == CegoObject::UBTREE 
	     || _type == CegoObject::PBTREE )
	{
	    if (_pBTC)
	    {
		_pBTC->reset();
	    }
	    else
	    {
		_pBTC = new CegoBTreeCursor(_pTM, _tabSetId, _idxName, _type, &_attrCond, _ignoreTouched, _readUncommitted);
	    }

	    if ( _orCondList.Size() > 0 )
	    {
		
		bool hasTuple = _pBTC->getFirst(fl, dp);

		bool moreComp=true;
		while ( hasTuple == false && moreComp )
		{
		    // switch to next attribute condition in or list
		    delete _pBTC;
		    _pBTC=0;
		    
		    CegoAttrCond* pAD = _orCondList.Next();
		    if ( pAD )
		    {
			setupAnd(*pAD);
			
			_pBTC = new CegoBTreeCursor(_pTM, _tabSetId, _idxName, _type, &_attrCond, _ignoreTouched, _readUncommitted);
			hasTuple = _pBTC->getFirst(fl, dp);
		    }
		    else
		    {
			moreComp = false;
		    }
		}
		return hasTuple;
	    }
	    else
	    {
		return _pBTC->getFirst(fl, dp);
	    }

	} 
	else
	{
	    throw Exception(EXLOC, Chain("Invalid index type"));
	}
    }
    else
    {	
	if ( _pC )
	{
	    _pC->reset();
	}
	else
	{
	    _pC = _pTM->getObjectCursor(_tabSetId, _tableName, _tableName, CegoObject::TABLE);
	}

	unsigned len;
	char* pc = (char*)_pC->getFirst(len, dp);

	if ( pc && len > 0 )
	{	    
	    unsigned long long tid;
	    unsigned long long tastep;
	    CegoTupleState ts;

	    unsigned toff = _qh.decodeTupleHeader(tid, tastep, ts, pc);

	    char* tp = pc + toff;
	    unsigned tlen = len - toff;
	    
	    if ( tid != 0  )
	    {
		if ( _ignoreTouched == true )
		{
		    if ( ts == CegoTupleState::INSERTED 
			 && tid == _pTM->getTID(_tabSetId) 
			 && tastep < _pTM->getTAStep(_tabSetId) )
		    {
			_qh.decodeFVL(fl, tp, tlen);
			return true;
		    }
		    else
		    {
			return getNext(fl, dp);
		    }
		}
		else
		{
		    if ( ( _readUncommitted == true 
			   && ts == CegoTupleState::INSERTED )
			 || ( _readUncommitted == false 
			      && (( ts == CegoTupleState::INSERTED && tid == _pTM->getTID(_tabSetId)) 
				  || ( ts == CegoTupleState::DELETED && tid != _pTM->getTID(_tabSetId)))))
		    {
			_qh.decodeFVL(fl, tp, tlen);
			return true;
		    }
		    else
		    {
			return getNext(fl, dp);
		    }		
		}
	    }	    
	    _qh.decodeFVL(fl, tp, tlen);
	    return true;
	}
	return false;
    }
}

bool CegoTableCursor::getNext(ListT<CegoField>& fl, CegoDataPointer& dp)
{
    if ( _idxMatch == CegoAttrCond::FULL || _idxMatch == CegoAttrCond::PART  )
    {
	if ( _type == CegoObject::AVLTREE 
	     || _type == CegoObject::UAVLTREE 
	     || _type == CegoObject::PAVLTREE )
	{
	    if ( _pIC == 0 )
	    {
		throw Exception(EXLOC, "Invalid avl cursor");
	    }
	    return _pIC->getNext(fl, dp);
	}
	else if ( _type == CegoObject::BTREE 
	     || _type == CegoObject::UBTREE
	     || _type == CegoObject::PBTREE )
	{
	    if ( _pBTC == 0 )
	    {
		throw Exception(EXLOC, "Invalid btree cursor");
	    }


	    if ( _orCondList.Size() > 0 )
	    {
		bool hasTuple = _pBTC->getNext(fl, dp);
	    
		bool moreComp=true;
		while ( hasTuple == false && moreComp )
		{

		    // cout << "Switching cursor ... " << endl;			    
		    // switch to next attribute condition in or list
		    delete _pBTC;
		    _pBTC=0;
		    
		    CegoAttrCond* pAD = _orCondList.Next();
		    if ( pAD )
		    {
			// cout << "Next or compare =  " << pAD->toChain() << endl;			    
			setupAnd(*pAD);
		    
			_pBTC = new CegoBTreeCursor(_pTM, _tabSetId, _idxName, _type, &_attrCond, _ignoreTouched, _readUncommitted);
			hasTuple = _pBTC->getFirst(fl, dp);
		    }
		    else
		    {
			moreComp = false;
		    }
		}
		return hasTuple;
	    }
	    else
	    {
		return _pBTC->getNext(fl, dp);
	    }	      
	}
	else
	{
	    throw Exception(EXLOC, Chain("Invalid index type"));
	}
    }
    else
    {
	if ( _pC == 0 )
	{
	    throw Exception(EXLOC, "Invalid ObjectCursor");
	}
	
	do
	{	    
	    unsigned len;

	    char* pc = (char*)_pC->getNext(len, dp);

	    if ( pc && len > 0 )
	    {	       	      
		bool useEntry=true;
		
		unsigned long long tid;
		unsigned long long tastep;
		CegoTupleState ts;

		unsigned toff = _qh.decodeTupleHeader(tid, tastep, ts, pc);
		
		char* tp = pc + toff;
		unsigned tlen = len - toff;

		if ( tid != 0 )
		{	    
		    if ( _ignoreTouched == true )
		    {			
			if ( ts == CegoTupleState::INSERTED 
			     && tid == _pTM->getTID(_tabSetId) 
			     && tastep < _pTM->getTAStep(_tabSetId) )
			{
			    _qh.decodeFVL(fl, tp, tlen);
			    return true;   
			}
			else 
			{
			    useEntry = false;
			}
		    }
		    else
		    {	
			if ( ( _readUncommitted == true 
			       && ts == CegoTupleState::INSERTED )
			     || ( _readUncommitted == false 
				  && (( ts == CegoTupleState::INSERTED && tid == _pTM->getTID(_tabSetId)) 
				      || ( ts == CegoTupleState::DELETED && tid != _pTM->getTID(_tabSetId)))))			    
			{
			    _qh.decodeFVL(fl, tp, tlen);
			    return true;   
			}
			else 
			{
			    useEntry=false;
			}
		    }
		}
		
		if ( useEntry )
		{
		    
		    if ( fl.Size() > 0 )
			_qh.decodeFVL(fl, tp, tlen);
		    
		    return true;
		}
	    }
	    else
	    {
		return false;
	    }	    
	}  while ( 1 );
    }    
}

const CegoObject::ObjectType CegoTableCursor::getIndexType() const
{
    return _type;
}

const Chain& CegoTableCursor::getIndexName() const
{
    return _idxName;
}

void CegoTableCursor::abort()
{
    if (_pIC)
    {
	_pIC->abort();
    }
    if (_pBTC)
    {
	_pBTC->abort();
    }
    if ( _pC )
    {
	_pC->abort();
    }
}
