//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to 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.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// blockrecordarray.h
//
// cBlockRecordArray
//
#ifndef __BLOCKRECORDARRAY_H
#define __BLOCKRECORDARRAY_H

#ifndef __BLOCKFILE_H
#include "blockfile.h"
#endif
#ifndef __DEBUG_H
#include "core/debug.h"
#endif

//
// TODO -- I need to guarantee that all of the structures in this class do not have any pad bytes. 
//		This is true under win32, but I am not sure if it will always be true. -- 10 feb 99 mdb
//

class eArchive;

class cBlockRecordArray
{
public:
	//-------------------------------------------------------------------------------------
	// Construction
	//-------------------------------------------------------------------------------------
	cBlockRecordArray(cBlockFile* pBlockFile, int blockNum);
		// this object is associated with a single block in a cBlockFile. It is _not_
		// asserted that the blockNum is valid for the associated file at this point, 
		// since it is possible that the file has not been loaded yet
	cBlockRecordArray();
		// if this version of the ctor is called, then Init() must be called before it can be used.
	void	Init(cBlockFile* pBlockFile, int blockNum);

	virtual ~cBlockRecordArray();

	//-------------------------------------------------------------------------------------
	// Initialization ... exactly one of these methods must be called before this class can be used
	//-------------------------------------------------------------------------------------
	void	InitNewBlock() ; //throw (eArchive)
		// this method should be called when a new block has been created, and before this 
		// class operates on it.
	void	InitForExistingBlock() ; //throw (eArchive)
		// this method should be called to initialize this class for use with a block that has 
		// previously been created and initialized with InitNewBlock().

	//-------------------------------------------------------------------------------------
	// Data Manipulation
	//-------------------------------------------------------------------------------------
	int		AddItem(int8* pData, int dataSize, int mainIndex) ; //throw (eArchive)	
		// inserts the given item into the array; returns the index that it was inserted into.
		// this asserts that there is room for the new element, and updates the avail. space and
		// max index as necessary.
	void	DeleteItem(int index ) ; //throw (eArchive)
		// deletes the specified item; this asserts that the index is valid, and updates the avail.
		// space and max index as necessary. 
	int8*	GetDataForReading( int index, int32& dataSize ) ; //throw (eArchive)
		// returns a pointer to the named data. This method will assert that the address is
		// valid. The data pointer returned is guarenteed to be valid only until the next
		// method call into this class.
	int8*	GetDataForWriting( int index, int32& dataSize ) ; //throw (eArchive)
		// this is the same as the previous function, except the dirty bit for the page is also set
	bool	IsItemValid(int index) const ; //throw (eArchive)
		// returns true if the given index has a valid value.

	bool	IsClassValid() const;
		// returns true if the class has an internally consistant state. If an archive exception
		// occurs, false is returned. This method is good for determining if a newly opened block
		// file is actually a block file

	bool	Initialized() const			{							return mbInit; }
	int		GetAvailableSpace() const	{ ASSERT( Initialized() );	return mSpaceAvailable; }
	int		GetNumItems() const			{ ASSERT( Initialized() );	return mNumItems; }
		// returns the number of items currently being stored in the array

	///////////////////////////////////////////////////////////////////
	// tRecordIndex -- this is what each record in the array is
	//		indexed by. These come at the beginning of the block and
	//		the record data comes at the end.
	///////////////////////////////////////////////////////////////////
	struct tRecordIndex
	{
	private:
		int32	mOffset;	// offset from the end of the block that my data is at; offset 1 is the last byte in the block
		int32	mMainIndex;	// my main array index
	public:
		// byte order safe access methods...
		//
		void  SetOffset		( int32 off )	{ mOffset	= tw_htonl( off ); }
		int32 GetOffset		(  )			{ return	  tw_ntohl( mOffset ); }
		void  SetMainIndex	( int32 idx )	{ mMainIndex= tw_htonl( idx ); }
		int32 GetMainIndex	(  )			{ return	  tw_ntohl( mMainIndex ); }
	};
	///////////////////////////////////////////////////////////////////
	// tHeader -- the info that appears at the beginning of the block
	///////////////////////////////////////////////////////////////////
	struct tHeader
	{
	private:
		int32	mSpaceAvailable;
		int32	mNumItems;
	public:
		// byte order safe access methods...
		//
		void  SetSpaceAvail	( int32 sa )	{ mSpaceAvailable	=	tw_htonl( sa ); }
		int32 GetSpaceAvail	(  )			{ return				tw_ntohl( mSpaceAvailable ); }
		void  SetNumItems	( int32 ni )	{ mNumItems			=	tw_htonl( ni ); }
		int32 GetNumItems	(  )			{ return				tw_ntohl( mNumItems ); }
	};

	enum	{	MAX_RECORDS		= cBlockFile::BLOCK_SIZE / ( sizeof(tRecordIndex) + sizeof(uint32) ),
					// MAX_RECORDS signifies the maximum number of records that can be stored in a single block. 
					// It is rationalized like this: each used record needs a tRecordIndex plus an extra uint32
					// as a minimum storage requirement (even though it is ok to have a record that is filled in 
					// with zero bytes)
				MARGIN			= 10 , 
					// MARGIN is the min. amount of room that should exist between the indexes and the data. This
					// is used when calculating the available space, and also in assertions.
				MAX_DATA_SIZE	= cBlockFile::BLOCK_SIZE - sizeof(tRecordIndex) - sizeof(tHeader) - MARGIN,
					// this is the largest single piece of data that could theoreticaly be stored (if it were the
					// only thing stored in the block). We assume that the single record index will take up space,
					// the header will take up space, and the margin will take up space.
				INVALID_INDEX	= -1
					// when tRecordIndex::mMainIndex is set to this, it indicates that the index is not being used.
			};

	///////////////////////////////////////////////////////////////////
	// tIndexArray -- the beginning of a block is treated 
	//		as one of these
	///////////////////////////////////////////////////////////////////
	struct tIndexArray
	{
		tHeader			mHeader;
		tRecordIndex	maRecordIndex[MAX_RECORDS];
	};

private:
	cBlockFile*		mpBlockFile;
	int				mBlockNum;			// the block that I am operating on
	int				mSpaceAvailable;	// amount of storage I have left
	int				mNumItems;			// number of items currently being stored in this array
	bool			mbInit;				// has InitXXX() been called?

	void UpdateFreeSpace(cBlockFile::Block* pBlock);
		// calculates the total free space available in this array and assigns
		// it to mSpaceAvailable. Note that the value of mSpaceAvailable signifies
		// the size of the largest single item that could be added to the array.
	void WriteHeaderInfo(cBlockFile::Block* pBlock);
		// gets the header information at the top of our block in sync with our member vars
	void ReadHeaderInfo	(cBlockFile::Block* pBlock);
		// reads the header info from the block and sets our member vars to what was read

	///////////////////////////////////////////////////////////////////////////
	// Profiling / Debugging interface
	///////////////////////////////////////////////////////////////////////////
#ifdef _BLOCKFILE_DEBUG
public:
	void TraceContents(int dl = -1) const;
		// traces out all the info we can on the current state of this class
		// dl is the debug level to trace it at; -1 means to use D_DEBUG
	void AssertValid() const;
		// ASSERTs as much as we can about the consistency of our internal state.
#endif
};



#endif //__BLOCKRECORDARRAY_H

