///////////////////////////////////////////////////////////////////////////////
//                                                         
// CegoFileHandler.cc
// ------------------
// Cego database file handler implementation
//     
// 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: CegoFileHandler
// 
// Description: 
// 
// The filehandler class provides access to all datafiles. Datafiles consists
// of a defined number of pages managed by the this class.
// At the header of a datafile, a bitmap is stored for used page information
//
// Status: QG-2.6
//
///////////////////////////////////////////////////////////////////////////////

#include <lfc/Exception.h>

#include "CegoFileHandler.h"
#include "CegoBufferPage.h"

#define BIT_PER_BYTE 8

CegoFileHandler::CegoFileHandler(const Chain& logFile, int pageSize) : CegoModule(logFile)
{
    for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
    {
	_isReg[i]=false;
	_tabSetId[i]=0;
	_fhList[i]=0;
	_buMask[i]=0;
    }
    _modId = getModId("CegoFileHandler");
}

CegoFileHandler::~CegoFileHandler()
{
    for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
    {
	if ( _fhList[i] != 0 )
	{
	    _fhList[i]->close();
	}
    }    
}

bool CegoFileHandler::isBackup(int fileId)
{
    if ( _buMask[fileId] )
	return true;
    return false;
}

void CegoFileHandler::setBackup(int fileId, bool m)
{
    if ( m == true )
    {
	_buMask[fileId] = new unsigned[ (  getNumPages(fileId) / (BIT_PER_BYTE * sizeof(int))) + 1];

	int i;
	
	for (i = 0; i < ( getNumPages(fileId) / (BIT_PER_BYTE * sizeof(int))) + 1; i++)
	{
	    _buMask[fileId][i] = 0;
	}
	_fbmMask[fileId] = false;
    }
    else
    {
	if ( _buMask[fileId] != 0 )
	{
	    delete _buMask[fileId];
	    _buMask[fileId] = 0;
	}
    }
}

void CegoFileHandler::markPage(int fileId, int pageId)
{
    if ( _buMask[fileId] == 0 )
    {
	Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
	throw Exception(EXLOC, msg);
    }
    unsigned bmid = pageId / ( BIT_PER_BYTE * sizeof(int));
    unsigned bmoffset = pageId % ( BIT_PER_BYTE * sizeof(int));
    
    unsigned bm = _buMask[fileId][bmid];
    
    unsigned f = ~(~0 << 1);
    f = f << bmoffset;
    
    _buMask[fileId][bmid] = bm | f;

}

bool CegoFileHandler::isMarked(int fileId, int pageId)
{

    if ( _buMask[fileId] == 0 )
    {
	Chain msg = Chain("No backup mode for fileId ") + Chain(fileId);
	throw Exception(EXLOC, msg);

    }
    unsigned bmid = pageId / ( BIT_PER_BYTE * sizeof(int));
    unsigned bmoffset = pageId % ( BIT_PER_BYTE * sizeof(int));
    
    unsigned bm = _buMask[fileId][bmid];    
        
    unsigned checkbit = bm >> bmoffset & ~(~0 << 1);    
    
    if (  checkbit )
    {
	return true;
    }
    return false;

}

int CegoFileHandler::getFBMSize(int fileId)
{
    return (_numPages[fileId] / (BIT_PER_BYTE * sizeof(int))) + 1;
}

void CegoFileHandler::readFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
{
    File *pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot read fbm", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {
	
	pF->seek(0);

	int tabSetId;
	pF->readByte((char*)&tabSetId, sizeof(int));

	int numPages = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(int))) + 1;
	int i;
	for (i = 0; i < numPages; i++)
	{
	    pF->readByte((char*)&fbm[i], sizeof(int));
	}
		
	pLockHandle->unlockDataFile(fileId);

    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot read fbm", e);
    }
}


