/**********************************************************************
 *
 * Master.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.
 *
 *********************************************************************/

////////////////////////////////////////////////////////////
//  MASTER.CPP: Master file record object methods
//                                                                        
////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <iomanip>
#include "XRFFile.h"
#include "IsisUtil.h" 
#include "Master.h"
#include "MFFile.h"
#include "IsisDb.h"
#include "Unimarc.h"
#include <algorithm>


using namespace std;

   

char MfRecord::m_buf[MFBLKSIZE];      // Buffer to hold 1 File block



/////////////////////////////////////////////////////////////////////
// LeaderAndDirectory methods

int LeaderAndDirectory::GetFieldSize()
{
	int field_len = 0;
	for (int i=0; i!=nvf_; ++i)
		field_len  += dir_[i].len_;
	return field_len;
}

bool LeaderAndDirectory::operator==(const LeaderAndDirectory& rhs) const
{
    if (mfn_ != rhs.mfn_)         // Master file number
		return false;
    if (mfrl_ != rhs.mfrl_)       // Record length
		return false;
	if (mfbwb_ != rhs.mfbwb_)     // Backward pointer block number
		return false;
    if (mfbwp_ != rhs.mfbwp_)     // Backward pointer offset
		return false;
    if (base_ != rhs.base_)       // Offset to begining of variable fields
		return false;
    if (nvf_ != rhs.nvf_)         // Number of directory entries
		return false;
    if (status_ != rhs.status_)   // 1 = DELETED
		return false;
	for(std::vector<DirEntry>::size_type i=0; i!=nvf_; i++) // Directory
	{
		if (dir_[i]!=rhs.dir_[i])
		    return false;
	}
    return true;
}

bool LeaderAndDirectory::operator!=(const LeaderAndDirectory& rhs) const
{
    if (mfn_ != rhs.mfn_)         // Master file number
		return true;
    if (mfrl_ != rhs.mfrl_)       // Record length
		return true;
	if (mfbwb_ != rhs.mfbwb_)     // Backward pointer block number
		return true;
    if (mfbwp_ != rhs.mfbwp_)     // Backward pointer offset
		return true;
    if (base_ != rhs.base_)       // Offset to begining of variable fields
		return true;
    if (nvf_ != rhs.nvf_)         // Number of directory entries
		return true;
    if (status_ != rhs.status_)   // 1 = DELETED
		return true;
	for(std::vector<DirEntry>::size_type i=0; i!=nvf_; i++) // Directory
	{
		if (dir_[i]!=rhs.dir_[i])
		    return true;
	}
    return false;
}
//-------------------------------------------------------------------------------
// ostream& operator<<(ostream& s, const LeaderAndDirectory& leader_directory)
//
// Overloaded instance of the output operator for LeaderAndDirectory objects
//---------------------------------------------------------------------------------
cstringstream& operator<<(cstringstream& s, const LeaderAndDirectory& leader_directory)
{
	s.write((char *) &leader_directory.mfn_,    sizeof(ISIS_LONG));
	s.write((char *) &leader_directory.mfrl_,   sizeof(ISIS_INT));
	s.write((char *) &leader_directory.mfbwb_,  sizeof(ISIS_LONG));
	s.write((char *) &leader_directory.mfbwp_,  sizeof(ISIS_INT));
#ifdef UNIX_BIREME
	ISIS_INT dummy = 0;
	s.write((char *) &dummy,   sizeof(ISIS_INT));
#endif

	s.write((char *) &leader_directory.base_,   sizeof(ISIS_INT));
	s.write((char *) &leader_directory.nvf_,    sizeof(ISIS_INT));
	s.write((char *) &leader_directory.status_, sizeof(ISIS_INT));

	int n = leader_directory.dir_.size();
    assert(n==leader_directory.nvf_);	
	for(vector<DirEntry>::size_type i=0; i!=leader_directory.dir_.size(); i++)
	{
	     assert(leader_directory.dir_[i].tag_>0 && leader_directory.dir_[i].tag_<SHRT_MAX);
		 DirEntry de = leader_directory.dir_[i];
         s.write((char *) &de, GetDirEntrySize());
	}
	
	return s;
}

