/**********************************************************************
 *
 * Master.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.
 *
 *********************************************************************/

///////////////////////////////////////                                   */
//  Master.h: Master File Record object
///////////////////////////////////////                                   */
 
#ifndef __MASTER_RECORD_H__
#define __MASTER_RECORD_H__


#include "BlkFile.h"
#include <vector>
#include <map>
#include <sstream>


 
// Length in bytes of master file header 
#define MFCTLLEN sizeof(SMfHeader)+sizeof(SMfHeader) // Length in bytes of master file header

const int MFALIGN   =    2;   // Master file record alignment
const int MFBLKSIZE =  512;   // Master file block size

typedef int16_t   ISIS_INT;  // used to be defined as 'short int' which worked for 32-bit Unix, but not 64-bit
typedef mg_s_long ISIS_LONG; // used to be defined as 'long int' which worked for 32-bit Unix, but not 64-bit

#ifdef UNIX_BIREME
    //Length of Leader in master record
	inline 	int GetLeaderSize()
	{
		return (2*sizeof(ISIS_LONG)+6*sizeof(ISIS_INT));
	}
    // Mini Leader length guaranteed to be in same block
	inline 	int GetMinLeaderSize()
	{ 
		return (2*sizeof(ISIS_LONG)+4*sizeof(ISIS_INT));
	}
#else
    //Length of Leader in master record
	inline 	int GetLeaderSize() 
	{
		return (2*sizeof(ISIS_LONG)+5*sizeof(ISIS_INT)); 
	}
    // Mini Leader length guaranteed to be in same block
	inline 	int GetMinLeaderSize()
	{ 
		return (2*sizeof(ISIS_LONG)+3*sizeof(ISIS_INT));
	}
#endif

// Size of Mfn and Mfrl in the leader part
inline int GetSubLeaderSize() { return sizeof(ISIS_LONG)+sizeof(ISIS_INT); }
 
inline int GetDirEntrySize() { return (3*sizeof(ISIS_INT)); }
 
 
const TCHAR SYSRSEP      =  TCHAR('%'); // Repeatable field separator
const TCHAR SUBFIELD_SEP =  TCHAR('^'); // Subfield separator

////////////////////////////////////////
// Directory entry aggregate structure
//
struct DirEntry  
{
   ISIS_INT  tag_;
   ISIS_INT  pos_;
   ISIS_INT  len_;
   DirEntry() 
	   : tag_(0), pos_(0), len_ (0) {  }

   DirEntry(ISIS_INT tag, ISIS_INT pos, ISIS_INT len)
	   : tag_(tag), pos_(pos), len_ (len) {  }
   bool operator==(const DirEntry& rhs) const
   {
	   if (tag_!=rhs.tag_ || pos_!=rhs.pos_ || len_!=rhs.len_)
		   return false;
	   return true;
   }
   bool operator!=(const DirEntry& rhs) const
   {
	   if (tag_!=rhs.tag_ || pos_!=rhs.pos_ || len_!=rhs.len_)
		   return true;
	   return false;
   }

};
 

//////////////////////////////////////////////////////////////////////////
// Data Master record aggregate structure
//
struct LeaderAndDirectory 
{
   ISIS_LONG        mfn_;            // Master file number
   ISIS_INT         mfrl_;           // Record length
   ISIS_LONG        mfbwb_;          // Backward pointer block number
   ISIS_INT         mfbwp_;          // Backward pointer offset
   ISIS_INT         base_;           // Offset to begining of variable fields
   ISIS_INT         nvf_;            // Number of directory entries
   ISIS_INT         status_;         // 1 = DELETED
   std::vector<DirEntry> dir_;            // Directory

   LeaderAndDirectory()
   {
	   Clear();
   }
   LeaderAndDirectory(const LeaderAndDirectory& src)
   {
	   Copy(src);
   }

   LeaderAndDirectory& operator=(const LeaderAndDirectory& rhs)
   {
	   if (&mfn_ != &rhs.mfn_)
	   {
		   Copy(rhs);
	   }
	   return *this;
   }