void CegoFileHandler::writeFBM(int fileId, unsigned *fbm, CegoLockHandler *pLockHandle)
{
    File *pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot write fbm", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {
	
	pF->seek(0);

	int tabSetId;
	pF->readByte((char*)&tabSetId, sizeof(int));


	int numPages = (_numPages[fileId] / (BIT_PER_BYTE * sizeof(int))) + 1;
	int i;

	for (i = 0; i < numPages; i++)
	{
	    pF->writeByte((char*)&fbm[i], sizeof(int));
	}
		
	pLockHandle->unlockDataFile(fileId);

    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot write fbm", e);
    }
}



/* the cleanDataFile method traces the corresponding datafile to check for
   allocated but unused pages. This may happen, if the allocatePage method
   was called, but for any reason ( e.g. a system crash ) the page could never be written
   back to file. In this case, the fixStat attribute is still zero. so the page
   can be released for other use */ 
 
void CegoFileHandler::cleanDataFile(int fileId, CegoLockHandler *pLockHandle)
{

    File* pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot clean datafile", e);
    }
    
    int offset = sizeof(FileType) +  3 * sizeof(int)
	+  (_numPages[fileId] / ( BIT_PER_BYTE * sizeof(int) )) * sizeof(int) ;
    
    int pageId;
    
    for (pageId = 0; pageId < _numPages[fileId]; pageId++)
    {
	
	pF->seek(offset + pageId * ( _pageSize + sizeof(int)));
	int fixStat;
	pF->readByte((char*)&fixStat, sizeof(int));    
	
	if ( fixStat == 0 && isClaimed(fileId, pageId, pLockHandle) == true )
	{
	    log(_modId, Logger::NOTICE, Chain("Releasing page ") + Chain(pageId));
	    unsigned *fbm;
	    int fbmSize;
	    releasePage(fileId, pageId, pLockHandle, fbm, fbmSize);
	}
    }
}

/* a new datafile is created using the initDataFile method. With the given tabSetId, path, fileId, size and type
   the file is created and initialized. Note, that after creation the datafile is still NOT registered for further usage.
   This must be done using the regDataFile method */

void CegoFileHandler::initDataFile(int tabSetId, const Chain& path, int fileId, int numPages, FileType ft)
{
    
    File* pF = new File(path);
    

    if ( pF->exists() )
    {      
	Chain msg = Chain("Datafile ") + path + Chain(" already exists");
	delete pF;
	throw Exception( EXLOC, msg);
    }


    try 
    {

	pF->open(File::WRITE);
	
	pF->writeByte((char*)&tabSetId, sizeof(int));
	pF->writeByte((char*)&ft, sizeof(FileType));
	// cout << "Init datafile with numPages " << numPages << endl;
	pF->writeByte((char*)&numPages, sizeof(int));
		
	int i;
	
	for (i = 0; i < (numPages / (BIT_PER_BYTE * sizeof(int))) + 1; i++)
	{
	    unsigned bm = 0;
	    pF->writeByte((char*)&bm, sizeof(int));
	}
	
	char* initBuf = new char[_pageSize];
	CegoBufferPage ip(initBuf, _pageSize);
	ip.initPage(CegoBufferPage::TABLE);
	    
	for (i = 0; i < numPages; i++)
	{
	    int fixStat = 0;
	    pF->writeByte((char*)&fixStat, sizeof(int));
	    pF->writeByte(initBuf, _pageSize);
	}
	delete initBuf;
	
	pF->close();
	delete pF;
    }
    catch ( Exception e )
    {
	delete pF;
	throw Exception(EXLOC, "Cannot init datafile", e);
    }
}

/* the regDataFile method is used for datafile registration. The given file is checked
   for consistency and is registered */

