/**********************************************************************
 *
 * XRFFile.cpp
 * Copyright (C) 2003  UNESCO
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * 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 of the License, 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; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

////////////////////////////////////////////////////////////
// XrfFile.cpp: XrfFile object methods
//
////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "XRFFile.h"



//-----------------------------------------------------------------------------------
// IsisError  XrfFile::OpenXrf(const _TCHAR *fname, FileSystem::AccessMode mode
//				                              /* = FileSystem::FILE_READWRITE */)
//
// Open xrf_ file with file name fname 
//-----------------------------------------------------------------------------------
IsisError  XrfFile::OpenXrf(const _TCHAR *fname, FileSystem::AccessMode mode
				                              /* = FileSystem::FILE_READWRITE */)
{
  TCHAR xrfFileName[_MAX_PATH];
  _tcscpy(xrfFileName, fname);

  if (!CFileBase::Exists(xrfFileName)) {
    xrfFileName[strlen(xrfFileName) - 3] = 'X';
    xrfFileName[strlen(xrfFileName) - 2] = 'R';
    xrfFileName[strlen(xrfFileName) - 1] = 'F';
  }

    IsisError isis_error;
    xrfa_->xrfpos_ = -1;   // No block into buffer
	try {
        FileError rc = CFileBase::Open(xrfFileName, mode);
	}
	catch(CFileBaseException)
	{
		isis_error = ISIS_XRF_OPEN_ERROR;
        throw CIsisDbException(isis_error, __FILE__, __LINE__); 
	}
  	mg_s_long file_size   = CFileBase::FileSize(file_name);
	
	last_block_  = file_size/XRFBLKSIZE;

	if (last_block_<1 || last_block_*XRFBLKSIZE != file_size)
	{
		isis_error =  ISIS_XRF_CORRUPT;
        throw CIsisDbException(isis_error, __FILE__, __LINE__); 
	}

	return ISIS_NO_ERROR;
}
 
//////////////////////////////
 
 
IsisError  XrfFile::CreateXrf(const _TCHAR *fname)
// Creates an xrf_ file
{
    IsisError isis_error;
	try 
	{
        FileError rc = CFileBase::Create(fname);
    	// Initialize an empty block and write-it
		xrfa_->Init();
        xrfa_->xrfpos_ = 1; // Block number
		xrfa_->xrfeof_ = 1; // Last xrf_ block
        last_block_    = 1;		
        WriteXrf(1L);
	}
	catch(CFileBaseException)
	{
		isis_error = ISIS_XRF_CREATE_ERROR;
        throw CIsisDbException(isis_error, __FILE__, __LINE__);
	}
	
    return ISIS_NO_ERROR;
}
//-----------------------------------------------------------------------------------
// IsisError  XrfFile::CreateXrf(const _TCHAR *fname, mg_s_long nextMfn)
//
// Special xrf_ file creation for building xrf_ blocks of an existing mst
//-----------------------------------------------------------------------------------
IsisError  XrfFile::CreateXrf(const _TCHAR *fname, mg_s_long nextMfn)
// Creates an xrf_ file
{
    IsisError isis_error;
    mg_s_long nBlocks = (nextMfn-1)/MAXTIV + 1;       // Number of blocks to allocate
	try 
	{
        FileError rc = CFileBase::Create(fname);
		xrfa_->Init();
    	// Initialize an empty block and write-it
		for (int iBlock=1; iBlock<= nBlocks; iBlock++)
		{
			TRACE("\niBlock=%d", iBlock);
			xrfa_->xrfpos_ = iBlock;
			xrfa_->xrfeof_ = 0;
			if (iBlock == nBlocks)
			    xrfa_->xrfeof_ = 1;

			WriteXrf(iBlock);
		}
	}
	catch(CFileBaseException)
	{
		isis_error = ISIS_XRF_CREATE_ERROR;
        throw CIsisDbException(isis_error, __FILE__, __LINE__);
	}

    last_block_ = nBlocks;	
    return ISIS_NO_ERROR;
}

//------------------------------------------------------------------------------
// void XrfFile::UnPack(void)
//
// Unpack addresses of master file records. The current packed XRF block 
// contained in xrf_ structure will be unpacked in xrfa_ structure.
//
//  A master file address is packed as follow: addr = mfb*2048 + mfp
//
//  2048 = 2**11 e.g. mfp->[0:2047] and block number -> [1: 2**20]
//  2**20 = 1 048 576
//
// As we can put 127 addresses per block, then the limit on number of records
// is 127 * 1 048 576
//------------------------------------------------------------------------------
void XrfFile::UnPack(void)
{
   fix_endianness(xrf_->xrfpos_);

   xrfa_->xrfpos_ = xrf_->xrfpos_;
   if (xrfa_->xrfpos_ < 0)
   {
      xrfa_->xrfpos_ = - xrf_->xrfpos_;
      xrfa_->xrfeof_ = 1;
   }
   else
      xrfa_->xrfeof_ = 0;
 
   for (int i=0; i<MAXTIV; i++)
   {
      mg_s_long j = xrf_->xrftiv_[i];             // Packed value
      fix_endianness(j);
      bool b = (j < 0);
      if (b)
         j = - j;
      xrfa_->xrftiv_[i].xrfmfb_ = int(j / 2048L);   // Unpack block number
      if (b)
         xrfa_->xrftiv_[i].xrfmfb_ = -xrfa_->xrftiv_[i].xrfmfb_;
      xrfa_->xrftiv_[i].xrfmfp_ = int(j % 2048L);   // Unpack offset
   }
}
 
