///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBTreeNode.cc
// -----------------
// Cego btree node class implementation
//     
// Design and Implementation by Bjoern Lemke               
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoBTreeNode
// 
// Description: This class handles operations on btree elements.
//              These elements either can be nodes or leafs and depending on the type, the corresponding methods are available.
//
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

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

// CEGO INCLUDES
#include "CegoBTreeNode.h"
#include "CegoDataPointer.h"
#include "CegoTupleState.h"
#include "CegoQueryHelper.h"

// POSIX INCLUDES
#include <string.h>
#include <stdlib.h>

// must match the value of method CegoDataPointer.getEncodingLength 
#define DPENCODINGLENGTH CegoDataPointer::getEncodingLength()

CegoBTreeNode::CegoBTreeNode()
{
    _pI = 0;
    _len = 0;
    _keyLen = 0;
    _entrySize = 0;
    
    _maxEntries = 0;
    _pageId = 0;
    _nextPageId = 0;
    _pNextChild = 0;
    _nt = CegoBTreeNode::UNDEFINED;
}

CegoBTreeNode::~CegoBTreeNode()
{
}

const CegoBTreeNode::NodeType CegoBTreeNode::getType() const
{
    return _nt;
}

void CegoBTreeNode::setPtr(void* p, unsigned len)
{
    _pI = p;
    _len = len;
}

void* CegoBTreeNode::getPtr()
{
    return _pI;
}

unsigned CegoBTreeNode::getLen() const
{
    return _len;
}

void CegoBTreeNode::setPageId(PageIdType pageId)
{
    _pageId = pageId;
}

PageIdType CegoBTreeNode::getPageId() const
{
    return _pageId;
}

void CegoBTreeNode::setNextPageId(PageIdType pageId)
{
    _nextPageId = pageId;
}

PageIdType CegoBTreeNode::getNextPageId() const
{
    return _nextPageId;
}

void CegoBTreeNode::initNode()
{
    if ( _pI )
    {
	(*(unsigned*)(_pI)) = 0;
	return;
    }
    throw Exception(EXLOC, Chain("Btree node not set up"));
}

unsigned CegoBTreeNode::numEntries() const
{
    if ( _pI )
    {
	return (*(unsigned*)(_pI));
    }
    throw Exception(EXLOC, Chain("Btree node not set up"));
}

void CegoBTreeNode::incEntries()
{
    if ( _pI )
    {
	(*(unsigned*)(_pI))++;
	return;
    }
    throw Exception(EXLOC, Chain("Btree node not set up"));
}

void CegoBTreeNode::decEntries()
{
    if ( _pI )
    {
	(*(unsigned*)(_pI))--;
	return;
    }
    throw Exception(EXLOC, Chain("Btree node not set up"));
}

unsigned CegoBTreeNode::getEntrySize() const
{
    return _entrySize;
}

void CegoBTreeNode::getChildPage(const CegoBTreeValue& iv, PageIdType& pageId, bool traceMin)
{

    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    unsigned ne = numEntries();

    if ( ne == 0 )
	throw Exception(EXLOC, "No child entries avaiable");

    unsigned lb=0;
    unsigned rb=ne;

    unsigned i;
    bool posFound=false;
    
    while ( posFound == false)
    {
	i = (lb+rb) / 2;

	char* pKey = (char*)((long long)_pI + sizeof(unsigned)) + sizeof(PageIdType) + i * getEntrySize();
	CegoBTreeValue nv;
	nv.setPtr(pKey, _keyLen);

	// cout << "getChildPage : Comparing " << iv.toChain(_pSchema) << " and " << nv.toChain(_pSchema) << " i=" << i << " ne=" << ne << endl;

	bool traceMatch;
	if ( traceMin )
	    traceMatch = iv.isHigher(nv, _pSchema);
	else
	    traceMatch = iv.isEqualHigher(nv, _pSchema);
	
	if ( traceMatch )
	{
	    lb=i;
	}
	else
	{
	    rb=i;	    
	}
	
	if ( rb - lb <= 1 )
	{
	    if ( lb != rb )
	    {
		// cout << "Checking left value .." << endl;
		// we still have to check the left value
		char* pLeftKey = (char*)((long long)_pI + sizeof(unsigned)) + sizeof(PageIdType) + lb * getEntrySize();
		CegoBTreeValue lv;
		lv.setPtr(pLeftKey, _keyLen);
		
		if ( iv.isHigher(lv, _pSchema) )
		{	    
		    i=rb;
		}
		else
		{
		    i=lb;
		}
	    }
	    else
	    {
		// evaluation can be skipped
		i=rb;
	    }
	    posFound=true;
	}      
    }

    char* p = (char*)((long long)_pI + sizeof(unsigned)) + i * getEntrySize();

    memcpy(&pageId, p, sizeof(PageIdType));    
    return;
}