   void Copy(const LeaderAndDirectory& rhs)
   {
	   mfn_    = rhs.mfn_;
	   mfrl_   = rhs.mfrl_;
	   mfbwb_  = rhs.mfbwb_;
	   mfbwp_  = rhs.mfbwp_;
       base_   = rhs.base_;
       nvf_    = rhs.nvf_;
	   status_ = rhs.status_;
	   dir_    = rhs.dir_;
   }
   void Clear()
   {
	   mfn_ = mfrl_ = mfbwb_ = mfbwp_ = base_ = nvf_ = status_ = 0;
	   dir_.clear();
   }

   bool operator==(const LeaderAndDirectory& rhs) const;
   bool operator!=(const LeaderAndDirectory& rhs) const;
   int GetFieldSize();
};

cstringstream& operator<<(cstringstream& s, const LeaderAndDirectory& leader);
cstringstream& operator>>(cstringstream& s, LeaderAndDirectory& leader);

////////////////////////////////////////////////////////////
//
 
/////////////////////////////////
// Data master file record class
//

typedef std::map<int, ustring> TAG2FIELD;
class MfFile;
class MstFile;
class IsisDb;
class ACE_InputCDR;
class ACE_OutputCDR;
class UMfRecord;


class  MfRecord
{
	friend class MfFile;
	friend class MstFile;
	friend class IsisDb;
	friend class UMfRecord;
    friend int operator>>(ACE_InputCDR& cdr, MfRecord& mfr);
    friend int operator<<(ACE_OutputCDR& cdr, MfRecord& mfr);
protected:

	CFdt*                pFdt_;        // Use a copy of the FDT
	cstring              record_;      // Record in continuous storage
	                                   // always as ANSI!
	
	LeaderAndDirectory           leader_directory_;
	
	TAG2FIELD        fields_;
	std::vector<int> tags_;
    static char      m_buf[MFBLKSIZE];      // Buffer to hold 1 File block

protected: // Additional data
	short int state_;         // Record state in memory
	mg_s_long      mfb_read_;      // Record was read at Block number [1...]
	int       mfp_read_;      // Record was read at position in block
	int       mflen_read_;    // Record length was when record read 

protected:
   void         Copy(const MfRecord& rhs); 
   
   void         AddToRecord(const char* p, int len);
   int          AddDirEntry(const DirEntry& d);
   int          AddDirEntry(int tag, int pos, int len);
   int          DelDirEntry(int i);
   void         SetMfrl(int mfrl);
   void         SetMfbwb(mg_s_long mfbwb);
   void         SetMfbwp(int mfbwp);
   void         SetBase(int base);
   void         SetNvf(int nvf);
   void         SetStatus(int s);
   int          SetDirEntry(int i, int tag, int pos, int len);
   int          SetDirEntry(int i, const DirEntry& d);
   void         SetMfbRead(mg_s_long b);
   void         SetMfpRead(int pos);
   void         SetMflenRead(int len);

 
   void         SetSubLeaderAndDirectory(const char* p, int len);


   void         MakeTagToFieldMap();
   void         MakeTagVector();

   mg_s_long         ComputeMfrl();

   void         Copy(UMfRecord& rhs);           
public:
	enum { Record_Empty, Record_Read, Record_Changed, Record_Created };

public:
   //-----------------
   // Public Interface
   //-----------------
   MfRecord(CFdt* pFdt=NULL);                // Default constructor   
   virtual ~MfRecord();                      // Destructor

   MfRecord(const MfRecord& rhs);            // Copy constructor
   MfRecord& operator=(const MfRecord& rhs); // Assignment
 
   MfRecord(const UMfRecord& umfr);
   MfRecord& operator=(const UMfRecord& rhs);

   void Clear();
   void ClearData();


   void ReadLeader(MfFile& f, mg_s_long daddr);
   bool Read (MfFile& f, mg_s_long mfb, int mfp);
   void Write(MfFile& f, mg_s_long mfb, int mfp);

   void MakeISORecord(std::string& s);
 
