#pragma once

#include "Stuff.hpp"

namespace Stuff {

	//////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////
	// ****************************	   DATABASE API	   **************************** //
	//////////////////////////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////////////

	class Record;
	class RecordHandle;
	class Database;
	class DatabaseHandle;

	class RecordHandle
	{
	public:
		DatabaseHandle *m_databaseHandle;
		WORD m_ID;
		const char *m_name;
		__int64 m_timeStamp;
		DWORD m_length;
		const void *m_data;
		const Record *m_record;

		#if defined(_ARMOR)
			RecordHandle();
			~RecordHandle();
		#endif

		//
		// Add - Adds a record to the database and returns a unique ID that can be used to access the record.
		//       The record can be found by the RecordID returned or the RecordName
		//       The info fields will be changed to point at the database's copy of the field
		//
		void Add();

		//
		// Replace - Overwrites a record based on the RecordID passed.
		//       The record can be found by the RecordID returned or the RecordName
		//       The info fields will be changed to point at the database's copy of the field
		//
		void Replace();

		//
		// Delete - Removes the record with the given RecordID.
		//
		void Delete();

		//
		// FindID - Returns a pointer to the Data contained in the record with the ID specified, also fills in a pointer to the original name.
		//			True is returned if a record is found
		//
		bool FindID(bool load);

		//
		// FindName - Returns a pointer to the Data contained in the record with the Name specified, also fills in a pointer to the record ID number.
		//       True is returned if a record is found
		//       The info fields will be changed to point at the database's copy of the field
		//
		bool FindName(bool load);

		//
		// This function must be called before the data will be loaded (note that data is automatically loaded if loaded by ID rather than name)
		//
		void LoadData();

		//
		// This function will unload any loaded data for the record
		//
		void UnloadData();

		DWORD GetRealRecordSize();

		//
		// This function will unload any loaded data for the record BUT IT WILL NOT DELETE THE ACTUAL DATA ITSELF!!!!
		//
		void AbandonData();

		int GetDiskOffset();

		//
		// Return the current record information and moves the current record to the next record.
		// If there is a current record, True will be returned.
		//
		void First();
		bool ReadAndNext();

		void TestInstance() const;
	};

	class DatabaseHandle
	{
		friend class Database;
		friend class Record;
		friend class RecordHandle;

	public:
		Database *m_dataBase;

		DatabaseHandle(
			const char* filename,
			bool read_only,
			DWORD content_version
		);

		DatabaseHandle(
			const char* filename,
			MemoryStream *stream,
			DWORD content_version
		);

		~DatabaseHandle();

		void Save();
		void ScatterSave();
		void SaveAs(const char* filename);
		void Save(BYTE* buffer, DWORD length);

		void SpewLoadedRecords();
		void UnloadRecords();

		//
		// GetNumberOfRecords - Returns the number of records in the database
		//
		WORD GetNumberOfRecords();

		void TestInstance() const;


		// it does not store the crc on purpose
		// that way a hacker can't find the number easily...
		__int64 CalculateCRC(int key);
		__int64 CalculateDateCRC();

		MString
			GetFileName()
				{return m_fileName;}
		HGOSFILE
			GetFileHandle()
				{return m_fileHandle;}
		DWORD
			GetFileLength()
				{return m_fileLength;}

	protected:
		__int64 m_currentCRC;
		MString m_fileName;

		HGOSFILE m_fileHandle;
		BYTE *m_fileAddress;
		DWORD m_fileLength;
		DWORD m_MMHandle;

		DWORD m_contentVersion;
		HGOSHEAP m_heap;

		bool
			m_dirtyFlag,
			m_readOnly;
	};

	extern HGOSHEAP g_DatabaseHeap;
}