void CegoBTreeNode::setSchema(NodeType nt, ListT<CegoField>* pSchema, unsigned keyLen)
{    
    _pSchema = pSchema;
    
    _keyLen = keyLen;
    _nt = nt;

    if ( _nt == LEAF )
    {
	_maxEntries = ( _len - sizeof(unsigned) )  / ( _keyLen + DPENCODINGLENGTH );
	_entrySize = _keyLen + DPENCODINGLENGTH;
    }
    else if ( _nt == NODE )
    {	
	_maxEntries = ( _len - sizeof(unsigned) - sizeof(PageIdType) ) / ( _keyLen + sizeof(PageIdType) );
	_entrySize = _keyLen + sizeof(PageIdType);
    }
    else
    {
	throw Exception(EXLOC, Chain("Unknown btree node type"));
    }
}

bool CegoBTreeNode::valueExists(const CegoBTreeValue& iv, CegoObjectManager *pOM, unsigned tabSetId, unsigned long long tid) const
{    
    if ( _nt == NODE )
      throw Exception(EXLOC, Chain("Invalid method for node type"));
    
    unsigned ne = numEntries();
    
    if ( ne == 0 )
    {
	return false;
    }
        
    unsigned lb=0;
    unsigned rb=ne;

    unsigned i = (lb+rb) / 2;
    
    bool posFound=false;

    while ( posFound == false )
    {
	// cout << "lb=" << lb << " rb=" << rb << endl;
	
	i = (lb+rb) / 2;

	// cout << "i=" << i << endl;
	
	// cout << "Checking position " << i << endl;

	CegoBTreeValue nv;
	CegoDataPointer dp;

	nv.setPtr((char*)((long long)_pI + sizeof(unsigned)) + i * getEntrySize(), _keyLen);
	dp.decode((char*)((long long)_pI + sizeof(unsigned)) + i * getEntrySize() + _keyLen);
	
	CegoBTreeValue::Comparison valueComp = iv.comp(nv, _pSchema);
	
	if ( valueComp == CegoBTreeValue::EQUAL )
	{
	    if ( isTupleVisible(pOM, dp, tabSetId, tid) )
	    {
		return true;
	    }
	}

	if ( rb - lb <= 1 )
	{
	    posFound=true;
	}
	else
	{
	    if ( valueComp == CegoBTreeValue::MORE ) 
	    {
		// have we reached end of page ? if so, no more entries to check
		if ( i == ne )
		    posFound = true;
		else
		    lb=i;
	    }
	    else if ( valueComp == CegoBTreeValue::LESS )
	    {	   
		// have we beginning of page ? if so, no more entries to check
		if ( i == 0 )
		    posFound = true;
		else
		    rb=i;
	    }
	    else // if ( valueComp == CegoBTreeValue::EQUAL ) 
	    {

		// cout << "Searching down and up .." << endl;
		// if comparison is equal, we have to look downstairs and upstairs for
		// other equal and check for visibility
		
		// search down
		
		int d = i-1;
		bool goOn = true;
		while ( d >= 0 && goOn )
		{
		    // cout << "Searching down " << d << endl;
		    nv.setPtr((char*)((long long)_pI + sizeof(unsigned)) + (d)*getEntrySize(), _keyLen);
		    dp.decode((char*)((long long)_pI + sizeof(unsigned)) + (d)*getEntrySize() + _keyLen);

		    CegoBTreeValue::Comparison downComp = iv.comp(nv, _pSchema);

		    if ( downComp == CegoBTreeValue::EQUAL )
		    {
			if ( isTupleVisible(pOM, dp, tabSetId, tid) )
			{
			    return true;
			}
		    }
		    else
		    {
			goOn = false;
		    }
		    
		    d--;
		}

		// search up
		
		unsigned u = i+1;
		goOn = true;
		while ( u < ne && goOn )
		{
		    // cout << "Searching up " << u << endl;
		    nv.setPtr((char*)((long long)_pI + sizeof(unsigned)) + (u)*getEntrySize(), _keyLen);
		    dp.decode((char*)((long long)_pI + sizeof(unsigned)) + (u)*getEntrySize() + _keyLen);

		    CegoBTreeValue::Comparison upComp = iv.comp(nv, _pSchema);

		    if ( upComp == CegoBTreeValue::EQUAL )
		    {
			if ( isTupleVisible(pOM, dp, tabSetId, tid) )
			{
			    return true;
			}
		    }
		    else
		    {
			goOn = false;
		    }
		    u++;
		}

		return false;
	    }
	}
    }
    return false;
}

