///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoAVLIndexCursor.cc
// ------------------
// Cego index cursor class implementation
//         
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2013 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: CegoAVLIndexCursor
// 
// Description: Index cursors are used for reading tables based on an available index 
//              for the given attribute condition
//
// Status: QG-2.13
//
///////////////////////////////////////////////////////////////////////////////

// cego includes
#include "CegoAVLIndexCursor.h"
#include "CegoTupleState.h"

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

CegoAVLIndexCursor::CegoAVLIndexCursor()
{
}

CegoAVLIndexCursor::CegoAVLIndexCursor(CegoTableManager *pTM, int tabSetId, const Chain& indexName, CegoObject::ObjectType type, CegoAttrCond* pAttrCond, bool ignoreTouched, bool readUncommitted)
{
    _pTM = pTM;
    _indexName = indexName;
    _type = type;
    _pAttrCond = pAttrCond;

    _lockId = 0;
    _rdpLockId = 0;
    _dataLock = 0;

    _tabSetId = tabSetId;
    _ignoreTouched = ignoreTouched;
    _readUncommitted = readUncommitted;
    _cursorCached = false;
}

CegoAVLIndexCursor::~CegoAVLIndexCursor()
{
    abort();
    
    if (_rdpLockId)
    {
	_pTM->releaseDataPtr(_rdpLockId, false);
	_rdpLockId = 0;
    }
}