//----------------------------------------------------------------------------------
// istream& operator>>(istream& s, LeaderAndDirectory& leader_directory)
//
// Overloaded instance of the input operator for LeaderAndDirectory objects
//----------------------------------------------------------------------------------
cstringstream& operator>>(cstringstream& s, LeaderAndDirectory& leader_directory)
{
	if (!s.good()) return s;

	ios::iostate err = ios::goodbit;
	try
	{
		s.read((char *) &leader_directory.mfn_,    sizeof(ISIS_LONG));
	    s.read((char *) &leader_directory.mfrl_,   sizeof(ISIS_INT));
	    s.read((char *) &leader_directory.mfbwb_,  sizeof(ISIS_LONG));
	    s.read((char *) &leader_directory.mfbwp_,  sizeof(ISIS_INT));
#ifdef UNIX_BIREME
	    ISIS_INT dummy;
	    s.read((char *) &dummy,   sizeof(ISIS_INT));
#endif
	    s.read((char *) &leader_directory.base_,   sizeof(ISIS_INT));
	    s.read((char *) &leader_directory.nvf_,    sizeof(ISIS_INT));
	    s.read((char *) &leader_directory.status_, sizeof(ISIS_INT));

	    fix_endianness(leader_directory.mfn_);
	    fix_endianness(leader_directory.mfrl_);
	    fix_endianness(leader_directory.mfbwb_);
	    fix_endianness(leader_directory.mfbwp_);
	    fix_endianness(leader_directory.base_);
	    fix_endianness(leader_directory.nvf_);
	    fix_endianness(leader_directory.status_);

		if (!leader_directory.dir_.empty())
			leader_directory.dir_.clear();

		// Try to recover if nvf_ = 0
		if (leader_directory.nvf_==0)
			leader_directory.nvf_ = (leader_directory.base_ - GetLeaderSize()) / GetDirEntrySize();

		if (leader_directory.nvf_>0)
		{
			for(vector<DirEntry>::size_type i=0; i!=leader_directory.nvf_; i++)
			{
				DirEntry de;
		        s.read((char *) &de, GetDirEntrySize());
			    fix_endianness(de.tag_);
			    fix_endianness(de.pos_);
			    fix_endianness(de.len_);
			    // Don't check integrity of directory entries here
			    leader_directory.dir_.push_back(de);
			}

		}
	}
	catch(bad_alloc&)
	{
		err |= ios::badbit;
		ios::iostate exception_mask = s.exceptions();
		if (  (exception_mask & ios::failbit) &&
			!(exception_mask & ios::badbit))
		{
			s.setstate(err);
		}
		else if (exception_mask & ios::badbit)
		{
		  s.setstate(err);
		  // try { s.setstate(err); }
		  // catch( ios::failure& ) { }
			throw;
		
		}
	}
    catch(...)
	{
		err |= ios::failbit;
		ios::iostate exception_mask = s.exceptions();
		if ( (exception_mask & ios::badbit) &&
			(err & ios::badbit))
		{
			s.setstate(err);
		}
		else if (exception_mask & ios::failbit)
		{
		  s.setstate(err);
		  // try { s.setstate(err); }
		  // catch( ios::failure& ) { }
			throw;

		}
	}

	if (err) s.setstate(err);
	return s;
}


//////////////////////////////////////////////////////////////////////////////////
//

//  void MfRecord::Copy(UMfRecord& rhs)           
//  {
//  	Clear();
//  	pFdt_      = rhs.pFdt_;
//      state_     = Record_Created;
	
//  	// 1 - Prepare the LeaderAndDirectory, directory, end data part
//  	int n   = 0;
//  	int pos = 0;

//  	cstring all_fields_mb;
//      leader_directory_.dir_.clear();	
//  	all_fields_mb.erase();
//  	for (vector<int>::size_type i=0; i!=rhs.tags_.size(); ++i)
//  	{
//  		bool bRep = false;
//  		if (rhs.pFdt_)
//  		{
//  			if (rhs.pFdt_->GetEntryAt(i).rep)
//  			   bRep = true;
//  		}
//  		// Get the field, all occurences combined
//  		int tag = rhs.tags_[i];
//  	    awstring ws;
//  		bool rc = rhs.GetField(tag, ws);
//  		vector<awstring> occ;
//  		if (bRep)
//  			vector<awstring> occ = SplitOccurences(ws);
//  		else
//  			occ.push_back(ws);
//          // one entry per occurence
//  		int nocc = occ.size();
//  		for (vector<awstring>::size_type j=0; j!= nocc; ++j)
//  		{
//  			DirEntry de;
//  			de.tag_ = tag;
//  			de.pos_ = pos;
//  			cstring cs; // For storing the unicode string
//  			de.len_ = UnicodeToCodePage(occ[j], cs);
//              leader_directory_.dir_.push_back(de);
//  			all_fields_mb += cs;
//  			n++;
//  			pos += occ[j].length();
			