bool CegoBTreeNode::addValue(const CegoBTreeValue& iv, const CegoDataPointer& dp)
{    
    if ( _nt == NODE )
      throw Exception(EXLOC, Chain("Invalid method for node type"));
    
    unsigned ne = numEntries();
    
    if ( ne == _maxEntries )
    {
	return false;
    }

    if ( ne == 0 )
    {
	char* pKey = (char*)((long long)_pI + sizeof(unsigned));
	memcpy(pKey, iv.getPtr(), _keyLen);
	char* pDP = (char*)((long long)pKey + _keyLen);
	dp.encode(pDP);
	
	incEntries();

	return true;	
    }
        
    unsigned lb=0;
    unsigned rb=ne;

    unsigned i = (lb+rb) / 2;
    
    bool posFound=false;

    while ( posFound == false )
    {
	i = (lb+rb) / 2;

	CegoBTreeValue nv1;
	CegoBTreeValue nv2;

	CegoDataPointer dp1;
	CegoDataPointer dp2;

	nv1.setPtr((char*)((long long)_pI + sizeof(unsigned)) + i * getEntrySize(), _keyLen);
	dp1.decode((char*)((long long)_pI + sizeof(unsigned)) + i * getEntrySize() + _keyLen);

	CegoBTreeValue::Comparison valueComp1 = iv.comp(nv1, _pSchema);
	CegoBTreeValue::Comparison valueComp2;
	
	if ( i+1 == ne )
	{
	    valueComp2 = CegoBTreeValue::LESS;
	}
	else
	{
	    nv2.setPtr((char*)((long long)_pI + sizeof(unsigned)) + (i+1)*getEntrySize(), _keyLen);
	    dp2.decode((char*)((long long)_pI + sizeof(unsigned)) + (i+1)*getEntrySize() + _keyLen);

	    valueComp2 = iv.comp(nv2, _pSchema);
	}
	
	if ( ( valueComp1 == CegoBTreeValue::MORE || valueComp1 == CegoBTreeValue::EQUAL )
	     && ( valueComp2 == CegoBTreeValue::LESS || valueComp2 == CegoBTreeValue::EQUAL ) )
	{
	    i++;
	    posFound=true;
	}
	else if ( valueComp2 == CegoBTreeValue::MORE )
	{
	    if ( i == ne )
		posFound = true;
	    else
		lb=i;	    	    	    
	}
	else
	{
	    if ( i == 0 )
		posFound = true;
	    else
		rb=i;
	}
    }

    if ( i < ne )
    {
	shiftEntries(i, 1);
    }

    char* pKey = (char*)((long long)_pI + sizeof(unsigned) + i* getEntrySize());    
    memcpy(pKey, iv.getPtr(), _keyLen);
    char* pDP = (char*)((long long)pKey + _keyLen);
    dp.encode(pDP);

    incEntries();

    return true;
}