bool CegoAVLIndexCursor::getFirst(ListT<CegoField>& fl, CegoDataPointer& dp)
{

    if ( fl.isEmpty() )
    {
	throw Exception(EXLOC, "Empty field list");
    }
    
    if ( _cursorCached == false )
    {
	CegoTableObject ioe;
	
	_pTM->getObject(_tabSetId, _indexName, _type, ioe);

	_idxSchema = ioe.getSchema();
	
	Chain tabName = ioe.getTabName();
	
	CegoObjectCursor* pC = _pTM->getObjectCursor(_tabSetId, tabName, _indexName, _type);

	_cachedPointer = (char*)pC->getFirst(_cachedLen, _rdp);
	pC->abort();
	delete pC;
	
	_rdpLockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _rdp, _cachedPointer, _cachedLen);	
             
	_cursorCached = true;

    }

    // get root entry
    int len = _cachedLen;
    char* p = _cachedPointer;

    if ( p == 0 )
    {
	_eoc = true;
	return false;
    }
    
    _ie.setPtr(p, len);

    _idp = _ie.getRightBranch();

    CegoDataPointer nil;    
    if (_idp == nil)
    {
	_eoc = true;
	return false;
    }

    _eoc = false;

    _rootPassed = false;

    bool found = false;

    if (_pAttrCond)
    { 

	if ( _pAttrCond->getPrimaryCompMode() == CegoAttrComp::BTWN )
	    _pAttrCond->setPrimaryComparison( MORE_EQUAL_THAN );

	switch ( _pAttrCond->getPrimaryComparison() )
	{
	case NOT_EQUAL:
	case LESS_THAN:
	case LESS_EQUAL_THAN:
	    
	{
	    // go to the beginning

	    _lockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);
	    
	    _ie.setPtr(p, len);
	    
	    while ( _ie.getLeftBranch() != nil)
	    {

		// Traversing to left
		_idp = _ie.getLeftBranch();		

		_pTM->releaseDataPtr(_lockId, false);
		_lockId = 0;

		_lockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);
		
		_ie.setPtr(p, len);
	    }
	    
	    CegoComparison comp = compValue(_ie.getIdxPtr());
	    
	    if ( comp == LESS_THAN
		 || ( comp == EQUAL && _pAttrCond->getPrimaryComparison() == LESS_EQUAL_THAN )
		 || ( comp != EQUAL && _pAttrCond->getPrimaryComparison() == NOT_EQUAL ) )
	    {	       
		found = true;
	    }
	    else if ( _pAttrCond->getPrimaryComparison() == NOT_EQUAL )
	    {
		return getNext(fl, dp);
	    }
	    	    
	    break;
	}
	case MORE_THAN:
	{

	    // go to the smallest leaf

	    _lockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);  

	    _ie.setPtr(p, len);
	    
	    while ( ! found )
	    {

		CegoDataPointer idp;
		
		CegoComparison comp = compValue( _ie.getIdxPtr());
				
		if ( comp == LESS_THAN || comp == EQUAL ) 
		{
		    idp = _ie.getRightBranch();				

		    if (_ie.getParent() == _rdp )
		    {
			_rootPassed = true;
		    }
 
		}
		else if ( comp == MORE_THAN )
		{
		    idp = _ie.getLeftBranch();
		    
		    if ( idp == nil )
			found = true;
		}
		else
		{
		    return getNext(fl, dp);
		}
		

		if ( idp == nil && ! found )    
		{
		    return getNext(fl, dp);
		}
		else if ( ! found )
		{
		    _idp = idp;
		    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, idp, p, len);
		    _ie.setPtr(p, len);
		}
	    }
	    break;	    
	}
	case EQUAL:
	case MORE_EQUAL_THAN:
	{
	    
	    // go to the smallest leaf

	    _lockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len); 

	    _ie.setPtr(p, len);

	    while ( ! found )
	    {

		CegoDataPointer idp = _idp;
		
		CegoComparison comp = compValue(_ie.getIdxPtr()); 
				
		if ( comp == LESS_THAN ) 
		{
		    idp = _ie.getRightBranch();				

		    if (_ie.getParent() == _rdp )
		    {
			_rootPassed = true;
		    } 
		}
		else if ( comp == EQUAL ) 
		{
		    idp = _ie.getLeftBranch();

		    if ( idp == nil )
			found = true;
		} 
		else if ( comp == MORE_THAN ) 
		{		  		    
		    idp = _ie.getLeftBranch();		    
		}
				
		if ( idp == nil && ! found )    
		{
		    return getNext(fl, dp);
		}
		else if ( ! found )
		{
		    _idp = idp;
		    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, idp, p, len);		    
		    _ie.setPtr(p, len);

		}
	    }
	    break;	    
	}

	}
    }
    else
    {
	// go to the beginning
	
	_lockId = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);
	
	_ie.setPtr(p, len);
	
	while ( _ie.getLeftBranch() != nil)
	{	    
	    _idp = _ie.getLeftBranch();			   
	    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);	    
	    _ie.setPtr(p, len);
	}
	    	
	found = true;
    }

	
    if (found )
    {

	char* p;
	int len;
	
	dp = _ie.getData();


	if (_dataLock)
	{
	    _pTM->releaseDataPtr(_dataLock, false);
	    _dataLock = 0;
	}
       
	_dataLock = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, dp, p, len);
	
	// skipping tid
	
	int tid;
	int tastep;
	CegoTupleState ts;

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

	char* ptastep = p + sizeof(int);
	memcpy(&tastep, ptastep, sizeof(int));

	char* pts = p + sizeof(int) + sizeof(int);
	memcpy(&ts, pts, sizeof(CegoTupleState));
	
	if (tid != 0)
	{
	    if ( _ignoreTouched )
	    {			
		if ( ts == INSERTED 
		     && tid == _pTM->getTID(_tabSetId) 
		     && tastep < _pTM->getTAStep(_tabSetId) )
		{
		    CegoTupleState ts;
		    _qh.decodeFVL(fl, p, 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)))))
		{
		    _qh.decodeFVL(fl, p, len, tid, tastep, ts);
		    return true;   
		}
		else
		{
		    return getNext(fl,dp);		    
		}
	    }
	}
	else
	{
	    _qh.decodeFVL(fl, p, len, tid, tastep, ts);
	    return true;
	}
    }
    return false;
}

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

    if ( _eoc )
	return false;

    if ( fl.isEmpty() )
    {
	throw Exception(EXLOC, "Empty field list");
    }

    do {

	CegoDataPointer nil;
	
	char* p;
	int len;    

	int tid;

	bool rbFound=false;
	bool nativeFound=false;
	
	bool attrMatch = false;

	if (_ie.getParent() == _rdp )
	{
	    _rootPassed = true;
	}
	
	if (_ie.getRightBranch() != nil) 
	{
	    
	    _idp = _ie.getRightBranch();	   
	    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);

	    
	    _ie.setPtr(p, len);
	    
	    while ( _ie.getLeftBranch() != nil)
	    {
		_idp = _ie.getLeftBranch();		
		_lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);		
		_ie.setPtr(p, len);
	    }	
	}
	else
	{
	    // searching appropriate parent
	    
	    if ( _ie.getParent() == _rdp &&  _rootPassed )
	    {
		if (_lockId)
		{
		    _pTM->releaseDataPtr(_lockId, false);
		    _lockId=0;
		}
		_eoc = true;
		return false;
	    }
	    
	    CegoDataPointer pdp = _ie.getParent();
	    
	    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, pdp, p, len);			    

	    _ie.setPtr(p, len);
	    
	    bool entryFound = false;
	    while ( ! entryFound )
	    {
		
		if (_ie.getLeftBranch() == _idp)
		{
		    _idp = pdp;
		    entryFound = true;
		}
		else
		{
		    
		    if ( _ie.getParent() == _rdp )
		    {
			if ( _rootPassed )
			{
			    abort();
			    _eoc = true;
			    return false;
			}
			else
			{
			    
			    _idp = _ie.getRightBranch();
			    
			    _lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);			    			    

			    _ie.setPtr(p, len);
			    
			    while ( _ie.getLeftBranch() != nil)
			    {
				
				_idp = _ie.getLeftBranch();
								
				_lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, _idp, p, len);			    			    

				_ie.setPtr(p, len);
			    }
			    entryFound = true;
			}
		    }
		    else
		    {
			_idp = pdp;
			
			pdp = _ie.getParent();
			
			_lockId = _pTM->releaseAndClaimDataPtr(_lockId, false, _tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, pdp, p, len);			    			    			

			_ie.setPtr(p, len);
			
		    }
		}
	    }	    
	}
        
	if (_pAttrCond)
	{

	    char *p = _ie.getIdxPtr();
	    CegoComparison comp;
	    comp = compValue(p);

	    if ( _pAttrCond->getPrimaryCompMode() == CegoAttrComp::BTWN )
	    {
		_pAttrCond->setPrimaryComparison(LESS_EQUAL_THAN);
	    }
	  	  
	    switch (_pAttrCond->getPrimaryComparison())
	    {
		
	    case EQUAL:
	    {	  
		switch (comp)
		{
		case EQUAL:
		    attrMatch=true;
		    break;
		default:
		    break;
		}
		break;
	    }
	    case NOT_EQUAL:
	    {
		switch (comp)
		{
		case LESS_THAN:
		    attrMatch=true;
		    break;
		case MORE_THAN:
		    attrMatch=true;
		    break;
		case EQUAL:
		    return getNext(fl, dp);
		default:
		    break;
		}
		break;
	    }
	    case LESS_THAN:
	    {		
		switch (comp)
		{
		case LESS_THAN:
		    attrMatch=true;
		    break;
		default:
		    break;
		}
		break;
	    }
	    case MORE_THAN:	     
	    {
		switch (comp)
		{
		case MORE_THAN:
		    attrMatch=true;
		    break;
		default:
		    break;		
		}	
		break;       
	    }
	    case LESS_EQUAL_THAN:
	    {
		switch (comp)
		{
		case EQUAL:
		    attrMatch=true;
		    break;
		case LESS_THAN:
		    attrMatch=true;
		    break;
		default:
		    break;
		}
		break;
	    }
	    
	    case MORE_EQUAL_THAN:
	    {
		switch (comp)
		{
		case EQUAL:
		    attrMatch=true;
		    break;
		case MORE_THAN:
		    attrMatch=true;
		    break;
		default:
		    break;
		}
		break;
	    }
	    }	    
	}
	else
	{
	    attrMatch = true;
	}
	
	if ( attrMatch )
	{
	    
	    char* p;
	    int len;
	    	    
	    dp = _ie.getData();
	    

	    if (_dataLock)
	    {
		_pTM->releaseDataPtr(_dataLock, false);
		_dataLock = 0;
	    }
	    _dataLock = _pTM->claimDataPtr(_tabSetId, CegoLockHandler::READ, CegoBufferPool::SYNC, dp, p, len);
	    
	    // skipping tid
	    
	    int tid;
	    int tastep;
	    CegoTupleState ts;
	    memcpy(&tid, p, sizeof(int));

	    char* ptastep = p + sizeof(int);
	    memcpy(&tastep, ptastep, sizeof(int));

	    char* pts = p + sizeof(int) + sizeof(int);
	    memcpy(&ts, pts, sizeof(CegoTupleState));
	    
	    if (tid != 0)
	    {
		if ( _ignoreTouched == true )
		{
		    if ( ts == INSERTED 
			 && tid == _pTM->getTID(_tabSetId) 
			 && tastep < _pTM->getTAStep(_tabSetId) )
		    {
			CegoTupleState ts;
			_qh.decodeFVL(fl, p, len, tid, tastep, ts);
			return true;   
		    }
		    else
		    {		
			// ignore entry
		    }
		}
		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, p, len, tid, tastep, ts);
			return true;
		    }
		    else
		    {
			// ignore entry
		    }
		}
	    }
	    else
	    {
		
		CegoTupleState ts;
		_qh.decodeFVL(fl, p, len, tid, tastep, ts);
		return true;
	    }
	}
	else
	{
	    abort();
	    _eoc = true;
	    return false;
	}
    } while (1);
}