//  		}
//  	}

//  	leader_directory_.nvf_  = n;
//  	leader_directory_.base_ = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();
//  	leader_directory_.mfrl_ = ComputeMfrl();

//  	// 2   - Make record

//  	record_.erase(); // Empty the record

//  	// Prepare the binary part, note that this part must be  independent
//  	// of the character set.

//  	cstringstream ost(std::ios::out | std::ios::binary);   
//  	ost << leader_directory_;   // Output the LeaderAndDirectory to a string

	
//  	AddToRecord(ost.str().c_str(), ost.str().length());

//  	// Here comes the part which is dependent from the character set,
//  	// the data itself.
//  	AddToRecord((char*)all_fields_mb.c_str(), all_fields_mb.length());

//  	DecomposeRecord();
//  }

//  MfRecord::MfRecord(const UMfRecord& rhs)     // Copy constructor
//  { 
//     Copy(rhs);
//  }

//  MfRecord& MfRecord::operator=(const UMfRecord& rhs) // Assignment
//  {
//     if (&leader_directory_ != &rhs.leader_directory_)
//     {
//     	   Copy(rhs);
//     }
//     return *this;
//  }

/////////////////////////////////////////////////////////////////////////////////////////
// Accessors
 
 

////////////////////////////////////////////////////////////////////////////////////////
// Methods working on the in memory image of the record
 
//-------------------------------------------------------------------------------
// bool  MfRecord::GetField(int tag, string& s)
//
// This function takes as input a tag and will place the field into the string
// s. It will return true if the field exists and false otherwise.
//-------------------------------------------------------------------------------
bool  MfRecord::GetField(int tag, ustring& s)
{
	PRECONDITION(tag>=0 && tag < SHRT_MAX);

	map<int, ustring>::iterator it = fields_.find(tag);

	bool ret = false;
	if (it != fields_.end())
	{
		s   = it->second;
		ret = true;
	}
	return ret;
}

//////////////////////////////////////////////////////////////////////////////////////
// Modifiers, They synchronize the record structure with the internal structures


//---------------------------------------------------------------------------------
// AddField --- Add a field to the master record structure.
//            
//--------------------------------------------------------------------------------- 
bool MfRecord::AddField(int tag, ustring& s)
{
	PRECONDITION(tag>=0 && tag < SHRT_MAX);

	map<int, ustring>::iterator it = fields_.find(tag);

	bool ret = false;
	if (it == fields_.end())
	{
		fields_.insert(make_pair(tag, s));
		tags_.push_back(tag);
		sort(tags_.begin(), tags_.end());
		ret = true;
		ComposeRecord();
	}
	return ret;
}
//---------------------------------------------------------------------------------
// AddOccurence --- Add an occurence to an existing field.
//--------------------------------------------------------------------------------- 
bool MfRecord::AddOccurence(int tag, ustring& o)
{
	PRECONDITION(tag>=0 && tag < SHRT_MAX);

	map<int, ustring>::iterator it = fields_.find(tag);

	bool ret = false;
	if (it != fields_.end())
	{
	    it->second += SYSRSEP;
		it->second += o;
		ret = true;
		ComposeRecord();
	}
	return ret;
}

//-----------------------------------------------------------------------------
// bool  MfRecord::DeleteField(int tag)
//
//-----------------------------------------------------------------------------
bool  MfRecord::DeleteField(int tag)
{
	PRECONDITION(tag>=0 && tag < SHRT_MAX);

	map<int, ustring>::iterator it = fields_.find(tag);

	bool ret = false;
	if (it != fields_.end())
	{
		fields_.erase(it);
		ret = true;
		vector<int>::iterator j = find(tags_.begin(), tags_.end(), tag);
		if (j != tags_.end())
			tags_.erase(j);
		ComposeRecord();
	}
	return ret;
}

//-------------------------------------------------------------------------------
// bool ReplaceField(int tag, string& s)
//
//-------------------------------------------------------------------------------
bool MfRecord::ReplaceField(int tag, ustring& s)
{
    
	PRECONDITION(tag>=0 && tag < SHRT_MAX);

	map<int, ustring>::iterator it = fields_.find(tag);

	bool ret = false;
	if (it != fields_.end())
	{
		it->second  = s;
		ret         = true;
	}
	else
		AddField(tag, s);
	ComposeRecord();

	return ret;
}