bool CegoBTreeNode::deleteValue(const CegoBTreeValue& iv, const CegoDataPointer& dp)
{
    if ( _nt == NODE )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    unsigned ne = numEntries();

    if ( ne == 0 )
	return false;

    char* pKey = (char*)((long long)_pI + sizeof(unsigned));

    CegoBTreeValue lv;
    
    unsigned i=0;
    while ( i < ne )
    {
	lv.setPtr(pKey, _keyLen);

	CegoDataPointer ldp;
	ldp.decode(pKey + _keyLen);
	
	if ( lv.isEqual(iv, _pSchema) && ldp == dp)
	{
	    shiftEntries(i+1, -1);	    
	    decEntries();
	    return true; 	    
	}
	
	pKey += getEntrySize();
	i++;
    }
    
    return false;
}

bool CegoBTreeNode::addNode(const CegoBTreeValue& iv, const CegoBTreeNode& node)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));
    
    unsigned ne = numEntries();       	
    if ( ne == _maxEntries || ne == 0 )
    {
	return false;
    }

    bool posFound=false;
    char* p = 0;
    unsigned i=0;	    
    
    while ( i < ne && posFound == false)
    {	
	p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
	CegoBTreeValue nv(p, _keyLen);
	if ( nv.isHigher(iv, _pSchema) )
	{
	    shiftEntries(i, 1);
	    posFound=true;
	}
	else if ( iv.isEqual(nv, _pSchema) )
	{
	    // if equal, we also have to check the maximum value
	    if ( node.getMax().isHigher(nv, _pSchema) )
	    {
		i++;
	    }
	    else
	    {
		shiftEntries(i, 1);
		posFound=true;		    
	    }
	}
	else
	{	    
	    i++;
	}
    }
 
    ////////////////////////////////////
    // Setting up new node information //
    ////////////////////////////////////

    p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
    
    // put index value
    memcpy(p, iv.getPtr(), _keyLen);
    p+=_keyLen;
    
    // put child pointer
    PageIdType pageId = node.getPageId();
    memcpy(p, &pageId, sizeof(PageIdType));
    p+=sizeof(PageIdType);
    
    incEntries();
    
    return true;
}

void CegoBTreeNode::split(CegoBTreeNode& n)
{
    unsigned pos = 0; 

    if ( _nt == LEAF )
    {
	pos = numEntries() / 2;
	char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
	char* destPtr = (char*)((long long)n._pI + sizeof(unsigned));
	unsigned s =  ( numEntries() - pos ) * getEntrySize();

	memcpy(destPtr, srcPtr, s);	
    }
    else if ( _nt == NODE )
    {
	pos = numEntries() / 2;
	char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
	char* destPtr = (char*)((long long)n._pI + sizeof(unsigned));
	unsigned s =  ( numEntries() - pos ) * getEntrySize() + sizeof(PageIdType);

	memcpy(destPtr, srcPtr, s);	
    }
    else
    {
	throw Exception(EXLOC, Chain("Invalid method for undefined node type"));
    }

    unsigned s1 = pos;
    unsigned s2 = numEntries() - pos;
    memcpy(_pI, &s1, sizeof(unsigned));
    memcpy(n._pI, &s2, sizeof(unsigned));
}

// Note : offset might be < 0
void CegoBTreeNode::shiftEntries(unsigned pos, int offset)
{
    if ( _nt == LEAF )
    {
	char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
	char* destPtr = (char*)((long long)_pI + sizeof(unsigned) + ( pos + offset) * getEntrySize());
	unsigned shiftSize = ( numEntries() - pos ) * getEntrySize();

	memmove(destPtr, srcPtr, shiftSize);
    }
    else if ( _nt == NODE )
    {
	char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + pos * getEntrySize());
	char* destPtr = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + ( pos + offset ) * getEntrySize());
	unsigned shiftSize = ( numEntries() - pos ) * getEntrySize();

	memmove(destPtr, srcPtr, shiftSize);
    }
}

CegoBTreeValue CegoBTreeNode::getMin() const
{
    if ( numEntries() == 0 )
	throw Exception(EXLOC, Chain("No values values available in node"));
          
    if ( _nt == LEAF )    
    {
	char* p = (char*)((long long)_pI + sizeof(unsigned));
	CegoBTreeValue iv(p, _keyLen);
	return iv;
    }
    else if ( _nt == NODE )
    {
	char* p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType));
	CegoBTreeValue iv(p, _keyLen);
	return iv;
    }
    else
    {
	throw Exception(EXLOC, Chain("Invalid node type"));
    }
}