//------------------------------------------------------------------------------
// void XrfFile::Pack(void)
//
// Pack addresses of master file records. The current unpacked XRF block 
// contained in xrfa_ will be packed in xrf_ structure.
//------------------------------------------------------------------------------
void XrfFile::Pack(void)
{
   mg_s_long j = xrfa_->xrfpos_;
   if (xrfa_->xrfeof_ != 0)
      j = -j;
   xrf_->xrfpos_ = j;

 
   for (int i=0; i<MAXTIV; i++)
   {
      j = xrfa_->xrftiv_[i].xrfmfb_;    // Unpacked block number
      int b = (j < 0);
      if (b)
         j = - j;
      //-------------------------------------------
      // pack block number and offset in a mg_s_long
      //-------------------------------------------
      j = j * 2048 + xrfa_->xrftiv_[i].xrfmfp_;
      if (b) j = -j;
      xrf_->xrftiv_[i] = j;     // Put value in xrf_
   }
}
 
//------------------------------------------------------------------------------
// int XrfFile::GetMfp(mg_s_long mfn, mg_s_long& mfb, int& mfp)
//
// Gets the address of master file data record with MFN "mfn" by accessing the 
// xrf_ file). mfb is the master file block number [1:NBLK] and mfp is the offset
// in the master file block where the record starts [0:BLKSIZE-1]
//
//  Returns   Active              = Record active
//            LogicalyDeleted     = Record logically deleted
//            PhysicalyDeleted    = Record physically deleted
//            EndOfFile
//  Note: mfp can be >=      than BLKSIZE if the inverted file
//  wasn't updated, then true mfp is mfp%BLKSIZE
//------------------------------------------------------------------------------
int XrfFile::GetMfp(mg_s_long mfn, mg_s_long& mfb, int& mfp)
{
	PRECONDITION(mfn>0);
	PRECONDITION(last_block_>0);
    int  comp;
    mg_s_long comb;
    int  rc;
	
    mg_s_long psxrf=(mfn-1)/MAXTIV+1;       // Block number
	if (psxrf > last_block_)
	{
        rc = EndOfFile;                // Inexistent record (eof)
		return rc;
		throw CIsisDbException(ISIS_XRF_INVALID_BLOCK, __FILE__, __LINE__);
	}
    if (psxrf != xrfa_->xrfpos_) 
	{                                  // If current block in xrf_ different
		try
		{
            ReadXrf(psxrf);            // then read the block
		}
		catch (CFileBaseException e)
		{
            throw CIsisDbException(ISIS_XRF_READ_ERROR, __FILE__, __LINE__);
		}

    }
    int i = int((mfn-1) % MAXTIV);     // Index
    comb = xrfa_->xrftiv_[i].xrfmfb_;     // Master file block #
    comp = xrfa_->xrftiv_[i].xrfmfp_;     // Offset in block

	if (comb > 0)
        rc = Active;                   // Record active 
    else if (comb < 0 && comp > 0)
        rc = LogicalyDeleted;          // Record logicaly deleted 
	else if (comb == -1 && comp == 0)
        rc = PhysicalyDeleted;         // Record physically deleted 
    else if (comb == 0 && comp == 0)
        rc = EndOfFile;                // Inexistent record (eof)
	else
	{
        throw CIsisDbException(ISIS_XRF_INVALID_STATUS, __FILE__, __LINE__); 
	}
    mfb = labs(comb);
    mfp = comp;
	if (rc == Active)
	{
	    ASSERT(mfb>0 && mfb <= MAXMFB);
		ASSERT(mfp>=0 && mfp < 2048);
	    int mfpTemp = mfp%XRFBLKSIZE;
	    ASSERT(mfpTemp>=0 && mfpTemp<XRFBLKSIZE);
	}
    return(rc);
}
 
void XrfFile::Grow(mg_s_long from_lastb, mg_s_long to_lastb)
{
	for (mg_s_long i=from_lastb+1; i<=to_lastb; i++)
	{
        //--------------------
        // append empty blocks
        //--------------------
 		xrfa_->Init();
		xrfa_->xrfpos_ = i;
		xrfa_->xrfeof_ = (i==to_lastb) ? 1 : 0; // Eof flag
	    try
		{
           WriteXrf(i);           // Write xrf_ at block address xrfb
		}
		catch (CFileBaseException)
		{
			throw(ISIS_XRF_WRITE_ERROR);
		}
	}
}
 
//------------------------------------------------------------------------------
// void XrfFile::PutMfp(mg_s_long mfn, mg_s_long mfb, int mfp, int status)
//
// Puts the address of a master file record (mfb,mfp) as the address for MFN 
// "mfn" in xrf_ file
//------------------------------------------------------------------------------
void XrfFile::PutMfp(mg_s_long mfn, mg_s_long mfb, int mfp, int status)
{
}