ustring MfRecord::GetAllFields()
{
	cstring all_fields; // As a string of ANSI characters
	all_fields.erase();
	all_fields = record_.substr(GetBase(), GetFieldSize());
	return ustring(all_fields);
}

void MfRecord::ConvertToAnsi()
{
	/*
	int mfrl = GetMfrl();
	int size1 = record_.length();
	if (GetFieldSize()==0)
		return;
	cstring s = record_.substr(GetBase(), GetFieldSize());
	CString cs(s.c_str());
	cs.OemToAnsi();
	TRACE("\nbase=%d GetFieldSize=%d",GetBase(),GetFieldSize());
	record_.replace(GetBase(),GetBase()+GetFieldSize(),
		                                 cs.GetBuffer(GetFieldSize()));
	int size2 = record_.length();
	cs.ReleaseBuffer();
	int padding = size1-size2;
	for (int i=0; i<padding; i++)
		record_ += " ";
*/
}

mg_s_long MfRecord::GetDirectorySize()
{
	return (GetNvf()*GetDirEntrySize());
}

mg_s_long MfRecord::ComputeMfrl()
{
	return (GetLeaderSize() + GetDirectorySize() + GetFieldSize());
}

//--------------------------------------------------------------------------------------
// void MfRecord::ComposeRecord()
//
// Build the record into continuous bytes for output
//--------------------------------------------------------------------------------------
void MfRecord::ComposeRecord()
{
	PRECONDITION(tags_.size() == fields_.size());
	
	// 1 - Prepare the LeaderAndDirectory, directory, end data part
	int n   = 0;
	int pos = 0;

	ustring all_fields;
    leader_directory_.dir_.clear();	
	all_fields.erase();
	for (vector<int>::size_type i=0; i!=tags_.size(); ++i)
	{
		int tag = tags_[i];
		bool bRep = false;
		if (pFdt_)
		{
			if (pFdt_->GetEntryAt(i).rep)
			   bRep = true;
		}
		vector<ustring> occ;
		if (bRep)
			vector<ustring> occ = SplitOccurences(fields_[tags_[i]]);
		else
			occ.push_back(fields_[tags_[i]]);
        // one entry per occurence
		int nocc = occ.size();
		for (vector<ustring>::size_type j=0; j!= nocc; ++j)
		{
			DirEntry de;
			de.tag_ = tag;
			de.pos_ = pos;
			de.len_ = occ[j].length();
            leader_directory_.dir_.push_back(de);
			all_fields += occ[j];
			n++;
			pos += occ[j].length();
			
		}
	}

	leader_directory_.nvf_  = n;
	leader_directory_.base_ = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();
	leader_directory_.mfrl_ = ComputeMfrl();

	// 2   - Make record

	record_.erase(); // Empty the record

	// Prepare the binary part, note that this part must be  independent
	// of the character set.

	cstringstream ost(std::ios::out | std::ios::binary);   
	ost << leader_directory_;   // Output the LeaderAndDirectory to a string

	
	AddToRecord(ost.str().c_str(), ost.str().length());

	// Here comes the part which is dependent from the character set,
	// the data itself.
	cstring all_fields_ansi(all_fields); 
	AddToRecord(all_fields_ansi.c_str(), all_fields_ansi.length());
	
}

//-------------------------------
// Helper functions for decompose
//-------------------------------

