///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBufferPage.cc
// -----------------
// Cego BufferPage implementation module
//     
// Design and Implementation by Bjoern Lemke
//     
// (C)opyright 2000-2025 Bjoern Lemke
//
// IMPLEMENTATION MODULE
//
// Class: CegoBufferPage
//
// Description: Database page container class
//
// Status: CLEAN
//
///////////////////////////////////////////////////////////////////////////////

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

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

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

#define ALLOCDELTA sizeof(unsigned)

// to ensure memory alignment, pagehead size is need to be calculated in the
// following way ( at least required for SPARC ) 
#define PAGEHEAD (((sizeof(PageHead)-1)/BUPMNG_ALIGNMENT)+1)*BUPMNG_ALIGNMENT

CegoBufferPage::CegoBufferPage()
{
    _isFixed = false;
    _pageId = 0;
}

CegoBufferPage::CegoBufferPage(const CegoBufferPage &bp)
{
    _pageId = bp._pageId;
    
    _pageSize = bp._pageSize;
    _pagePtr = bp._pagePtr;
    
    _ePtr = bp._ePtr;
    _blobPtr = bp._blobPtr;
    
    _entryLen = bp._entryLen;
    _entryPos = bp._entryPos;
    
    _pageHead = bp._pageHead;
    _isFixed = bp._isFixed;    
}

CegoBufferPage::CegoBufferPage(void* pagePtr, unsigned pageSize)
{
    _pageSize = pageSize;       
    _pagePtr = (char*)pagePtr;
    _pageHead = (PageHead*)pagePtr;
    _isFixed = false;
}

CegoBufferPage::~CegoBufferPage()
{
}

void CegoBufferPage::setFixed(bool isFixed)
{
    _isFixed = isFixed;
}

bool CegoBufferPage::isFixed() const
{
    return _isFixed;
}

void CegoBufferPage::initPage(CegoBufferPage::PageType t)
{
    PageHead ph;
    
    ph.pageType = t;
    ph.nextPageId = 0;

    memcpy(_pagePtr, &ph, PAGEHEAD);

    if ( t == TABLE || t == TUPLE )
    {
	unsigned* ePtr = (unsigned*)((long long)_pagePtr + PAGEHEAD);
	*ePtr = 0;
	
	unsigned* freePtr = (unsigned*)((long long)_pagePtr + (long)_pageSize);
	freePtr--;
	*freePtr=0;
    }
}

void CegoBufferPage::setPagePtr(char* ptr)
{
    _pagePtr = ptr;
}

void CegoBufferPage::setPageHead(PageHead* ptr)
{
    _pageHead = ptr;
}

void CegoBufferPage::setPageSize(unsigned pageSize)
{
    _pageSize = pageSize;
}

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

PageIdType& CegoBufferPage::getPageId()
{
    return _pageId;
}

void CegoBufferPage::setNextPageId(PageIdType pageId)
{
    _pageHead->nextPageId = pageId;
}

PageIdType& CegoBufferPage::getNextPageId() const
{
    return _pageHead->nextPageId;
}

unsigned CegoBufferPage::getNumEntries()
{
    unsigned numEntries = 0;
    if ( getFirstEntry() )
    {
	numEntries++;
	while ( getNextEntry() )
	    numEntries++;
    }
    return numEntries;
}

void CegoBufferPage::setType(CegoBufferPage::PageType t)
{
    _pageHead->pageType = t;
}

CegoBufferPage::PageType& CegoBufferPage::getType() const
{
    return _pageHead->pageType;
}