   mg_s_long         GetMfn()           const; // Get MFN
   int          GetMfrl()          const; // Get Master File Record Length
   mg_s_long         GetMfbwb()         const; // Get Master File Backward Block number
   int          GetMfbwp()         const; // Get Master File Backward block position
   int          GetBase()          const; // Get Variable fields starting byte
   int          GetNvf()           const; // Get Number of entries in the directory
   int          GetStatus()        const; // Get Record Status
   DirEntry     GetDirEntry(int i) const; // Get a reference to the ieme directory entry
   int          GetState()         const; // Get record state in memory
   mg_s_long         GetMfbRead()       const;
   int          GetMfpRead()       const;
   int          GetMflenRead()     const;

   ustring     GetAllFields(); // ANSI or UNICODE
   cstring     GetRecordAsString() { return record_; }
   const char* GetRecord()         { return record_.c_str(); }
   int         GetRecordLength()   { return record_.length(); }

   void        ConvertToAnsi();


   bool         GetField(int tag, std::string& s);  // Get field content for field with tag "tag" 
   bool         AddField(int tag, std::string& s);
   bool         DeleteField(int tag);
   bool         ReplaceField(int tag, std::string& s);
   bool         AddOccurence(int tag, std::string& o);

   int          GetFieldTag(int i);
   int          GetFieldPos(int i);
   int          GetFieldLen(int i);
 
   void         SetMfn(mg_s_long mfn);
   void         SetState(int s);

   mg_s_long         GetDirectorySize();
   int          GetFieldSize()     { return leader_directory_.GetFieldSize(); }

   std::vector<int>  GetFieldTags(); // Get tags of fields in increasing order,
                                     // Occurences made only 1 field
   void         ExtractSubLeader();
   void         ExtractLeader();
   bool         CheckIntegrity();
   bool         CheckLeader();
   bool         CheckLeaderAndDirectory(); 

   bool operator==(const MfRecord& rhs) const
   {
	   return (record_ == rhs.record_); 
   }
   bool operator!=(const MfRecord& rhs) const
   {
	   return (record_ != rhs.record_); 
   }

   void         SetRecord(const char *s) { record_ = s;  DecomposeRecord();}
   void         SetRecord(const cstring &s) 
				{ record_ = s;  DecomposeRecord();}
   void         ComposeRecord();
   bool         DecomposeRecord();

   bool         CheckRecord();
   void         Dump();
} ;
 
//////////////////////////////
 



inline void MfRecord::AddToRecord(const char* p, int len)
{
	record_ += cstring(p,len);
} 

/////////////////////////////

inline MfRecord::MfRecord(CFdt* pFdt) 
// Makes a Master file Record object
{
	Clear();
	pFdt_ = pFdt;
   // Nothing more to do
}
 
inline void MfRecord::Copy(const MfRecord& rhs)           
{
   record_     = rhs.record_;
   leader_directory_     = rhs.leader_directory_;
   fields_     = rhs.fields_;
   state_      = rhs.state_;
   mfb_read_   = rhs.mfb_read_;
   mfp_read_   = rhs.mfp_read_;
   mflen_read_ = rhs.mflen_read_;
   tags_       = rhs.tags_;         
	pFdt_      = rhs.pFdt_;
}

inline MfRecord::MfRecord(const MfRecord& rhs)     // Copy constructor
{ 
   Copy(rhs);
}

inline MfRecord& MfRecord::operator=(const MfRecord& rhs) // Assignment
{
   if (&leader_directory_ != &rhs.leader_directory_)
   {
   	   Copy(rhs);
   }
   return *this;
}
 
inline void MfRecord::Clear()
{
   tags_.clear();
   record_.erase();
   leader_directory_.Clear();
   fields_.clear();
   SetBase(GetLeaderSize());
   SetMfrl(GetLeaderSize());
   state_ = Record_Empty;
   mfb_read_ = mfp_read_ = mflen_read_ = 0;
   pFdt_ = NULL;
}

// Clear the data part but keep other info intact so that
// it appears as an empty record
inline void MfRecord::ClearData()
{
   tags_.clear();
   record_.erase();
   leader_directory_.Clear();
   fields_.clear();
   SetBase(GetLeaderSize());
   SetMfrl(GetLeaderSize());
}


//////////////////////////////
 
inline MfRecord::~MfRecord()
{
}
 
//////////////////////////////
 
inline mg_s_long MfRecord::GetMfn() const
// Returns Master file number
{
   return leader_directory_.mfn_;
}
 
//////////////////////////////
 