bool MfRecord::CheckLeaderAndDirectory()
{
	if (GetMfrl() < 0)                  // Negative mfrl
	{
		//VLOG("DecomposeRecord -  Warning! Negative mfrl - mfn=%d", GetMfn());
		SetMfrl(-GetMfrl());
	}

	if (GetMfrl()<=GetBase())
	{
		//VLOG("DecomposeRecord - Error! mfn=%d GetMfrl()=%d > GetBase()=%d", 
		//	  GetMfn(),  GetMfrl(), GetBase());
		return false;
	}

	

	if (GetLeaderSize()>GetBase())
	{
		//VLOG("DecomposeRecord - Error! mfn=%d GetLeaderSize()=%d > GetBase()=%d", 
		//	  GetMfn(),  GetLeaderSize(), GetBase());
		return false;
	}
	if (GetNvf()==0) 
	{
	    // Null nvf      
		//VLOG("DecomposeRecord -  Warning! mfn=%d nvf=0", GetMfn());
		//SetNvf((GetBase() - GetLeaderSize()) / GetDirEntrySize());
		return false;
	}

	if (GetMfn()<=0 || GetMfrl()<=0 || GetBase()<0 || GetNvf()<0 || GetStatus()< 0) 
	{
		//VLOG("DecomposeRecord - Error! mfn=%d GetMfrl()=%d GetLeaderSize()=%d GetBase()=%d GetNvf()=%d GetStatus()=%d", 
		//      GetMfn(), GetMfrl(), GetLeaderSize(), GetBase(), GetNvf(), GetStatus());
		return false;
	}


	int base = GetLeaderSize() + leader_directory_.nvf_*GetDirEntrySize();

	if (record_.length() <= base)
	{
        //VLOG("DecomposeRecord - Error! mfn=%d record_.length()=%d < base=%d",
		//	 GetMfn(), record_.length(), base);
		return false;
	}

	int field_len = 0;
	for (int i=0; i!=GetNvf(); ++i)
	{
		if (GetFieldTag(i)<0 || GetFieldTag(i)>SHRT_MAX ||
			GetFieldPos(i)<0 || GetFieldPos(i)>SHRT_MAX ||
			GetFieldLen(i)<0 || GetFieldLen(i)>SHRT_MAX)
		{
            //VLOG("DecomposeRecord - Error! mfn=%d Bad dir entry i=%d tag=%d pos=%d len=%d",
			//    	GetMfn(), GetFieldTag(i), GetFieldPos(i), GetFieldLen(i));
			return false;
		}
		field_len  += GetFieldLen(i);
	}
	
    for (int j=0; j!=GetNvf(); ++j)
	{
		if (GetFieldPos(j)>=field_len || GetFieldPos(j)+GetFieldLen(j)>field_len)
		{
            //VLOG("DecomposeRecord - Error! mfn=%d Bad dir entry j=%d tag=%d pos=%d len=%d",
			//    	GetMfn(), GetFieldTag(j), GetFieldPos(j), GetFieldLen(j));
			return false;
		}
	}
	// Check for bad values
    if (GetBase() != base)	
		SetBase(base);
	
	if (record_.length() < base + field_len)
	{
        //VLOG("DecomposeRecord - Error! mfn=%d record_.length()=%d < base=%d + field_len=%d",
        //GetMfn(), record_.length(), base, field_len);  
		return false;
	}
	return true;
}

void MfRecord::MakeTagToFieldMap()
{
	cstring all_fields_ansi;
	all_fields_ansi.erase();
	all_fields_ansi = record_.substr(leader_directory_.base_, GetFieldSize());
	PRECONDITION(fields_.empty() && tags_.empty());


	// Build the map from tags to fields
	for (int i=0; i!=GetNvf(); ++i)
	{
		//int    all_len = all_fields.length();
		//if (pos>all_len || pos+len>all_len)
		//	return false;
		cstring field_ansi = 
			   all_fields_ansi.substr(GetFieldPos(i), GetFieldLen(i));
		ustring field(field_ansi);
	    map<int, ustring>::iterator it = fields_.find(GetFieldTag(i));
	    if (it != fields_.end())
		{
			// Repeatable field, several occ
		    it->second += SYSRSEP;
			it->second += field;
		}
		else
		{
		    fields_.insert(make_pair(GetFieldTag(i), field));
			
		}
		
	}
}
void MfRecord::MakeTagVector()
{
	// Build a vector of tags
	map<int, ustring>::const_iterator it;
    for ( it = fields_.begin(); it != fields_.end(); ++it )
		tags_.push_back(it->first);

	// Sort in ascending order
	sort(tags_.begin(), tags_.end());
}


//--------------------------------------------------------------------------------------
// bool MfRecord::DecomposeRecord()
//
// Make a memory version of the record to facilitate manipulations.
//--------------------------------------------------------------------------------------
bool MfRecord::DecomposeRecord()
{
	PRECONDITION(!record_.empty());

	if (record_.length() <= GetLeaderSize())
	{
		//VLOG("DecomposeRecord - Error! record_.length()=%d < GetLeaderSize()=%d",record_.length(), GetLeaderSize());
		return false;
	}

	// Input the LeaderAndDirectory from a string
    cstringstream ist(record_,cstringstream::in|cstringstream::binary);
	if (!(ist >> leader_directory_))
		return false;

	if (!CheckLeaderAndDirectory())
	{
		//VLOG("Record rejected mfn=%ld", GetMfn());
		return false;
	}

	//VLOG("Record accepted mfn=%ld", GetMfn());

    MakeTagToFieldMap();
    MakeTagVector();

	return true;
}