CegoBTreeValue CegoBTreeNode::getMax() const
{
    unsigned pos = numEntries() - 1;

    if ( pos >= 0 )
    {
	char* p;
	if ( _nt == LEAF )
	{
	    p = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
	}
	else
	{
	    p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + pos * getEntrySize());
	}
	CegoBTreeValue iv(p, _keyLen);
	return iv;

    }
    throw Exception(EXLOC, "No index values available in node");
}

bool CegoBTreeNode::propagate(const CegoBTreeValue& iv, const CegoBTreeNode& leftNode, const CegoBTreeNode& rightNode)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));
 
    unsigned ne = numEntries();       	
    if ( ne == _maxEntries )
    {
	return false;
    }

    if ( ne == 0 )
    {
	// first entry
	
	char* p = (char*)((long long)_pI + sizeof(unsigned));
	PageIdType pageId, nextPageId;
	
	// put left child pointer
	
	pageId = leftNode.getPageId();
	memcpy(p, &pageId, sizeof(PageIdType));
	p+=sizeof(PageIdType);

	// put index value
	memcpy(p, iv.getPtr(), _keyLen);
	p+=_keyLen;
	
	// put right child pointer
	nextPageId = rightNode.getPageId();
	memcpy(p, &nextPageId, sizeof(PageIdType));
	p+=sizeof(PageIdType);
	
	incEntries();
	
	return true;
    }
    else
    {
	bool posFound=false;
	char* p = 0;
	unsigned i=0;	    

	while ( i < ne && posFound == false)
	{
	    p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
	    CegoBTreeValue nv(p, _keyLen);

	    // cout << "Comparing " << iv.toChain(_pSchema) << " and " << nv.toChain(_pSchema) << "i=" << i << "ne=" << ne << endl;
	    
	    if ( iv.isHigher(nv, _pSchema) )
	    {		
		i++;
	    }
	    else if ( iv.isEqual(nv, _pSchema) )
	    {
		// if equal, we also have to check the maximum value
		if ( rightNode.getMax().isHigher(nv, _pSchema) )
		{
		    i++;
		}
		else
		{
		    shiftEntries(i, 1);
		    posFound=true;		    
		}
	    }
	    else
	    {
		shiftEntries(i, 1);
		posFound=true;
	    }
	}
	
	////////////////////////////////////
	// Setting up new node information //
	////////////////////////////////////

	p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
	
	// put index value
	memcpy(p, iv.getPtr(), _keyLen);
	p+=_keyLen;
	
	// put child pointer
	PageIdType pageId = rightNode.getPageId();
	memcpy(p, &pageId, sizeof(PageIdType));
	p+=sizeof(PageIdType);
	
	incEntries();
	
	return true;
    }
}
 
CegoBTreeNode& CegoBTreeNode::operator = (const CegoBTreeNode& n)
{
    _nt = n._nt;
    _pI = n._pI;
    _len = n._len;
    _pageId = n._pageId;
    _nextPageId = n._nextPageId;
    _keyLen = n._keyLen;
    _entrySize = n._entrySize;
    _maxEntries = n._maxEntries;
    _pSchema = n._pSchema;
    return (*this);
}