void CegoFileHandler::regDataFile(int tabSetId, const Chain& path, int fileId, CegoLockHandler *pLockHandle)
{

    pLockHandle->lockDataFile(fileId, CegoLockHandler::READ);

    if ( _isReg[fileId] )
    {

	pLockHandle->unlockDataFile(fileId);

	if ( _path[fileId] == path )
	{
	    log(_modId, Logger::NOTICE, Chain("Data file ") + path + Chain(" already exists, skipping"));
	    return;
	}
	else
	{
	    Chain msg = Chain("Datafile Id ") + Chain(fileId) + Chain(" already occupied");
	    throw Exception(EXLOC, msg);
	}
    }

    int checkTabSetId;
    int numPage;
    FileType ft;
	
    try {

	File checkFile(path);	    
	checkFile.open(File::READ);

	
	checkFile.readByte((char*)&checkTabSetId, sizeof(int));	
	checkFile.readByte((char*)&ft, sizeof(FileType));
	checkFile.readByte((char*)&numPage, sizeof(int));
	
    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	Chain msg = Chain("Cannot access datafile <") + path + Chain(">");
	throw Exception(EXLOC, msg, e);
    }
    
    if ( tabSetId != checkTabSetId )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Table Set Id does not match");
    }
	
    _isReg[fileId] = true;
    _path[fileId] = path;
    _tabSetId[fileId] = tabSetId;
    _fileType[fileId] = ft;
    _numPages[fileId] = numPage;

    pLockHandle->unlockDataFile(fileId);
}

/* all registered files of the corresponding table set are closed and unregistered 
   with the releaseFiles method */

void CegoFileHandler::releaseFiles(int tabSetId)
{

    for (int i = 0; i<FILMNG_MAXDATAFILE; i++)
    {
	if ( _tabSetId[i] == tabSetId && _fhList[i] != 0 )
	{    
	    _fhList[i]->close();
	    _fhList[i]=0;
	    _tabSetId[i]=0;
	    _isReg[i]=false;    
	}
    }
}

/* a single database page is written to file using the writePage method. With the corresponding fileId, pageId
   and the pointer to the data, the file handler can perform the write operation. The fixStat parameter indicates
   the number of bufferFix operations on the page and is normally used by the BufferPool class for the page
   relocation strategy */ 

void CegoFileHandler::writePage(int fileId, int pageId, int fixStat, char* pageData, CegoLockHandler *pLockHandle)
{
    
    File* pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot write page", e);
    }


    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {

	int offset = sizeof(int) + sizeof(FileType) + 2 * sizeof(int)
	    +  (_numPages[fileId] / ( BIT_PER_BYTE * sizeof(int) )) * sizeof(int) ;
		
	pF->seek(offset + pageId * ( _pageSize + sizeof(int)));
	pF->writeByte((char*)&fixStat, sizeof(int));    
	pF->writeByte(pageData, _pageSize);

	
	if ( _buMask[fileId] )
	    markPage(fileId, pageId);
	

	pLockHandle->unlockDataFile(fileId);

    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot write page", e);
    }

}

/* pages from datafiles are read by using the readPage method. The method seeks to the appropriate offset
   in the corresponding datafile and reads the page data into the given data pointer */

void CegoFileHandler::readPage(int fileId, int pageId, int& tabSetId, int& fixStat, char* pageData, CegoLockHandler *pLockHandle)
{
    File *pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot read page", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {

	pF->seek(0);
	pF->readByte((char*)&tabSetId, sizeof(int));
	
	int offset = sizeof(int) + sizeof(FileType) + 2 * sizeof(int) 
	    +  (_numPages[fileId] / ( BIT_PER_BYTE * sizeof(int) )) * sizeof(int) ;
	
	pF->seek(offset + pageId * ( _pageSize + sizeof(int)) );
	
	pF->readByte((char*)&fixStat, sizeof(int));
	
	if ( pF->readByte(pageData, _pageSize) != _pageSize )
	    throw Exception(EXLOC, "Incomplete file read");
	
	pLockHandle->unlockDataFile(fileId);

    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot read page", e);
    }
}

/* the claimPage method allocates a given page in the given file. 
   Normally this method is used for "well known entry pages", e.g. the system catalog page
   which is used as an entry point. */
 