void* CegoBufferPage::newEntry(unsigned size)
{   
    if ( size % BUPMNG_ALIGNMENT )
	size = ( size / BUPMNG_ALIGNMENT + 1) * BUPMNG_ALIGNMENT;
    
    unsigned* ePtr;
    
    unsigned* freePtr = (unsigned*)((long long)_pagePtr + (long)_pageSize);
    freePtr--;
    
    while (*freePtr != 0 )
    {
	ePtr = (unsigned*)((long long)_pagePtr + *freePtr);
	if (*ePtr >=  size && *ePtr <= (unsigned)(size + ALLOCDELTA))
	{
	    unsigned* sPtr = freePtr;
	    
	    while (*(sPtr-1) != 0)
	    {
		sPtr--;
	    }
	    *freePtr = *sPtr;
	    *sPtr = 0;

	    ePtr++;
	    return ePtr;
	}
	else if (*ePtr > (unsigned)(size + ALLOCDELTA))
	{
	    unsigned osize = *ePtr;
	  	   
	    *ePtr = size;
		
	    char* tPtr = (char*)ePtr;
	    tPtr += size + sizeof(unsigned);
		
	    unsigned* sPtr = (unsigned*)tPtr;
	    *sPtr= osize - size - sizeof(unsigned);
	    
	    *freePtr = (long long)sPtr - (long long)_pagePtr;
	    
	    ePtr++;
	    return ePtr;
	    
	}
	freePtr--;	
    }   
    
    // no appropriate entry found in freelist,
    // allocating new space
    
    unsigned* fPtr = (unsigned*)((long long)_pagePtr + PAGEHEAD);
    
    // cout << "fptr is " << (int)fPtr << endl;

    while (*(unsigned*)fPtr != 0 )
    {	
	fPtr = (unsigned*)((long long)fPtr + *fPtr + sizeof(unsigned));
    }
    
    // check if enough memory in page
    
    if ((long long)fPtr + size >= (long long)_pagePtr + _pageSize - ( _pageSize * BUPMNG_MINFREERATIO ) / 100 )
    {
	// not enough space on page
	return 0;
    }
    
    *(unsigned*)fPtr = size;
    char *sPtr = (char*)fPtr;
    sPtr = sPtr + size + sizeof(unsigned);
    unsigned* nPtr = (unsigned*)sPtr;
    *nPtr = 0;

    fPtr++;

    return (fPtr);
}

void CegoBufferPage::freeEntry(void* p)
{
    unsigned* freePtr = (unsigned*)((long long)_pagePtr + (long)_pageSize);
    freePtr--;
    
    unsigned* setptr[2];

    setptr[0]=0;
    setptr[1]=0;
    
    unsigned i = 0;
    unsigned n = 0;
    unsigned count=0;
    
    while ( *freePtr && count < (unsigned)(( _pageSize * BUPMNG_MINFREERATIO) / ( 100 * sizeof(unsigned))))
    {
	// cout << "*FreePtr=" << *freePtr << endl;
	// cout << "p pos " << ((int)p - (int)_pagePtr) << endl;
	// cout << "p size " << *((int*)p -1) << endl;
	// cout << "size of freePtr " << *(int*)(*freePtr + (int)_pagePtr) << endl;
	
	if ( *freePtr == ((long long)p - (long long)_pagePtr) + *((unsigned*)p - 1))
	{
	    // cout << "right free neighbour detected" << endl;
	    setptr[i]=freePtr;
	    i++;
	}
	else if (*freePtr + sizeof(unsigned) + *(unsigned*)(*freePtr + (long long)_pagePtr)
		 == ((long long)p - (long long)_pagePtr) - sizeof(unsigned))
	{
	    // cout << "left free neighbour detected" << endl;   
	    setptr[i]=freePtr;
	    n=i;
	    i++;
	}
	freePtr--;
	count++;
    }

    if (count == (unsigned)(( _pageSize * BUPMNG_MINFREERATIO ) / ( 100 * sizeof(unsigned))))
    {
	throw Exception(EXLOC, "Minfree exceeded");
    }
    switch (i)
    {
    case 0:
    {
	*freePtr = (long long)p - (long long)_pagePtr - sizeof(unsigned);
	freePtr--;
	*freePtr = 0;
	break;
    }
    case 1:
    {
	unsigned newsize = *((unsigned*)p-1) + *(unsigned*)( *setptr[0] + (long long)_pagePtr) + sizeof(unsigned);
	
	// cout << "Setup newsize with " << newsize << endl;
	
	if (*setptr[0] > (long long)p - (long long)_pagePtr)
	{	    
	    *setptr[0] = (long long)p - (long long)_pagePtr - sizeof(unsigned);
	    *((unsigned*)p-1) = newsize;
	}
	else if (*setptr[0] < (long long)p - (long long)_pagePtr)
	{   
	    *(unsigned*)( *setptr[0] + (long long)_pagePtr) = newsize;
	}	
	break;
    }
    case 2:
    {	
	unsigned newsize = *((unsigned*)p -1)
	    + *(unsigned*)( *setptr[0]+ (long long)_pagePtr)
	    + *(unsigned*)( *setptr[1] + (long long)_pagePtr) + 2 * sizeof(unsigned);
	
	// cout << "Setup newsize with " << newsize << endl;

	unsigned* newptr = setptr[n];
	
	if (*setptr[0] > (long long)p)
	{
	    *setptr[0] = *newptr;
	    *(unsigned*)( *setptr[0] + (long long)_pagePtr) = newsize;
	    freePtr++;
	    *setptr[1] = *freePtr;
	} 
	else
	{
	    *setptr[1] = *newptr;
	    *(unsigned*)( *setptr[1] + (long long)_pagePtr) = newsize;
	    freePtr++;
	    *setptr[0] = *freePtr;
	}

	*freePtr = 0;
	break;
    }
    }
}

