/**********************************************************************
 *
 * MFFile.h
 * 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.
 *
 *********************************************************************/

////////////////////////////////////////////////////////////
// MFFILE.H: Class definition for blocked I/O on the master
//           file
////////////////////////////////////////////////////////////
 
#ifndef _MFFILE_H_
#define _MFFILE_H_
#ifndef _MASTER_H_
#include "Master.h"
#endif
#ifndef _BLKFILE_H_
#include "BlkFile.h"
#endif
#include <string>

enum MfLockType
{
	MF_READ_LOCK,
	MF_WRITE_LOCK
};

struct SMfLockHeader
{
	mg_s_long lockProtect_; // Serialize access to the file lock members
	mg_s_long readLock_;    // Shared (or read_only) file lock
	mg_s_long writeLock_;   // Exclusive (or write-only) file lock

	SMfLockHeader() : lockProtect_(0), readLock_(0), writeLock_(0)
	{}
};

enum  { USERMST=0, MSGMST=1 };
//  binary  hex
//  ------  ---
//  0000    0
//  0001    1
//  0010    2
//  0011    3

class MfAddress
{
private:
	mg_s_long  m_mfb;
	short m_mfp;
	char  m_status;
public:
	MfAddress() { m_status = 0x00; }
	MfAddress(mg_s_long mfb, short mfp)
	{ ASSERT(mfb>0); ASSERT(mfp>=0); m_mfb = mfb; m_mfp = mfp; m_status = 0x03; }

	mg_s_long getMfb()          { ASSERT(m_status & 0x01); return m_mfb; }
	short getMfp()         { ASSERT(m_status & 0x02); return m_mfp; } 

	void setMfb(mg_s_long mfb)  { ASSERT(mfb>0);  m_mfb = mfb; m_status |= 0x01; }
	void setMfp(short mfp) { ASSERT(mfp>=0); m_mfp = mfp; m_status |= 0x02; }
	bool isValid()         { return (m_status == 0x03); }
};
 
/////////////////////////////////////////////////////////////////////////
// Master File header record aggregate structure
//
struct  SMfHeader 
{
   ISIS_LONG   ctlmfn_;    // Always 0
   ISIS_LONG   nxtmfn_;    // MFN to be assigned to next record created
   ISIS_LONG   nxtmfb_;    // Last block number allocated (1st is 1)
   ISIS_INT    nxtmfp_;    // Offset to next available position in last block (1:MFBLKSIZE)
   ISIS_INT    mftype_;    // Type of master file (USERMST or MSGMST)
   ISIS_LONG   reccnt_;
   ISIS_LONG   mfcxx1_;
   ISIS_LONG   mfcxx2_;
   ISIS_LONG   mfcxx3_;
public:
   SMfHeader(const char *dbname="");
   ~SMfHeader()                 { }
};
 
//--------------------------------------------------------------------------
// SMfHeader::SMfHeader(const char *dbname /* ="" */)
//
// Default constructor, initializes the Master file header to default values
//--------------------------------------------------------------------------
inline SMfHeader::SMfHeader(const char *dbname /* ="" */)
{
   ctlmfn_ = 0;
   nxtmfn_ = 1L;
   nxtmfb_ = 1L;
   nxtmfp_ = MFCTLLEN+1;
   mftype_ = USERMST;
   reccnt_ = mfcxx1_=mfcxx2_=mfcxx3_=0;
}
 
//////////////////////
// Master file class
//
class MfFile : public BlkFile  
{
   friend class IsisDb;
   friend class MfRecord;

protected:
   SMfHeader mfh_;            // Header structure
   mg_s_long nextaddr_;            // Address of next record
   char buf_[MFBLKSIZE];      // Buffer to hold 1 Master File block
   bool hdr_created_;         // True if 1st block exists
   int  fileError_;
   mg_s_long lastWrittenBlock_;

protected:  
   void WriteMfRecord(mg_s_long mfb, int mfp, MfRecord &r);
   void AppendNewBlock();

public:
   void setNextMfn(mg_s_long mfn)       // Set MFN to be assigned to next record created
   { mfh_.nxtmfn_ = mfn; }

protected:
   void setNextMfb(mg_s_long nxtmfb_)  // Set last block number allocated (1st is 1)
   { mfh_.nxtmfb_ = nxtmfb_; }
   void setNextMfp(int nxtmfp_)   // Set offset to next available position in last block
   { mfh_.nxtmfp_ = nxtmfp_; }
   int setm_mftype(int mftype_)     // Set type of master file (USERMST or MSGMST)
   { mfh_.mftype_ = mftype_; }     
   virtual void ReadBlk(void* d, mg_s_long b)  { BlkFile::ReadBlk(d, b);  }
   virtual void WriteBlk(void* d, mg_s_long b) { BlkFile::WriteBlk(d, b); }
   void ReadMfHdr(SMfHeader &h);
   void ReadMfHdr();
   void WriteMfHdr(SMfHeader &h);
   void WriteMfHdr();
   void SetLastWrittenBlock(mg_s_long b) { lastWrittenBlock_ = b; }
public:
   MfFile();
   virtual ~MfFile();

   int  OpenMf(const TCHAR* name, FileSystem::AccessMode mode = FileSystem::FILE_READWRITE);
   int  CreateMf(const TCHAR* name, int MSTtype = USERMST);
   void CloseMf() { if (ReadyForWriting()) WriteMfHdr(); Close(); }