void CegoFileHandler::claimPage(int fileId, int pageId, CegoLockHandler *pLockHandle)
{

    File *pF;

    try {

	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot claim page", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {

	unsigned bmid = pageId / ( BIT_PER_BYTE * sizeof(int));
	unsigned bmoffset = pageId % ( BIT_PER_BYTE * sizeof(int));
	
	pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + bmid * sizeof(int));
	
	unsigned bm;
	pF->readByte((char*)&bm, sizeof(unsigned));
	
	unsigned f = ~(~0 << 1);
	f = f << bmoffset;
	
	bm = bm | f;
	
	pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + bmid * sizeof(int));
	pF->writeByte((char*)&bm, sizeof(unsigned));

	if ( _buMask[fileId] )
	    _fbmMask[fileId] = true;

	
	pLockHandle->unlockDataFile(fileId);
    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw e;
    }
}

/* any new required page can be requested using the allocatePage method.
   the required filetype must be specified. 
   the method returns the assigned file and page information */

void CegoFileHandler::allocatePage(int tabSetId, FileType ft, int& fileId, int& pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize, bool doAppend)
{

    int fid = 0;
    
    while (fid < FILMNG_MAXDATAFILE)
    {
	if ( _isReg[fid] && _tabSetId[fid] == tabSetId && _fileType[fid] == ft )
	{		
	    
	    File* pF;
	    pF = getHandle(fid);
	    
	    pLockHandle->lockDataFile(fid, CegoLockHandler::WRITE);	
	    
	    try 
	    {

		unsigned ws = sizeof(unsigned)*BIT_PER_BYTE;

		int k;

		if ( doAppend == false  || _appendPos[fid] == 0)
		{

		    /*
		    int ts;
		    pF->readByte((char*)&ts, sizeof(int));		
		    
		    FileType aft;
		    pF->readByte((char*)&aft, sizeof(FileType));
		    
		    int numPage;			
		    pF->readByte((char*)&numPage, sizeof(int));
		    */
		    
		    // cout << "AppendPos = " << _appendPos[fid] << " ... Seek to " << 2*sizeof(int) + sizeof(FileType) << endl;

		    pF->seek(2*sizeof(int) + sizeof(FileType));
		    k = 0;
		}
		else
		{		    
		    // cout << "Seek to " << 2*sizeof(int) + sizeof(FileType) + (_appendPos[fid] / ws ) * sizeof(unsigned) << endl;		    
		    pF->seek(2*sizeof(int) + sizeof(FileType) + ( _appendPos[fid] / ws) * sizeof(unsigned));
		    k = _appendPos[fid];
		}
		
		// cout << "k=" << k << " numPages = " << _numPages[fid] << endl;

		bool isEop = k >= _numPages[fid];
		
		while (!isEop)
		{
		    unsigned bm;
		    
		    pF->readByte((char*)&bm, sizeof(unsigned));
		    
		    // printBitMap(bm);
		       
		    if ( (bm & ~0) != ~0) 
		    {
			// free page available
			int l = 0;
			while (l < ws && ! isEop)
			{
			    if ( k+l < _numPages[fid])
			    {
				unsigned checkbit = bm >> l & ~(~0 << 1);
				if (checkbit == 0)
				{
				    
				    pageId = k + l;
				    fileId = fid;
				    
				    // cout << "FH: " << fid << "," <<  k << "," << l << " using pageId " << pageId << endl;
				    
				    unsigned f = ~(~0 << 1);
				    f = f << l;
				    // printBitMap(bm);
				    // cout << "1 bm=" << bm << endl;
				    bm = bm | f;
				    // printBitMap(bm);
				    
				    if ( _buMask[fileId] )
				    {
					
					if ( _fbmMask[fileId] == false )
					{	       						      							
					    fbmSize = getFBMSize(fileId);
					    fbm = new unsigned[fbmSize]; 							    
					    readFBM(fileId, fbm, pLockHandle);
					}
					else
					{
					    fbmSize = 0;
					}
					
					_fbmMask[fileId] = true;
				    }
				    // cout << "2 bm=" << bm << endl;
				    pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + k / BIT_PER_BYTE);
				    pF->writeByte((char*)&bm, sizeof(unsigned));
				    
				    pLockHandle->unlockDataFile(fid);

				    if ( _appendPos[fid] < k )
				    {
					// cout << "Increasing append pos to " << k << endl;
					_appendPos[fid] = k;
				    }
				    // cout << "Got page " << pageId << endl;
				    return;
				}
				l++;
			    }
			    else
			    {
				isEop = true;
			    }
			}	      
		    }
		    k+=ws;		    
		}
		pLockHandle->unlockDataFile(fid);	
	    }
	    catch ( Exception e )
	    {
		pLockHandle->unlockDataFile(fid);
		throw Exception(EXLOC, "File allocation error", e);
	    }		
	} 
	fid++;		    
    }
    Chain fileType;
    if ( ft == DATAFILE )
	fileType = Chain("Data");
    else if ( ft == SYSTEMFILE )
	fileType = Chain("System");
    else if ( ft == TEMP )
	fileType = Chain("Temp");
    
    throw Exception(EXLOC, fileType + Chain(" file pages exceeded "));
}


