///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoBufferPage.cc
// -----------------
// Cego BufferPage implementation module
//     
// 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: CegoBufferPage
// 
// Description: 
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include <lfc/Exception.h>

#include "CegoDefs.h"
#include "CegoBufferPage.h"

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

#define ALLOCDELTA sizeof(int)

// 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;
}

CegoBufferPage::CegoBufferPage(const CegoBufferPage &bp)
{
    _pageId = bp._pageId;
    _fileId = bp._fileId;
    
    _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, int pageSize)
{
    // cout << "creating Buffer Page with pageptr " << (unsigned)pagePtr << endl;
    _pageSize = pageSize;       
    _pagePtr = (char*)pagePtr;
    _pageHead = (PageHead*)pagePtr;
    _isFixed = false;
}

CegoBufferPage::~CegoBufferPage()
{
}

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

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

void CegoBufferPage::initPage(CegoBufferPage::PageType t)
{

    PageHead ph;
    
    ph.pageType = t;
    ph.nextFileId = 0;
    ph.nextPageId = 0;

    memcpy(_pagePtr, &ph, PAGEHEAD);

    if ( t != BLOB )
    {
	int* ePtr = (int*)((long)_pagePtr + PAGEHEAD);
	*ePtr = 0;
	
	int* freePtr = (int*)((long)_pagePtr + (long)_pageSize);
	freePtr--;
	*freePtr=0;
    }
}

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

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

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


void CegoBufferPage::setFileId(int fileId)
{
    _fileId = fileId;
}

int CegoBufferPage::getFileId()
{
    return _fileId;
}

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

int CegoBufferPage::getPageId()
{
    return _pageId;
}

void CegoBufferPage::setNextFileId(int fileId)
{
    _pageHead->nextFileId = fileId;
}

int CegoBufferPage::getNextFileId()
{
    return _pageHead->nextFileId;
}

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

int CegoBufferPage::getNextPageId()
{
    return _pageHead->nextPageId;
}

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

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

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

void* CegoBufferPage::newEntry(int size)
{
    
    if ( size % BUPMNG_ALIGNMENT )
	size = ( size / BUPMNG_ALIGNMENT + 1) * BUPMNG_ALIGNMENT;
    
    int* ePtr;
    
    int* freePtr = (int*)((long)_pagePtr + (long)_pageSize);
    freePtr--;
    
    while (*freePtr != 0 )
    {

	ePtr = (int*)((long)_pagePtr + *freePtr);
	if (*ePtr >=  size && *ePtr <= size + ALLOCDELTA)
	{
	    int* sPtr = freePtr;
	    
	    while (*(sPtr-1) != 0)
	    {
		sPtr--;
	    }
	    *freePtr = *sPtr;
	    *sPtr = 0;

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

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

    fPtr++;

    return (fPtr);
}

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

    setptr[0]=0;    
    setptr[1]=0;
    
    int i = 0;
    int n;
    int count=0;
    
    while ( *freePtr && count < ( _pageSize * BUPMNG_MINFREERATIO) / ( 100 * sizeof(int)))
    {
	// 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)p - (long)_pagePtr) + *((int*)p - 1))
	{
	    // cout << "right free neighbour detected" << endl;
	    setptr[i]=freePtr;
	    i++;
	}
	else if (*freePtr + sizeof(int) + *(int*)(*freePtr + (long)_pagePtr)  == ((long)p - (long)_pagePtr) - sizeof(int))
	{
	    // cout << "left free neighbour detected" << endl;
	    
	    setptr[i]=freePtr;
	    n=i;
	    i++;
	}
	
	freePtr--;
	count++;
    }

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

	int newsize = *((int*)p -1) + *(int*)( *setptr[0] + (long)_pagePtr) + *(int*)( *setptr[1] + (long)_pagePtr) + 2 * sizeof(int);
	
	// cout << "Setup newsize with " << newsize << endl;

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

	*freePtr = 0;
	break;
    }
    }
}

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

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

    _entryPos = 0;
    return getNextEntry();
}

void* CegoBufferPage::getNextEntry()
{

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

    // cout << "Next entry .." << endl;

    while ( *(int*)_ePtr != 0 )
    {
	// checking free list

	int* freePtr = (int*)((long)_pagePtr + (long)_pageSize);

	freePtr--;

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

	    _ePtr = _ePtr + *((int*)_ePtr) + sizeof(int);
	}
	else
	{
	    
	    _entryLen = *(int*)_ePtr; 

	    char* rPtr = _ePtr + sizeof(int);
	    
	    _entryPos = _ePtr - _pagePtr + sizeof(int);
	    
	    _ePtr = _ePtr + *((int*)_ePtr) + sizeof(int);

	    return rPtr;
    	}
    }
    return 0;
}

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

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

int CegoBufferPage::getEntryLen()
{
    return _entryLen;
}

int CegoBufferPage::getEntryPos()
{
    return _entryPos;
}

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

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

    int* e = (int*)getFirstEntry();
    int i=1;
    if (e)
    {
	e--;
	
	cout << "Entry " << i << " Pos=" << (long)e - (long)_pagePtr << " Size=" << *e << endl;
	i++;
	while ( ( e = (int*)getNextEntry()) > 0  )
	{
	    e--;
	    cout << "Entry " << i << " Pos=" << (long)e - (long)_pagePtr << " Size=" << *e << endl;
	    i++;
	}
    }

    int* freePtr = (int*)((long)_pagePtr + (long)_pageSize);
    freePtr--;
    
    cout << "---------------------------" << endl;
    cout << "Free entries : " << endl;
    i=1;
    while ( *freePtr )
    {
	cout << "Free Entry " << i << " Pos=" << *freePtr << " Size=" << *(int*)(*freePtr + (long)_pagePtr)<< endl;
	i++;
	freePtr--;
    }
    
    cout << endl;
}


CegoBufferPage& CegoBufferPage::operator = ( const CegoBufferPage& bp )
{
    _pageId = bp._pageId;
    _fileId = bp._fileId;
    
    _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 && _fileId == bp._fileId )
	return true;
    return false;
}

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