void* CegoBufferPage::getFirstEntry()
{
    _ePtr = (char*)((long long)_pagePtr + PAGEHEAD);

    // cout << "BufferPage First:  _ePtr is  " << (unsigned)_ePtr << endl;

    _entryPos = 0;
    return getNextEntry();
}

void* CegoBufferPage::getNextEntry()
{    
    // cout << "BufferPage:  _ePtr is  " << (unsigned long long)_ePtr << endl;
    // cout << "->  size is   " << *(int*)_ePtr << endl;
    // cout << "->  delta to page end is  " << (unsigned long long)_ePtr - (unsigned long long)_pagePtr << endl;

    // cout << "Next entry .." << endl;
    
    while ( *(unsigned*)_ePtr != 0 )
    {
	// checking free list
	unsigned* freePtr = (unsigned*)((long long)_pagePtr + (long long)_pageSize);

	freePtr--;
		
	bool isFreeEntry = false;
	
	while ( *freePtr != 0 && ! isFreeEntry )
	{
	    if ( _ePtr == (char*)((long long)_pagePtr + *freePtr))
	    {
		isFreeEntry = true;
	    }
	    else
	    {
		freePtr--;
	    }
	}
		
	if ( isFreeEntry )
	{
	    // skipping free entry
	    _entryPos = _entryPos + *((unsigned*)_ePtr) + sizeof(unsigned);
	    _ePtr = _ePtr + *((unsigned*)_ePtr) + sizeof(unsigned);
	}
	else
	{	  
	    _entryLen = *(unsigned*)_ePtr; 
	    char* rPtr = _ePtr + sizeof(unsigned);	    
	    _entryPos = _ePtr - _pagePtr + sizeof(unsigned);	    
	    _ePtr = _ePtr + *((unsigned*)_ePtr) + sizeof(unsigned);
	    return rPtr;
    	}
    }
    return 0;
}

char* CegoBufferPage::getChunkEntry() const
{
    return (char*)((long long)_pagePtr + PAGEHEAD);
}

unsigned CegoBufferPage::getPageSize() const
{
    return _pageSize;    
}

unsigned CegoBufferPage::getChunkLen() const
{
    return _pageSize - PAGEHEAD;    
}

unsigned CegoBufferPage::getEntryLen() const
{
    return _entryLen;
}

unsigned CegoBufferPage::getEntryPos() const
{
    return _entryPos;
}

void* CegoBufferPage::getPagePtr() const
{
    return _pagePtr;
}

void CegoBufferPage::printPage()
{
    cout << "--- BufferPage ---" << endl;
    cout << "PageId: " << _pageId << endl;
    cout << "NextPageId: " << _pageHead->nextPageId << endl;
    cout << "PageSize: " << _pageSize << endl;
    cout << "PagePtr: " << (unsigned long long)_pagePtr << endl;

    unsigned* e = (unsigned*)getFirstEntry();
    unsigned i=1;
    if (e)
    {
	e--;
	cout << "Entry " << i << " Pos=" << (long long)e - (long long)_pagePtr << " Size=" << *e << endl;
	i++;
	while ( ( e = (unsigned*)getNextEntry()) != 0  )
	{
	    e--;
	    cout << "Entry " << i << " Pos=" << (long long)e - (long long)_pagePtr << " Size=" << *e << endl;
	    i++;
	}
    }
    unsigned* freePtr = (unsigned*)((long long)_pagePtr + (long)_pageSize);
    freePtr--;
    
    cout << "---------------------------" << endl;
    cout << "Free entries : " << endl;
    i=1;
    while ( *freePtr )
    {
	cout << "Free Entry " << i << " Pos=" << *freePtr << " Size=" << *(unsigned*)(*freePtr + (long long)_pagePtr)<< endl;
	i++;
	freePtr--;
    }    
    cout << endl;
}

CegoBufferPage& CegoBufferPage::operator = ( const CegoBufferPage& bp )
{
    _pageId = bp._pageId;    
    _pageSize = bp._pageSize;
    _pagePtr = bp._pagePtr;
    
    _ePtr = bp._ePtr;
    _blobPtr = bp._blobPtr;
    
    _entryLen = bp._entryLen;
    _entryPos = bp._entryPos;
    
    _pageHead = bp._pageHead;
    _isFixed = bp._isFixed;

    return (*this);
}

bool CegoBufferPage::operator == ( const CegoBufferPage& bp)
{
    if ( _pageId == bp._pageId )
	return true;
    return false;
}

bool CegoBufferPage::operator != ( const CegoBufferPage& bp)
{
    if ( _pageId != bp._pageId )
	return true;
    return false;
}