void MfRecord::ExtractSubLeader()
{
	//PRECONDITION(!record_.empty() && record_.length()>=sizeof(leader_directory_.mfn_)+sizeof(leader_directory_.mfrl_));

	leader_directory_.mfn_  = *(reinterpret_cast<ISIS_LONG*>((char *) record_.c_str()));
	leader_directory_.mfrl_ = *(reinterpret_cast<ISIS_INT*> ((char *)(record_.c_str()+sizeof(ISIS_LONG))));
	fix_endianness(leader_directory_.mfn_);
	fix_endianness(leader_directory_.mfrl_);
	if (leader_directory_.mfrl_ < 0) leader_directory_.mfrl_ = -leader_directory_.mfrl_; 	// mfrl can be negative!!!!

	//POSTCONDITION(leader_directory_.mfn_>0 && leader_directory_.mfrl_>=0);
}

bool MfRecord::CheckLeader()
{
	int leader_size = GetLeaderSize();
	if (leader_directory_.mfn_<=0)
		return false;
	if (abs(leader_directory_.mfrl_)<leader_size)
		return false;
	return true;
}


bool MfRecord::CheckIntegrity()
{
	int leader_size = GetLeaderSize();
	if (leader_directory_.mfn_<=0)
		return false;
	if (abs(leader_directory_.mfrl_)<leader_size)
		return false;
	if (leader_directory_.nvf_<0)
		return false;
	//if (leader_directory_.base_ != leader_directory_size + leader_directory_.nvf_*sizeof(MAXDIRL))
	//	return false;
	if (leader_directory_.status_ == 1)
		return true;
	else if (leader_directory_.status_ == 0)
		return true;
	else
		return false;

	return true;
}
 

void  MfRecord::Dump()
{

	ustringstream os;
    os.str("");


    os <<  "\n================= entry in MfRecord::Dump==============";
    os <<  "\n   MFN      " << GetMfn();
    os <<  "\n   MFRL     " << GetMfrl();
    os <<  "\n   MFBWB    " << GetMfbwb();
    os <<  "\n   MFBWP    " << GetMfbwp();
    os <<  "\n   BASE     " << GetBase();
    os <<  "\n   NVF      " << GetNvf();
    os <<  "\n   STATUS   " << GetStatus();
 	//TRACE(os.str().c_str());


	for (int i=0; i<GetNvf(); i++)
	{
		  ustring stemp;
		  bool rc = GetField(GetDirEntry(i).tag_, stemp);
		  int len;
		  if (rc)
		  {
		       len = 50;
		       if (stemp.length()<len)
			      len = stemp.length();
		  }
		  ustring fld = (rc==false) ? _T("Empty") : stemp.substr(0,len);
	      os.str("");
		  os << _T(" tag=") << ISIS_INT(GetDirEntry(i).tag_)
             << _T(" pos=") << ISIS_INT(GetDirEntry(i).pos_)
			 << _T(" len=") << ISIS_INT(GetDirEntry(i).len_)
			 << _T(" ")     << fld.c_str() << std::endl; 
          TRACE(_T("\n%s"),os.str().c_str());   
	  }

      TRACE("\n---------------- exit in MfRecord::Dump-------------");
}