void CegoAVLIndexCursor::abort()
{
    if (_lockId)
    {
	_pTM->releaseDataPtr(_lockId, false);
	_lockId = 0;
    }

    if (_dataLock)
    {
	_pTM->releaseDataPtr(_dataLock, false);
	_dataLock = 0;
    }

    
}

void CegoAVLIndexCursor::reset()
{
    abort();
}

CegoComparison CegoAVLIndexCursor::compValue(char* idxVal)
{

    CegoField* pF = _idxSchema.First();
	     
    while ( pF ) 
    {

	int flen;
	memcpy(&flen, idxVal, sizeof(int));
	
	idxVal += sizeof(int);
	
	CegoFieldValue fv;
	fv.setLength(flen);
	fv.setValue(idxVal);	
	if ( flen > 0 )
	    fv.setType(pF->getType());

	CegoAttrComp* pAC = _pAttrCond->getAttrCompSet().First();

	while ( pAC )
	{
	    if  ( (Chain)pAC->getAttrName() == (Chain)pF->getAttrName()  )
	    {	       
		if ( fv < pAC->getFieldValue() )
		{
		    return LESS_THAN;
		}
		
		if ( pAC->getCompMode() == CegoAttrComp::VAL
		     || pAC->getCompMode() == CegoAttrComp::ATTR )
		{
		    if ( fv > pAC->getFieldValue() )
		    {
			return MORE_THAN;
		    }
		}
		else if ( pAC->getCompMode() == CegoAttrComp::BTWN )
		{
		    if ( fv > pAC->getFieldValue2() )
		    {
			return MORE_THAN;
		    }
		}		
	    }
	    pAC = _pAttrCond->getAttrCompSet().Next();
	}		

	idxVal += flen;

	pF = _idxSchema.Next();

    }

    return EQUAL;

}

