///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoObjectCursor.cc
// -------------------
// Cego object cursor class implementation
//      
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoObjectCursor
// 
// Description: Basic cursor to trace on content objects
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

// CEGO INCLUDES
#include "CegoDefs.h"
#include "CegoObjectCursor.h"

CegoObjectCursor::CegoObjectCursor()
{
}

CegoObjectCursor::CegoObjectCursor(CegoBufferPool *pBufPool, CegoLockHandler *pLockHandle, 
				   unsigned tabSetId, 
				   CegoObject::ObjectType type, 
				   PageIdType pageId)
{
    _pBufPool = pBufPool;
    _pLockHandle = pLockHandle;
    _tabSetId = tabSetId;    
    _pageId = pageId;   
    _startPageId = pageId;
    _type = type;
    _lockId = 0;
    _recLock = 0;
    _isEOC = ( pageId == 0 );
    _lastPageSync = false;
    _modId = _pBufPool->getModId("CegoObjectCursor");
}

CegoObjectCursor::~CegoObjectCursor()
{
    abort();
}

unsigned CegoObjectCursor::getTabSetId()
{
    return _tabSetId;
}

void CegoObjectCursor::setLastPageSync(bool doSync)
{
    _lastPageSync = doSync;
}

void* CegoObjectCursor::getFirst(unsigned& len, CegoDataPointer& dp)
{
    if ( _isEOC )
	return 0;
    
    try
    {	
	_pBufPool->bufferFix(_bp, _tabSetId, _pageId, CegoBufferPool::SYNC, _pLockHandle);
	
	try
	{
	    _lockId = _pLockHandle->lockData(_type, _pageId, CegoLockHandler::READ);
	}
	catch ( Exception e)
	{
	    Chain msg;
	    e.pop(msg);
	    
	    _lockId = 0;
	    
	    _pBufPool->log(_modId, Logger::LOGERR, Chain("Data page lock error for pageId=") + Chain(_pageId) + Chain(" : ") +  msg);		
	    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
	    throw Exception(EXLOC, msg);
	}
	
	if ( void* p = _bp.getFirstEntry() )
	{
	    dp.setPageId(_pageId);
	    dp.setOffset(_bp.getEntryPos());	    
	    len = _bp.getEntryLen();
	    return p;
	}
	else
	{	
	    _pageId = _bp.getNextPageId();
	    
	    // keep last page in sync ?
	    if ( ! ( _lastPageSync && _pageId == 0 ) )
	    {
		_pLockHandle->unlockData(_type, _lockId);    
		_lockId = 0;
	    }
	   	   
	    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
	    
	    while ( ! ( _pageId == 0 ) )
	    {		
		CegoBufferPage nbp;						
		
		_pBufPool->bufferFix(nbp, _tabSetId, _pageId, CegoBufferPool::SYNC, _pLockHandle);
		
		_bp = nbp;
		
		try
		{
		    _lockId = _pLockHandle->lockData(_type, _pageId, CegoLockHandler::READ);
		}
		catch ( Exception e)
		{
		    _lockId = 0;
		    
		    Chain msg;
		    e.pop(msg);			    
		    _pBufPool->log(_modId, Logger::LOGERR, Chain("Data page lock error for pageId=") + Chain(_pageId) + Chain(" : ") +  msg);		
		    	    
		    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
		    
		    throw Exception(EXLOC, Chain("Data page lock error"), e);
		}
		
		if ( void* p = _bp.getFirstEntry())
		{		    
		    dp.setPageId(_pageId);
		    dp.setOffset(_bp.getEntryPos());		    
		    len = _bp.getEntryLen();
		    return p;
		}
		else
		{
		    _pageId = _bp.getNextPageId();

		    // keep last page in sync ?
		    if ( ! ( _lastPageSync && _pageId == 0 ) )
		    {
			_pLockHandle->unlockData(_type, _lockId);    
			_lockId = 0;
		    }
		    		   		    
		    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
		}
	    }
	    _isEOC = true;
	    return 0;
	} 
    }
    catch ( Exception e)
    {
	abort();
	throw Exception(EXLOC, Chain("Cannot get first entry"), e);
    }
}

void* CegoObjectCursor::getNext(unsigned& len, CegoDataPointer& dp)
{
    if ( _isEOC )
	return 0;
    
    try
    {
	void* p = _bp.getNextEntry();    
	if ( p )
	{
	    dp.setPageId(_pageId);	    
	    dp.setOffset(_bp.getEntryPos());	    
	    len = _bp.getEntryLen();
	    return p;
	}
	else
	{
	    _pageId = _bp.getNextPageId();

	    // keep last page in sync ?
	    if ( ! ( _lastPageSync && _pageId == 0 ) )
	    {
		_pLockHandle->unlockData(_type, _lockId);    
		_lockId = 0;
	    }
	    
	    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
	    
	    while ( ! ( _pageId == 0 ) )
	    {	
		CegoBufferPage nbp;
		_pBufPool->bufferFix(nbp, _tabSetId, _pageId, CegoBufferPool::SYNC, _pLockHandle);
		
		_bp = nbp;
		
		try
		{
		    _lockId = _pLockHandle->lockData(_type, _pageId, CegoLockHandler::READ);
		}
		catch ( Exception e )
		{
		    _lockId = 0;
		    
		    Chain msg;
		    e.pop(msg);
		    _pBufPool->log(_modId, Logger::LOGERR, Chain("Data page lock error for pageId=") + Chain(_pageId) + Chain(" : ") +  msg);			    
		    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);

		    throw Exception(EXLOC, Chain("Data page lock error"), e);
		    
		}
		
		if ( void* p = _bp.getFirstEntry())
		{	    
		    dp.setPageId(_pageId);
		    dp.setOffset(_bp.getEntryPos());		    
		    len = _bp.getEntryLen();
		    return p;
		}
		else
		{
		    _pageId = _bp.getNextPageId();
		    
		    // keep last page in sync ?
		    if ( ! ( _lastPageSync && _pageId == 0 ) )
		    {
			_pLockHandle->unlockData(_type, _lockId);    
			_lockId = 0;
		    }
		    
		    _pBufPool->bufferUnfix(_bp, false, _pLockHandle);
		}
	    }
	    _isEOC = true;	
	    return 0;
	}
    }	
    catch ( Exception e)
    {	    	    
	abort();
	throw Exception(EXLOC, Chain("Cannot get next entry"), e);
    }
}

void CegoObjectCursor::abort()
{
    if (_bp.isFixed())
    {
	_pBufPool->bufferUnfix(_bp, false, _pLockHandle);
    }
    if (_lockId > 0 )
    {
	_pLockHandle->unlockData(_type, _lockId);
	_lockId = 0;
    }
    
    _isEOC = true;
}

void CegoObjectCursor::reset()
{
    abort();
    _isEOC = false;
    _pageId = _startPageId;
}