bool MfRecord::CheckRecord()
{
	PRECONDITION(!record_.empty());

	if (record_.length() < GetLeaderSize())
	{
		TRACE(_T("\nWrong LeaderAndDirectory in record!"));
		return false;
	}

    cstringstream ist(record_,cstringstream::in|cstringstream::binary);

	LeaderAndDirectory leader_directory;
	ist >> leader_directory;

	if (leader_directory.mfrl_ < 0)                  // Negative mfrl
	{
		//VLOG("CheckRecord -  Warning! Negative mfrl - mfn=%d", GetMfn());
	}
	
	if (leader_directory.nvf_<=0) 
	{
		//VLOG("CheckRecord -  Warning! mfn=%d nvf=0", leader_directory.mfn_);
	}

	if (leader_directory.mfn_<=0 || leader_directory.mfrl_<=0 ||
		leader_directory.base_<0 || leader_directory.nvf_ < 0 ||
		leader_directory.status_ < 0) 
	{
		//VLOG("CheckRecord - Corrupted record! leader_directory.mfn_=%d leader_directory.mfrl_=%d leader_directory.base_=%d leader_directory.nvf_=%d leader_directory.status_=%d",
        //      leader_directory.mfn_, leader_directory.mfrl_,
		//	  leader_directory.base_, leader_directory.nvf_,
		//	  leader_directory.status_);
		return false;
	}


	int base = GetLeaderSize() + GetDirectorySize();

	if (record_.length() < base)
	{
		//VLOG("CheckRecord - Corrupted record! mfn=%d record_.length()=%d base=%d",
		//	  leader_directory.mfn_, record_.length(), base);
		return false;
	}

	int field_len = 0;
	for (int i=0; i!=leader_directory.nvf_; ++i)
	{
		DirEntry de = leader_directory.dir_[i];
//  		if (de.tag_<0 || de.tag_>SHRT_MAX || de.pos_<0 || de.pos_>SHRT_MAX || de.len_<0 || de.len_>SHRT_MAX)
//  		{
//  		    //VLOG("CheckRecord - Corrupted record! mfn=%d tag=%d pos=%d len=%d",
//  			//	  leader_directory.mfn_, de.tag_, de.pos_, de.len_);
//  			return false;
//  		}
		if (i>0)
		{
			if(de.pos_ != leader_directory.dir_[i-1].pos_+leader_directory.dir_[i-1].len_)
			{
		    //VLOG("CheckRecord - Position not consistent - mfn=%d",
			//	  leader_directory.mfn_);
			return false;
			}
		}
		field_len  += de.len_;
	}
	

	// Check for bad values
    if (leader_directory.base_ != base)	
		leader_directory.base_ = base;
	
	if (record_.length() < base + field_len)
	{
		    //VLOG("CheckRecord - Corrupted record, record length < base+field_len  - mfn=%ld",
			//	  leader_directory.mfn_);
		return false;
	}

	return true;
}

void MfRecord::ReadLeader(MfFile& f, mg_s_long daddr)
{
    int leader_size = GetLeaderSize();
    char* buffer = new char[leader_size];
    f.Fetch(buffer, leader_size, daddr);  // Read record leader
    Clear();
    AddToRecord(buffer, leader_size);
    delete[] buffer;
}

bool  MfRecord::Read(MfFile& f, mg_s_long mfb, int mfp)
{
    //TRACE("\nMfFile::ReadMfRecord -- mfb=%ld mfp=%d", mfb, mfp);
	PRECONDITION(mfb>0 && mfb<=MAXMFB);
    mg_s_long  blk;
    int   mfrr, offs, l;

	try
	{
		Clear();
	    // Save original mfb, mfp pair into m
	    SetMfbRead(mfb);
	    SetMfpRead(mfp);
	    SetState(MfRecord::Record_Read);

	    // Be sure that mfp is in range [0:MFBLKSIZE-1]
	    mfp = mfp%MFBLKSIZE;
		assert(mfp>=0 && mfp<MFBLKSIZE);
        //TRACE("\nMfFile::ReadMfRecord - mfb=%ld mfp=%d", mfb, mfp);

		// 1 - Read block where record starts 
        f.ReadBlk(m_buf,mfb);                    

        blk = mfb;
		// 2 - Extract the record leader (Mfn+Mfrl) from the block buffer, the 
		//     record leader cannot be split on two  consecutive blocks.
        int len = GetSubLeaderSize(); // Mfn + Mfrl
		ASSERT(mfp+len<MFBLKSIZE);

        AddToRecord(m_buf+mfp, len); // Move 1st 6 bytes  (Mfn+Mfrl)
		ExtractSubLeader();

		// 3 - Continue to transfer data for building the MfRecord object
        int mflen = GetMfrl();                // Get record length
	    SetMflenRead(mflen);                  // Save it 
        //TRACE("\nMfFile::readMfRecord - After reading leader mfn=%ld mflen=%d",GetMfn(), mflen);
        mfrr = mflen - len;                     // Not yet moved
        l = (mfrr > (f.GetBlockSize()-(mfp+len)))      // What remains to move
            ? (f.GetBlockSize()-(mfp+len)) : mfrr;         // in this block
        offs = len;
        AddToRecord(m_buf+mfp+len, l); // Move-it
        mfrr -= l;
        while (mfrr>0)                        // Record continues on next block ?
		{                
            f.ReadBlk(m_buf,++blk);             // Yes read next block
            offs += l;
            l = (mfrr > f.GetBlockSize()) ? f.GetBlockSize() : mfrr;
            AddToRecord(m_buf, l); // Move data
            mfrr -= l;
		}
	    //TRACE("\nMfFile::ReadMfRecord -- mfb=%ld mfp=%d nvf=%d", mfb, mfp, GetNvf());
	}
	catch (CFileBaseException e)
	{
        throw CIsisDbException(ISIS_MF_READ_ERROR, __FILE__, __LINE__);
	}

	return DecomposeRecord();
}