   // Accessors for the Master File Header
   mg_s_long GetNextMfn()        // Get MFN to be assigned to next record created
   { return mfh_.nxtmfn_; }
   mg_s_long GetNextMfb()        // Get last block number allocated (1st is 1)
   { return mfh_.nxtmfb_; }
   int GetNextMfp()         // Get offset to next available position in last block
   { return mfh_.nxtmfp_; }
   int GetMftype()          // Get type of master file (USERMST or MSGMST)
   { return mfh_.mftype_; }   
   SMfHeader& GetSMfHeader() { return mfh_; }


   void CreateMfRecord(MfRecord &m, mg_s_long &mfb, int &mfp);
   void AddMfRecord(MfRecord &m, mg_s_long &mfb, int &mfp);
   void UpdateMfRecord(MfRecord &m, mg_s_long &mfb, int &mfp);
   bool ReadMfRecord(mg_s_long mfb, int mfp, MfRecord &r);

   void ReadMfRecord(mg_s_long addr, MfRecord &r);
   void WriteMfRecord(mg_s_long addr, MfRecord &r);
   void RewindMf();
   bool ReadMfNext(MfRecord &r);
   void WriteMfNext(MfRecord &r);
   void ReadLeader(mg_s_long daddr, MfRecord& m)
   {
	   m.ReadLeader(*this, daddr);
   }


   void LockMfHdr();
   void UnlockMfHdr();
   void LockMfr(mg_s_long mfn);
   void UnlockMfr(mg_s_long mfn);

public: // File lock functions
	void InitFileLockHdr(SMfLockHeader &hdr);
	FileError ResetFileLock();
	FileError WriteFileLockHdr(const SMfLockHeader &hdr);
	FileError ReadFileLockHdr(SMfLockHeader &hdr);
	int LockFile(MfLockType l_type = MF_WRITE_LOCK);
	int UnlockFile(MfLockType l_type = MF_WRITE_LOCK);


   void PrintMfRecord(MfRecord &r);
   void PrintMfHdr(SMfHeader &h);
   void PrintMfHdr();
};
 

class CMfIterator
{
	const MfFile& m_mf;
	mg_s_long cur;
public:
	CMfIterator(const MfFile& mf) : m_mf(mf) { }

	bool More() const;
	void Advance();

	MfRecord Current() 
	{ 
		MfRecord mfr(NULL);
		
		return mfr;
	}
};

 
//////////////////////////////
 
inline void  MfFile::LockMfHdr()
//  Locks the master file header record
{
	double_t dwPos   = 0;
	double_t dwCount = sizeof(SMfHeader);
	//CFile::LockRange(dwPos, dwCount);
}
 
//////////////////////////////
 
inline void MfFile::UnlockMfHdr()
//  Unlocks the master file header record
{
	double_t dwPos   = 0;
	double_t dwCount = sizeof(SMfHeader);
	//CFile::UnlockRange(dwPos, dwCount);
}
 
//////////////////////////////

inline void MfFile::ReadMfHdr()
//  Reads the master file header record
{
   ReadBlk(buf_, 1L);
   TRACE(_T("\nsizeof(SMfHeader)=%d"), sizeof(SMfHeader));
   ::MoveMemory(&mfh_, buf_, sizeof(SMfHeader));

   fix_endianness(mfh_.ctlmfn_);
   fix_endianness(mfh_.nxtmfn_);
   fix_endianness(mfh_.nxtmfb_);
   fix_endianness(mfh_.nxtmfp_);
   fix_endianness(mfh_.mftype_);
   fix_endianness(mfh_.reccnt_);
   fix_endianness(mfh_.mfcxx1_);
   fix_endianness(mfh_.mfcxx2_);
   fix_endianness(mfh_.mfcxx3_);
}
 
//////////////////////////////
 
inline void MfFile::WriteMfHdr()
//  Updates the master file header record
{
    if (hdr_created_) 
	   ReadBlk(buf_, 1L);
    TRACE(_T("\nsizeof(SMfHeader)=%d"), sizeof(SMfHeader));
    ::MoveMemory(buf_, &mfh_, sizeof(SMfHeader));
    WriteBlk(buf_, 1L);
}
 
 
//////////////////////////////
 
inline void MfFile::ReadMfHdr(SMfHeader &h)
//  Reads the master file header record
{
   ReadBlk(buf_, 1L);
   ::MoveMemory(&h, buf_, sizeof(SMfHeader));

   fix_endianness(h.ctlmfn_);
   fix_endianness(h.nxtmfn_);
   fix_endianness(h.nxtmfb_);
   fix_endianness(h.nxtmfp_);
   fix_endianness(h.mftype_);
   fix_endianness(h.reccnt_);
   fix_endianness(h.mfcxx1_);
   fix_endianness(h.mfcxx2_);
   fix_endianness(h.mfcxx3_);
}
 
//////////////////////////////
 
inline void MfFile::WriteMfHdr(SMfHeader &h)
//  Updates the master file header record
{
   if (hdr_created_)
	   ReadBlk(buf_, 1L);
   ::MoveMemory(buf_, &h, sizeof(SMfHeader));
   WriteBlk(buf_, 1L);
}
 
 
inline void MfFile::PrintMfHdr()
{
   PrintMfHdr(mfh_);
}
 
//////////////////////////////
 
inline bool MfFile::ReadMfNext(MfRecord &r)
// Reads the next physically record
{
	if (nextaddr_ >= CFileBase::FileSize(file_name))
	    return false;
   ReadMfRecord(nextaddr_, r);
   return true;
}
 
//////////////////////////////
 
inline void MfFile::WriteMfNext(MfRecord &r)
// Writes physically at next position
{
   WriteMfRecord(nextaddr_, r);
}
#endif