inline int MfRecord::GetMfrl() const
// Returns Master file record length
{
   return leader_directory_.mfrl_;
}
 
//////////////////////////////
 
inline mg_s_long MfRecord::GetMfbwb() const
// Returns backward pointer block number
{
   return leader_directory_.mfbwb_;
}
 
//////////////////////////////
 
inline int MfRecord::GetMfbwp() const
// Returns backward pointer offset
{
   return leader_directory_.mfbwp_;
}
 
//////////////////////////////
 
inline int MfRecord::GetBase() const
// Returns base value to field data
{
    return leader_directory_.base_;
}
 
//////////////////////////////
 
inline int MfRecord::GetNvf() const
// Returns the number of directory entries
// in the master file record
{
   return leader_directory_.nvf_;
}

//////////////////////////////
 
inline int MfRecord::GetStatus() const
// Returns the status of the record
{
   return leader_directory_.status_;
}

//////////////////////////////
 
 
//////////////////////////////
 
inline DirEntry MfRecord::GetDirEntry(int i) const
// Returns the ith dir entry
{
   return leader_directory_.dir_[i];
} 

//////////////////////////////
 
inline int MfRecord::GetState() const
{
   return state_;
} 

//////////////////////////////
 
inline mg_s_long MfRecord::GetMfbRead() const
{
   return mfb_read_;
} 
//////////////////////////////
 
inline int MfRecord::GetMfpRead() const
{
   return mfp_read_;
} 
    
//////////////////////////////
 
// Get tags of fields in increasing order,
inline std::vector<int> MfRecord::GetFieldTags()
{
	PRECONDITION(tags_.size() == fields_.size());
	return tags_;
}
//////////////////////////////
 
inline int MfRecord::GetMflenRead() const
{
   return mflen_read_;
} 
    
///////////////////////////////////////////////////////////////////////////////
 
inline void MfRecord::SetMfn(mg_s_long mfn)
// Set Master file number
{
	assert(mfn>0);
    leader_directory_.mfn_ = mfn;
}
//////////////////////////////
 
inline void MfRecord::SetMfrl(int mfrl)
// Set Master file record length
{
	assert(mfrl>0);
    leader_directory_.mfrl_ = mfrl;
}
//////////////////////////////
 
inline void MfRecord::SetMfbwb(mg_s_long mfbwb)
// Sets backward pointer block number to mfbwb
{
	assert(mfbwb>=0);
    leader_directory_.mfbwb_ = mfbwb;
}

//////////////////////////////
 
inline void MfRecord::SetMfbwp(int mfbwp)
// Sets backward pointer block offset to mfbwp
{
	assert(mfbwp>=0);
    leader_directory_.mfbwp_ = mfbwp;
}
//////////////////////////////
 
inline void  MfRecord::SetBase(int base)
// Sets the base value in the record aggregate
{
	assert(base>=0);
    leader_directory_.base_ = base;
}

//////////////////////////////
 
inline void MfRecord::SetNvf(int nvf)
// Sets the number of entries in the directory part
{
	assert(nvf>=0);
    leader_directory_.nvf_ = nvf;
}
 
//////////////////////////////
 
inline void MfRecord::SetStatus(int s)
// Sets the status of the record
{
   leader_directory_.status_ = s;
}
 
//////////////////////////////
 
inline void MfRecord::SetState(int s)
// Sets the status of the record
{
	assert(s==MfRecord::Record_Empty || s==MfRecord::Record_Read 
		|| s==MfRecord::Record_Changed || s==MfRecord::Record_Created);
   state_ = s;
}

inline void MfRecord::SetMfbRead(mg_s_long b)
{
	mfb_read_ = b;
}

inline void MfRecord::SetMfpRead(int pos)
{
	mfp_read_ = pos;
}

inline void MfRecord::SetMflenRead(int len)
{
	mflen_read_ = len;
}

inline int MfRecord::GetFieldTag(int i)
{
	return leader_directory_.dir_[i].tag_;
}
inline int MfRecord::GetFieldPos(int i)
{
	return leader_directory_.dir_[i].pos_;
}

inline int MfRecord::GetFieldLen(int i)
{
	return leader_directory_.dir_[i].len_;
}

#endif