void  MfRecord::Write(MfFile& f, mg_s_long mfb, int mfp)
{
	TRACE(_T("\nMfFile::writeMfRecord - mfb=%ld mfp=%d"), mfb, mfp);
	PRECONDITION(mfb>0 && mfb <= MAXMFB);
	// Be sure that mfp is in range [0:MFBLKSIZE-1]
	mfp = mfp%MFBLKSIZE;
	
    mg_s_long hiblk = f.GetNextMfb(); // Highest block number in master file
	
    int s  = 0;              // Index in master file record [0:mfrl-1]
    int d  = mfp;            // Index in block buffer [0:bsize-1]
    mg_s_long b = mfb;            // Starting block number
	
    // Adjust record length
	int padding = GetMfrl() % MFALIGN;
	if (padding>0)
	{
		SetMfrl(GetMfrl() + padding);
		for (int i=0; i<padding;i++)
			AddToRecord(" ", 1);
	}
	
    int mfrl = GetMfrl();
    TRACE(_T("\nMfFile::writeMfRecord - b=%ld d=%d mfrl=%ld hiblk=%ld"), b, d, mfrl, hiblk);
    TRACE(_T("\nBefore writing->"));
	do 
    {
		if (b <= hiblk)        
			f.ReadBlk(m_buf, b);
		else
			::memset(m_buf, ' ', MFBLKSIZE);
		
		while (d < MFBLKSIZE && s < mfrl)
			m_buf[d++] = record_.at(s++);
		if (d >= MFBLKSIZE) 
		{
			TRACE(_T("MfFile::WriteMfRecord - Before WriteBlk b=%ld"), b);
			f.SetLastWrittenBlock(b);
			f.WriteBlk(m_buf, b++);
			// Reset m_buf pos
			d = 0;
		}
	} while (s < mfrl);
	
    // Write last block
	if (d == 0)
		::memset(m_buf, ' ', MFBLKSIZE); // Should be an empty block
	
	f.WriteBlk(m_buf, b);       
	f.SetLastWrittenBlock(b);
} 

void IntToChar(int value, int width, std::string& s)
{
	std::ostringstream ost;

#ifdef _WIN32
	ost.setf(std::ios_base::right, std::ios_base::adjustfield);
#endif
	ost << std::setw(width) 
		<< std::setfill('0') 
		<< value;
	s = ost.str();
}
//--------------------------------------------------------------------------------------
// void MfRecord::MakeISORecord(std::string& s)
//
// Build the record into continuous bytes for output
//--------------------------------------------------------------------------------------
void MfRecord::MakeISORecord(std::string& s)
{
	std::string    tmp;

	MarcLeader l;

	// 1 - Complete the leader (base and record length), output the directory 
	//     to a string and prepare a string with the data fields
	int base = MARC_LEADER_LEN + GetNvf()*MARC_DIRENT_LEN + 1;
	IntToChar(base, 5, tmp);
	strncpy(l.base_ ,tmp.c_str(),5);

	DirectoryEntry mde;
	ustring    all_fields;
	all_fields.erase();
	ostringstream  osd;     // Output the directory to a string
	int pos = 0; 
	for (int i=0; i!=GetNvf(); ++i)
	{
		IntToChar(GetFieldTag(i), 3, tmp); 
		strncpy(mde.tag_,tmp.c_str(),3);
		int len = GetFieldLen(i)+1;  // Include space for separator
		IntToChar(len, 4, tmp);
		strncpy(mde.len_, tmp.c_str(), 4);
		IntToChar(pos, 5, tmp);
		strncpy(mde.scp_, tmp.c_str(), 5);
		osd << mde; 
		pos += len;

		cstring field = record_.substr(GetBase()+GetFieldPos(i), GetFieldLen(i));
		all_fields += ustring(field);
		all_fields += FIELDTERM;
	}
	// (+2) ->Field sep at end of directory and record sep
    int reclen =  MARC_LEADER_LEN + GetNvf()*MARC_DIRENT_LEN + pos +2;
	IntToChar(reclen, 5, tmp);
	strncpy(l.lreclen_,tmp.c_str(),5);


	ostringstream osl;     // Output the Leader to a string
	osl << l;

	// 2   - Make record
	s.erase();       // Empty the record
    s += osl.str();  // Add the leader
	s += osd.str();  // Add the directory
	s += FIELDTERM;  // Separator
	s += cstring(all_fields); // Data part
	s += RECTERM;
		
}
