///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoTableCursor.cc
// ------------------
// Cego table cursor 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: CegoTableCursor
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoTableCursor.h"

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

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

CegoTableCursor::CegoTableCursor(CegoTableManager* pTM, int 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;
    _pOE = 0;
    _pBTO = 0;

    _pLogger = _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 ( _idxSetup == false || attrCond.diff(_attrCond) == false )
    {
#ifdef CGDEBUG
	_pLogger->log(_modId, Logger::DEBUG, Chain("Setting up tablecursor with ") + attrCond.toChain());
#endif

	_idxSetup = true;

	_pTM->getObjectListByTable(_tabSetId, _tableName, _idxList, _btreeList, _keyList, _checkList);
    
	_pOE = _idxList.First();
	
	_idxMatch = CegoAttrCond::INAPP;
	
	int strength = 0;

	// first check for AVL index objects
	
	while ( _pOE && _idxMatch != CegoAttrCond::FULL )
	{	    
	    if (_pOE->getType() == CegoObject::PINDEX
		|| _pOE->getType() == CegoObject::INDEX
		|| _pOE->getType() == CegoObject::UINDEX )
	    {

#ifdef CGDEBUG
		_pLogger->log(_modId, Logger::DEBUG, Chain("Checking index ") + _pOE->getName());
#endif

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

		if ( indexMatch == CegoAttrCond::FULL || indexMatch == CegoAttrCond::PART )
		{

		    CegoAttrCond checkCond = attrCond.getIndexCond(_pOE->getSchema());

		    if ( checkCond.getStrength() > strength )
		    {

			strength = checkCond.getStrength();
#ifdef CGDEBUG
			_pLogger->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" tops for ") + checkCond.toChain());
#endif

			_attrCond = checkCond; 
			_attrCond.setIdxSchema(_pOE->getSchema());
			
			_idxName = _pOE->getName();
#ifdef CGDEBUG
			_pLogger->log(_modId, Logger::DEBUG, Chain("Using index ") + _idxName );
#endif
			
			_idxSchema = _pOE->getSchema();
			_type = _pOE->getType();
			_idxMatch = indexMatch;
		    }
		    else
		    {
#ifdef CGDEBUG
			_pLogger->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" to weak for ") + checkCond.toChain());
#endif
		    }
		}
	    }
	    _pOE = _idxList.Next();
	}
	
	if ( _idxMatch == CegoAttrCond::INAPP )
	{
	    // now check for btree index objects

	    _pBTO = _btreeList.First();
	
	    strength = 0;
	    	    
	    while ( _pBTO && _idxMatch != CegoAttrCond::FULL )
	    {	    
		if (_pBTO->getType() == CegoObject::PBTREE
		    || _pBTO->getType() == CegoObject::BTREE
		    || _pBTO->getType() == CegoObject::UBTREE )
		{
		    
#ifdef CGDEBUG
		    _pLogger->log(_modId, Logger::DEBUG, Chain("Checking btree ") + _pBTO->getName());
#endif

		    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();
#ifdef CGDEBUG
			    _pLogger->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
			    _pLogger->log(_modId, Logger::DEBUG, Chain("Using btree ") + _idxName );
#endif
			
			    _idxSchema = _pBTO->getSchema();
			    _type = _pBTO->getType();
			    _idxMatch = indexMatch;
			}
			else
			{
#ifdef CGDEBUG
			    _pLogger->log(_modId, Logger::DEBUG, Chain("Strength ") + Chain(strength) + Chain(" to weak for ") + checkCond.toChain());
#endif
			}
		    }
		}
		_pBTO = _btreeList.Next();
	    }
	}
    }
    else
    {
#ifdef CGDEBUG
	_pLogger->log(_modId, Logger::DEBUG, Chain("Tablecursor already set up "));
#endif
    }
    // Setting up attribute condition to actual field values


#ifdef CGDEBUG    

    if ( _idxMatch == CegoAttrCond::FULL )
    {
	_pLogger->log(_modId, Logger::DEBUG, Chain("Index classified as full qualified"));
    }
    else if ( _idxMatch == CegoAttrCond::PART )
    {
	_pLogger->log(_modId, Logger::DEBUG, Chain("Index classified as part qualified"));
    }
    else
	_pLogger->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::INDEX 
	     || _type == CegoObject::UINDEX 
	     || _type == CegoObject::PINDEX )
	{
	    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);
	    }
	    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);
	}

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

	if ( pc && len > 0 )
	{

	    bool useEntry=true;
	    
	    int tid;
	    int tastep;
	    CegoTupleState ts;
	    memcpy(&tid, pc, sizeof(int));

	    char* pctastep = pc + sizeof(int);
	    memcpy(&tastep, pctastep, sizeof(int));

	    char* pcts = pc + sizeof(int) + sizeof(int);
	    memcpy(&ts, pcts, sizeof(CegoTupleState));
	    
	    if ( tid != 0  )
	    {
		if ( _ignoreTouched == true )
		{
		    if ( ts == INSERTED 
			 && tid == _pTM->getTID(_tabSetId) 
			 && tastep < _pTM->getTAStep(_tabSetId) ) 

		    {
			CegoTupleState ts;
			_qh.decodeFVL(fl, pc, len, tid, tastep, ts);
			return true;
		    }
		    else
		    {
			return getNext(fl, dp);
		    }
		}
		else
		{
		    if ( ( _readUncommitted == true 
			   && ts == INSERTED )
			 || ( _readUncommitted == false 
			      && (( ts == INSERTED && tid == _pTM->getTID(_tabSetId)) 
				  || ( ts == DELETED && tid != _pTM->getTID(_tabSetId)))))
		    {
			CegoTupleState ts;
			_qh.decodeFVL(fl, pc, len, tid, tastep, ts);
			return true;
		    }
		    else
		    {
			return getNext(fl, dp);
		    }		
		}
	    }


	    _qh.decodeFVL(fl, pc, len, tid, tastep, ts);

	    return true;
	}
	return false;
    }
}

bool CegoTableCursor::getNext(ListT<CegoField>& fl, CegoDataPointer& dp)
{

    if ( _idxMatch == CegoAttrCond::FULL || _idxMatch == CegoAttrCond::PART  )
    {

	if ( _type == CegoObject::INDEX 
	     || _type == CegoObject::UINDEX 
	     || _type == CegoObject::PINDEX )
	{

	    return _pIC->getNext(fl, dp);
	}
	else if ( _type == CegoObject::BTREE 
	     || _type == CegoObject::UBTREE
	     || _type == CegoObject::PBTREE )
	{
	    return _pBTC->getNext(fl, dp);
	}
	else
	{
	    throw Exception(EXLOC, Chain("Invalid index type"));
	}
    }
    else
    {

	if ( _pC == 0 )
	{
	    throw Exception(EXLOC, "Invalid ObjectCursor");
	}
	
	do {
	    
	    int len;

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

	    if ( pc && len > 0 )
	    {

		bool useEntry=true;
		
		int tid;
		int tastep;

		CegoTupleState ts;

		memcpy(&tid, pc, sizeof(int));

		char* pctastep = pc + sizeof(int);
		memcpy(&tastep, pctastep, sizeof(int));

		char* pcts = pc + sizeof(int) + sizeof(int);
		memcpy(&ts, pcts, sizeof(CegoTupleState));


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

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

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