bool CegoBTreeNode::verify()
{
    unsigned n = numEntries();

    if ( _nt == LEAF )
    {
        unsigned i = 0;
	while ( i < n )
	{	    
	    char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + i * getEntrySize());
	    CegoBTreeValue iv;
	    iv.setPtr(srcPtr, _keyLen);
	    srcPtr += _keyLen;
	    CegoDataPointer dp;
	    dp.decode(srcPtr);

	    // we check pageId and offset, must not be zero in any case
	    if ( dp.getPageId() == 0 || dp.getOffset() == 0 )
	    {
		return false;
	    }
	    // cout << iv.toChain(_pSchema) << " " << dp << endl;	  
	    i++;
	}
    }
    else if ( _nt == NODE )
    {
	PageIdType pageId;

	char* p = (char*)((long long)_pI + sizeof(unsigned));
	
	memcpy(&pageId, p, sizeof(PageIdType));
			  
	// cout << levelIndent(level) << "Child : " << fileId << "," << pageId << endl;

	// we check fileId, must not be zero in any case
	if ( pageId == 0  )
	{
	    return false;
	}
	
	unsigned i = 0;
	while ( i < n )
	{
	    // cout << levelIndent(level) << "Key " << i << " : ";
	    
	    p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
	    CegoBTreeValue iv;
	    iv.setPtr(p, _keyLen);
	    // cout << iv.toChain(_pSchema) << endl;
	    p += _keyLen;
	    
	    memcpy(&pageId, p, sizeof(PageIdType));

	    // we check fileId, must not be zero in any case
	    if ( pageId == 0 )
	    {
		return false;
	    }
	    
	    // cout << levelIndent(level) << "Child : " << fileId << "," << pageId << endl;

	    i++;
	}
	// cout << levelIndent(level) << "################## " << endl;
    }
    else
    {
	return false;
    }
    
    return true;
}

bool CegoBTreeNode::verifyLeafFull(unsigned tabSetId, CegoObjectManager *pOM)
{
    
    unsigned n = numEntries();

    // we need a sorted schema for decodeFVL
    sortSchema();
    
    if ( _nt == LEAF )
    {
	unsigned i = 0;
	while ( i < n )
	{	    
	    char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + i * getEntrySize());
	    CegoBTreeValue iv;
	    iv.setPtr(srcPtr, _keyLen);
	    
	    srcPtr += _keyLen;
	    CegoDataPointer dp;
	    dp.decode(srcPtr);

	    // we check pageId and offset, must not be zero in any case
	    if ( dp.getPageId() == 0 || dp.getOffset() == 0 )
	    {
		return false;
	    }
	    else
	    {		
		char* p;
		unsigned len;

		CegoBufferPage bp;
		pOM->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, bp);
		
		// skipping tid
		
		unsigned long long tid;
		unsigned long long tastep;
		CegoTupleState ts;

		CegoQueryHelper qh;
		unsigned toff = qh.decodeTupleHeader(tid, tastep, ts, p);
		
		char* tp = p + toff;
		unsigned tlen = len - toff;

		// since index attributes may not be ordered regarding field id, we have to sort

		qh.decodeFVL(_sortFL, tp, tlen);

		Chain chainDump = iv.toChain(_pSchema);
			    
		ListT<CegoFieldValue> idxFVL = iv.valueToFVL(*_pSchema);

		CegoField *pF = _pSchema->First();
		CegoFieldValue *pIV = idxFVL.First();

		bool mismatchDetected = false;
		while ( pF && pIV )
		{

		    // get the appropriate value from sorted list
		    CegoField *pSortField = _sortFL.Find(*pF);
		    
		    if ( *pIV != pSortField->getValue() )
		    {
			mismatchDetected = true;
		    }
					    
		    pF = _pSchema->Next();
		    pIV = idxFVL.Next();
		}
		    
		pOM->releaseDataPtrUnlocked(bp, false);

		if ( mismatchDetected )
		{
		    return false;
		}
	    }

	    i++;
	}

	return true;
    }
    else
    {
	throw Exception(EXLOC, Chain("Invalid node type"));
    }
}