/* if a page is no longer used, it can be released using the releasePage method.
   Normally this is used, in case of delete operations or in case of temporarily used pages */

void CegoFileHandler::releasePage(int fileId, int pageId, CegoLockHandler *pLockHandle, unsigned* &fbm, int& fbmSize)
{

    File* pF;
    pF = getHandle(fileId);
    
    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try
    {

	if ( _buMask[fileId] )
	{	    
	    if ( _fbmMask[fileId] == false )
	    {
		
		fbmSize = getFBMSize(fileId);
		fbm = new unsigned[fbmSize]; 
		readFBM(fileId, fbm, pLockHandle);
	    }
	}
	else
	{
	    fbmSize = 0;
	}
	
	// pF->seek(0);
	
	unsigned bmid = pageId / ( sizeof(int) * BIT_PER_BYTE );
	unsigned bmoffset = pageId % ( sizeof(int) * BIT_PER_BYTE) ;
	
	// cout << "FH release : " << bmid << "," << bmoffset << " using pageId " << pageId << endl;
	
	pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + ( bmid * sizeof(int)));
	
	unsigned bm;
	pF->readByte((char*)&bm, sizeof(unsigned));
	
	unsigned f = ~(~0 << 1);
	f = f << bmoffset;
	// printBitMap(f);
	// printBitMap(bm);			    
	bm = bm & ~f;
	// printBitMap(bm);
	
	pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + ( bmid * sizeof(int)));
	pF->writeByte((char*)&bm, sizeof(unsigned));
	
	if ( _buMask[fileId] )
	    _fbmMask[fileId] = true;
	
	pLockHandle->unlockDataFile(fileId);
	
    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot release page", e);
    }
}

/* returns the number of total pages for the given fileId */

int CegoFileHandler::getNumPages(int fileId)
{

    try 
    {
	File* pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot get num pages", e);
    }

    return _numPages[fileId];
}

/* returns the number of used pages for the given fileId */

int CegoFileHandler::getNumUsedPages(int fileId, CegoLockHandler *pLockHandle)
{

    File *pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot get num used pages", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try
    {
	
	pF->seek(sizeof(int) + sizeof(FileType));
	
	int numPage;
	
	pF->readByte((char*)&numPage, sizeof(int));
	
	bool isEop = false;
	
	int p;
	
	int usedPages = 0 ;
	
	for ( p = 0; p < (numPage / (BIT_PER_BYTE * sizeof(int))) + 1; p++ )
	{
	    unsigned bm = 0;
	    pF->readByte((char*)&bm, sizeof(int));
	    
	    // printBitMap(bm);
	    
	    int i,j;
	    for (i = 0; i< sizeof(int); i++)
	    {
		for (j=0; j < BIT_PER_BYTE; j++)
		{
		    int one = 1;
		    
		    if ( one & bm )
			
			usedPages++;
		    
		    bm = bm >> 1;		
		}
	    }	      
	}

	pLockHandle->unlockDataFile(fileId);

	return usedPages;
    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot get num used paged", e);
    }
}

////////////////////////////////////////////////
////////////// private methods /////////////////
////////////////////////////////////////////////

/* to proof, if a given fileId/pageId  is already in use,
   the isClaimed method is used */
 
bool CegoFileHandler::isClaimed(int fileId, int pageId, CegoLockHandler *pLockHandle)
{

    File *pF;

    try 
    {
	pF = getHandle(fileId);
    }
    catch ( Exception e )
    {
	throw Exception(EXLOC, "Cannot check for claimed status", e);
    }

    pLockHandle->lockDataFile(fileId, CegoLockHandler::WRITE);

    try 
    {
    
	unsigned bmid = pageId / ( BIT_PER_BYTE * sizeof(int));
	unsigned bmoffset = pageId % ( BIT_PER_BYTE * sizeof(int));
	
	pF->seek(sizeof(int) + sizeof(FileType) + sizeof(int) + bmid * sizeof(int));
	
	unsigned bm;
	pF->readByte((char*)&bm, sizeof(unsigned));
	
	pLockHandle->unlockDataFile(fileId);
	
	unsigned checkbit = bm >> bmoffset & ~(~0 << 1);    
	
	if (  checkbit )
	{
	    return true;
	}
	return false;
    }
    catch ( Exception e )
    {
	pLockHandle->unlockDataFile(fileId);
	throw Exception(EXLOC, "Cannot check for claimed status", e);
    }
}

/* getHandle returns the file handle for the given file id. The
   file id must be valid and registered. If already open, the method
   returns the file handle. Otherwise, the corresponding file is opened
   for reading and writing and the handle is returned. */

File* CegoFileHandler::getHandle(int fileId)
{

    if ( fileId > FILMNG_MAXDATAFILE - 1 ) 
    {
	Chain msg = "File Id " + Chain(fileId)  + " out of valid range";
	throw Exception(EXLOC, msg);
    }

    if ( ! _isReg[fileId] )
    {
	Chain msg = "File Id " + Chain(fileId)  + " not registered";
	throw Exception(EXLOC, msg);
    }
 

    if (_fhList[fileId] == 0 )
    {

	File* pF = new File(_path[fileId]);
	
	try {
	    
	    pF->open(File::READWRITE);

	    /*
	    pF->seek(0);

	    
	    int tabSetId;
	    int numPage;
	    FileType ft;

	    pF->readByte((char*)&tabSetId, sizeof(int));

	    pF->readByte((char*)&ft, sizeof(FileType));

	    pF->readByte((char*)&numPage, sizeof(int));

	    _tabSetId[fileId] = tabSetId;
	    _fileType[fildId] = ft;
	    _numPages[fileId] = numPage;
	    */

	    _fhList[fileId] = pF;
	    _appendPos[fileId] = 0;

	}
	catch (Exception e)
	{
	    Chain msg = "Cannot get file handle " + Chain(fileId);
	    throw Exception(EXLOC, msg, e);
	}
    }
    File *pF = _fhList[fileId];
    return pF;
}

////////////////////////////////////////////////
//////// debugging/testing methods /////////////
////////////////////////////////////////////////


void CegoFileHandler::printBitMap(unsigned bm)
{    
    printf("--- BM -----------\n");
    
    int i,j;
    for (i = 0; i< sizeof(int); i++)
    {
	for (j=0; j < BIT_PER_BYTE; j++)
	{
	    int one = 1;
	    
	    if ( one & bm )
		
		printf("1");
	    else
		printf("0");
	    bm = bm >> 1;
	    
	}
    }
    printf("\n");
    
    printf("--------------\n");
    
}