void CegoBTreeNode::dump(Chain& chainDump, unsigned level)
{
    unsigned n = numEntries();

    if ( _nt == LEAF )
    {
	// cout << levelIndent(level) << "------- LEAF -------" << endl;
	chainDump += levelIndent(level) + Chain("------- LEAF -------\n");
	// cout << levelIndent(level) << "NumEntries=" << n << " PageId=" << _pageId << endl;
	chainDump += levelIndent(level)
	    + Chain("NumEntries=")
	    + Chain(n)
	    + Chain(" PageId=")
	    + Chain(_pageId)
	    + Chain("\n");
	unsigned i = 0;
	while ( i < n )
	{
	    
	    // cout << levelIndent(level) << "Entry " << i << " : ";
	    chainDump += levelIndent(level) + Chain("Entry ") + Chain(i) + Chain(" : ");
	    char* srcPtr = (char*)((long long)_pI + sizeof(unsigned) + i * getEntrySize());
	    CegoBTreeValue iv;
	    iv.setPtr(srcPtr, _keyLen);
	    srcPtr += _keyLen;
	    CegoDataPointer dp;
	    dp.decode(srcPtr);
	    // cout << iv.toChain(_pSchema) << " " << dp << endl;

	    chainDump +=  iv.toChain(_pSchema) + Chain(" ") + dp.toChain() + Chain("\n");
	    i++;
	}
	// cout << levelIndent(level) << "Next Page : " << _nextPageId << endl;
	chainDump +=  levelIndent(level) + Chain("Next Page : ") + Chain(_nextPageId) + Chain("\n");
	// cout << levelIndent(level) << "--------------------" << endl;
	chainDump +=  levelIndent(level) + Chain("--------------------") + Chain("\n");
    }
    else if ( _nt == NODE )
    {
	
	// cout << levelIndent(level) << "####### NODE ####### " << endl;
	chainDump += levelIndent(level) + Chain("####### NODE ####### \n");
	
	// cout << levelIndent(level) << "NumEntries=" << n << " PageId=" << _pageId << endl;
	chainDump += levelIndent(level) + Chain("NumEntries=") + Chain(n) + Chain(" PageId=") + Chain(_pageId) + Chain("\n");
	
	PageIdType pageId;

	char* p = (char*)((long long)_pI + sizeof(unsigned));
	
	memcpy(&pageId, p, sizeof(PageIdType));
			  
	// cout << levelIndent(level) << "Child : " << pageId << endl;	
	chainDump += levelIndent(level) + Chain("Child : ") + Chain(pageId) + Chain("\n");

	unsigned i = 0;
	while ( i < n )
	{
	    // cout << levelIndent(level) << "Key " << i << " : ";
	    chainDump += levelIndent(level) + Chain("Key ") + Chain(i) + Chain(" : ");
	    
	    p = (char*)((long long)_pI + sizeof(unsigned) + sizeof(PageIdType) + i * getEntrySize());
	    CegoBTreeValue iv;
	    iv.setPtr(p, _keyLen);
	    // cout << iv.toChain(_pSchema) << endl;
	    chainDump += iv.toChain(_pSchema) + Chain("\n");
	    p += _keyLen;
	    
	    memcpy(&pageId, p, sizeof(PageIdType));
	    
	    // cout << levelIndent(level) << "Child : " << pageId << endl;
	    chainDump += levelIndent(level) + Chain("Child : ") + Chain(pageId) + Chain("\n");

	    i++;
	}
	//cout << levelIndent(level) << "################## " << endl;
	chainDump += levelIndent(level) + Chain("##################\n");
    }
    else
    {
	// cout << levelIndent(level) << "??? UNDEFINED ??? " << endl;
	chainDump += levelIndent(level) + Chain("??? UNDEFINED ???\n");
    }
}

void CegoBTreeNode::reset()
{
    _pNextChild = (char*)((long long)_pI + sizeof(unsigned));
}

void CegoBTreeNode::setPosition(unsigned pos)
{
    _pNextChild = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
}

bool CegoBTreeNode::nextChildPointer(PageIdType &pageId)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    if ( (long long)_pNextChild > (long long)_pI + (long long)(sizeof(unsigned) + numEntries() * getEntrySize()) )
	return false;
    
    memcpy(&pageId, _pNextChild, sizeof(PageIdType));
    _pNextChild+=sizeof(PageIdType);
    
    _pNextChild+=_keyLen;
    
    return true;
}

unsigned CegoBTreeNode::getMedPage(unsigned lb, unsigned rb, PageIdType& pageId)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));
    
    unsigned med = (lb + rb) / 2;
    char* _pMed = (char*)((long long)_pI + (long long)(sizeof(unsigned) + med * getEntrySize()));
    
    memcpy(&pageId, _pMed, sizeof(PageIdType));
   
    return med;
}

bool CegoBTreeNode::rightValue(unsigned pos, CegoBTreeValue& val)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    char* pChild = (char*)((long long)_pI + sizeof(unsigned) + pos * getEntrySize());
    if ( (long long)pChild > (long long)_pI + (long long)(sizeof(unsigned) + numEntries() * getEntrySize()))
	return false;
        
    pChild+=sizeof(PageIdType);
    
    val.setPtr(pChild, _keyLen);
    return true;
}

void CegoBTreeNode::getLastChildPointer(PageIdType &pageId) const
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    if ( numEntries() == 0 )
    {
	pageId = 0;
    }
    else
    {
	char *p = (char*)((long long)_pI + (long long)(sizeof(unsigned) + numEntries() * getEntrySize()));    
	memcpy(&pageId, p, sizeof(PageIdType));
    }
}

void CegoBTreeNode::setFirstChildPointer(PageIdType pageId)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    char *p = (char*)((long long)_pI + (long long)sizeof(unsigned));    
    memcpy(p, &pageId, sizeof(PageIdType));
}

bool CegoBTreeNode::rightChild(CegoBTreeValue& val)
{
    if ( _nt == LEAF )
	throw Exception(EXLOC, Chain("Invalid method for node type"));
   
    if ( (long long)_pNextChild > (long long)_pI + (long long)(sizeof(unsigned) + numEntries() * getEntrySize()))
	return false;
    
    val.setPtr(_pNextChild - _keyLen, _keyLen);
    return true;
}

void CegoBTreeNode::getValue(unsigned pos, CegoBTreeValue& val, CegoDataPointer& dp)
{
    if ( _nt == NODE )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    if ( pos >= numEntries() )
	throw Exception(EXLOC, Chain("Node position out of range"));

    char* pVal = (char*)((long long)_pI + (long long)(sizeof(unsigned) + pos * getEntrySize()));
    
    val.setPtr(pVal, _keyLen);
    pVal +=_keyLen;
    dp.decode(pVal);
}

bool CegoBTreeNode::nextValue(CegoBTreeValue& val, CegoDataPointer& dp)
{
    if ( _nt == NODE )
	throw Exception(EXLOC, Chain("Invalid method for node type"));

    if ( (long long)_pNextChild >= (long long)_pI + (long long)(sizeof(unsigned) + numEntries() * getEntrySize()))
	return false;
    
    val.setPtr(_pNextChild, _keyLen);
    _pNextChild +=_keyLen;
    dp.decode(_pNextChild);
    _pNextChild += DPENCODINGLENGTH;
    
    return true;    
}


Chain CegoBTreeNode::levelIndent(unsigned level)
{
    Chain s;
    while ( level > 0 )
    {
	s += Chain(" ");
	level--;
    }
    return s;
}

bool CegoBTreeNode::isTupleVisible(CegoObjectManager* pOM, const CegoDataPointer &dp, unsigned tabSetId, unsigned long long tid) const
{    
    char* p;
    unsigned len;

    CegoBufferPage bp;
    pOM->claimDataPtrUnlocked(tabSetId, CegoBufferPool::SYNC, dp, p, len, bp);
    
    // skipping tid
    unsigned long long dataTid;
    unsigned long long tastep;
    CegoTupleState ts;

    CegoQueryHelper::decodeTupleHeader(dataTid, tastep, ts, p);
    
    pOM->releaseDataPtrUnlocked(bp, false);

    /*
    
    cout << "Datapointer = " << dp << endl;
    cout << "tid = " << tid << endl;
    cout << "dataTid = " << dataTid << endl;
    cout << "ts = " << ts << endl;
    */

    if ( dataTid == 0
	 || ( tid == dataTid && ( ts == CegoTupleState::INSERTED ) )
	 || ( tid != dataTid && ( ts == CegoTupleState::DELETED  ) ) )
    {
	return true;
    }
    else
    {
	// cout << "TID = " << tid << " DATATID = " << dataTid << " TS = " << ts << endl;
	return false;
    }
}

void CegoBTreeNode::sortSchema()
{    
    _sortFL.Empty();

    unsigned nextId = 1;

    while ( _sortFL.Size() < _pSchema->Size() )
    {

	CegoField *pNext = 0;
	CegoField *pF =  _pSchema->First();       

	while ( pF )
	{
	    if ( nextId == pF->getId() )
	    {
		pNext = pF;
	    }
	    pF =  _pSchema->Next();
	}

	if ( pNext )
	{
	    _sortFL.Insert(*pNext);
	}

	nextId++;
    }
}
