//
// Asura_Chunk_Stream.h
//
// Created by KOR - 23/06/2005
//
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_CHUNK_STREAM_H__
#define __ASURA_CHUNK_STREAM_H__

// Asura includes
#if defined ASURA_TOOLS || !defined ASURA_MAIN
#include "Core/Asura_Endian.h"					// Asura_Endian::SwapBytes()
#endif
#include "Core/Asura_Handle.h"					// filehandle
#include "Core/Asura_PlatformBasics.h"			// Asura_UnicodeChar

#if defined(ASURA_TOOLS)
// Tools headers
#include "Load&Save/LevelExport/Tools_Level_Export.h"
#endif	// defined(ASURA_TOOLS)

// Defines
#define ASURA_CHUNK_STREAM_ALLOW_OTHER_IO (1)

// Forward declares
class Asura_CompressedVector3_101010N;
class Asura_HalfFloat;
class Asura_Comp_Quat;
class Asura_Comp_Quat_Fast;
class Asura_Quat;
class Asura_Euler_3;
class Asura_RGBA;
class Asura_String;
class Asura_Vector_2;
class Asura_Vector_3;
class Asura_AVector3;
class Asura_Matrix_3x3;
class Asura_AMatrix3x3;
class Asura_Bounding_Box;
class Asura_SaveBlock_Base;

// Class definitions
template<class T> class Asura_ReadFromStream_Array_Wrapper
{
public:
	friend class Asura_Chunk_Stream;

	Asura_ReadFromStream_Array_Wrapper (u_int uCount, T* pxArray) : m_uCount(uCount), m_pxArray(pxArray)	{}

private:
	u_int	m_uCount;
	T*		m_pxArray;
};

template<class T> class Asura_WriteToStream_Array_Wrapper
{
public:
	friend class Asura_Chunk_Stream;

	Asura_WriteToStream_Array_Wrapper (u_int uCount, const T* pxArray) : m_uCount(uCount), m_pxArray(pxArray)	{}

private:
	u_int		m_uCount;
	const T*	m_pxArray;
};

template<typename T> class Asura_ReadFromStream_Bitfield_Wrapper
{
public:
	friend class Asura_Chunk_Stream;

	Asura_ReadFromStream_Bitfield_Wrapper(T& xValue) : m_xValue(xValue)	{}

private:
	T&	m_xValue;
};

template<typename T> class Asura_WriteToStream_Bitfield_Wrapper
{

public:

#if defined ASURA_TOOLS || !defined ASURA_MAIN

	Asura_WriteToStream_Bitfield_Wrapper(const T& xExpectedValue)	: m_uNumberOfEntries(0), m_xExpectedValue(xExpectedValue)
	{
	}

	void Add (const T xValue, const u_int uNumberOfBits)
	{
		T	xMask	= static_cast<T>((1 << uNumberOfBits) - 1);

		// Validation...
		Asura_Assert	((xValue == (xValue & xMask)), "Value appears to use more bits than specified.");
		Asura_Assert	((32 > m_uNumberOfEntries), "Too many entries in bit-field.");

		// Add the data...
		m_axValues [m_uNumberOfEntries]			= xValue & xMask;
		m_auNumberOfBits [m_uNumberOfEntries++]	= uNumberOfBits;
	}

#else

    Asura_WriteToStream_Bitfield_Wrapper( const T& xValue )
        : m_xValue( xValue )
    {
    }

    void Add( const T xValue, const u_int uNumberOfBits ) { }

#endif

private:

	friend class Asura_Chunk_Stream;

#if defined ASURA_TOOLS || !defined ASURA_MAIN

	u_int		m_uNumberOfEntries;
	const T&	m_xExpectedValue;

	T			m_axValues [32];
	u_int		m_auNumberOfBits [32];

#else

    const T& m_xValue;

#endif
};

template<typename T> class Asura_ReadFromStream_Enum_Wrapper
{
public:
	friend class Asura_Chunk_Stream;

	Asura_ReadFromStream_Enum_Wrapper (T& xValue) : m_xValue(xValue)	{}

private:
	T&	m_xValue;
};

template<typename T> class Asura_WriteToStream_Enum_Wrapper
{
public:
	friend class Asura_Chunk_Stream;

	Asura_WriteToStream_Enum_Wrapper (const T& xValue) : m_xValue(xValue)	{}

private:
	const T&	m_xValue;
};

class Asura_Chunk_Stream
{
public:
	enum STREAM_STATE
	{
		STREAM_ERROR = -1,
		STREAM_READY = 0,
		STREAM_READ_END,
		STREAM_READ_ID,
		STREAM_READ_HEADER,
		STREAM_WRITING_CHUNK,
	};

	// Constructor
	Asura_Chunk_Stream()
	: m_eState(STREAM_READY)
	, m_iChunkID(0)
#ifdef ASURA_CHUNK_STREAM_ALLOW_OTHER_IO
	, m_iExpectedEndPosition(-1)
#else
	, m_iByteCounter(-1)
#endif
	, m_uChunkStartPosition(0)
	, m_iChunkSize(0)
	, m_uResourceFileSizeOffset(0)
#if defined ASURA_TOOLS || !defined ASURA_MAIN
	, m_bSwapEndian(false)
	, m_bHasSetSwapEndian(false)
#endif
	{}
	// Destructor
	virtual ~Asura_Chunk_Stream()
	{
		Asura_Assertf(m_eState != STREAM_READ_ID && m_eState != STREAM_READ_HEADER, ("Didn't finish reading current chunk (%c%c%c%c)?", m_acTextID[0], m_acTextID[1], m_acTextID[2], m_acTextID[3]));
		Asura_Assertf(m_eState != STREAM_WRITING_CHUNK, ("Didn't finish writing current chunk (%c%c%c%c)?", m_acTextID[0], m_acTextID[1], m_acTextID[2], m_acTextID[3]));
	}

	virtual u_int Read(u_int uNumberOfBytes, void* pData) = 0;
	virtual u_int Skip(u_int uNumberOfBytes) = 0;
	virtual u_int Write(u_int uNumberOfBytes, const void* pData) = 0;
	// Overloaded write with position
	virtual u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition) = 0;
	// PeekInt() - Reads the next int, without moving the position of the stream along. Used for version checking.
	virtual int PeekInt() = 0;
	u_int PeekUInt() { return static_cast<u_int>(PeekInt()); }

	// Virtual function overridden in file streams to return correct file handle
	virtual Asura_Handle GetFileHandle() const { return ASURA_INVALID_HANDLE; }

	// Chunk read functions
	// Reads in the chunk ID and returns true if we have a valid chunk.
	bool ReadChunk_GetNextID( char* const pcChunkBuffer );
	// Reads in the rest of the chunk header, recording the size for error checking
	bool ReadChunk_GetNextHeader( char* const pcChunkBuffer, const int iHeaderSize );
	// Finishes the current chunk and does error checking
	bool ReadChunk_End(bool bFinishedReading);
	// Returns true if the chunk header has been read ,and the stream is waiting to end the chunk read
	bool HasReadChunkHeader() const {return m_eState == STREAM_READ_HEADER;}
	
	// Gets an integer position
	virtual u_int GetPosition() const = 0;

	// Chunk write functions
	void WriteChunk_Start(int iChunkID, int iVersion, int iFlags);
	void WriteChunk_End(void);

	// resource chunk write functions	
	void WriteResourceChunk_Start( u_int uResourceType, u_int uResourceSubType, int iVersion, int iFlags, const Asura_String& xFilename );
	void WriteResourceChunk_End( void );

	void WritePadding(const u_int uNumBytes, const u_char ucPadValue);
	void WritePadding(const u_int uNumBytes)	{ WritePadding (uNumBytes, s_ucPaddingValue); }
	void Write2BytePadding(void)				{ WritePadding (((2 - m_iChunkSize) & 1)); }
	void Write4BytePadding(void)				{ WritePadding (((4 - m_iChunkSize) & 3)); }
	void Write2BytePadding(const u_char ucPad)	{ WritePadding (((2 - m_iChunkSize) & 1), ucPad); }
	void Write4BytePadding(const u_char ucPad)	{ WritePadding (((4 - m_iChunkSize) & 3), ucPad); }

	void WriteAsuraTerminator(void)				{ WritePadding (16, 0x00); }

	// Chunk skip functions...
	void SkipPadding(const u_int uNumBytes);
	void Skip2BytePadding(void)					{ SkipPadding (((2 - m_iChunkSize) & 1)); }
	void Skip4BytePadding(void)					{ SkipPadding (((4 - m_iChunkSize) & 3)); }
	void SkipString()							{ ReadString(0); }

	//Reads string into buffer.
	//If the buffer isn't large enough then the string will be truncated and the remainder will be skipped. RAR 10/07/2006
	void ReadString(char* szReadStringBuffer,u_int uBufferSize);

#if defined ASURA_TOOLS || !defined ASURA_MAIN
	// Endian functions...
	void SetSwapEndian(bool bSwapEndian)		{ m_bSwapEndian = bSwapEndian; m_bHasSetSwapEndian = true; }
	bool ShouldSwapEndian(void) const			{ return (m_bSwapEndian); }
#endif

	// Stream operators for basic types
	Asura_Chunk_Stream& operator>> (int& iValue);
	Asura_Chunk_Stream& operator<< (const int iValue);

	Asura_Chunk_Stream& operator>> (u_int& uiValue);
	Asura_Chunk_Stream& operator<< (const u_int uiValue);

	Asura_Chunk_Stream& operator>> (float& fValue);
	Asura_Chunk_Stream& operator<< (const float fValue);

	Asura_Chunk_Stream& operator>> (short& sValue);
	Asura_Chunk_Stream& operator<< (const short sValue);

	Asura_Chunk_Stream& operator>> (u_short& usValue);
	Asura_Chunk_Stream& operator<< (const u_short usValue);

	Asura_Chunk_Stream& operator>> (char& cValue);
	Asura_Chunk_Stream& operator<< (const char cValue);

	Asura_Chunk_Stream& operator>> (u_char& ucValue);
	Asura_Chunk_Stream& operator<< (const u_char ucValue);

	Asura_Chunk_Stream& operator>> (int8& cValue);
	Asura_Chunk_Stream& operator<< (const int8 cValue);

	Asura_Chunk_Stream& operator>> (bool& bValue);
	Asura_Chunk_Stream& operator<< (const bool ucValue);

#if defined(ASURA_UNICODE_CHAR_DISTINCT_TYPE)
	Asura_Chunk_Stream& operator>> (Asura_UnicodeChar& wcValue);
	Asura_Chunk_Stream& operator<< (const Asura_UnicodeChar wcValue);
#endif	// defined(ASURA_UNICODE_CHAR_DISTINCT_TYPE)

	// Various useful Asura types. Will probably also want most of the other maths structures here
	Asura_Chunk_Stream& operator>> (Asura_CompressedVector3_101010N& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_CompressedVector3_101010N& xValue);

	Asura_Chunk_Stream& operator>> (Asura_HalfFloat& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_HalfFloat& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Comp_Quat& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Comp_Quat& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Comp_Quat_Fast& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Comp_Quat_Fast& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Quat& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Quat& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Euler_3& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Euler_3& xValue);

	Asura_Chunk_Stream& operator>> (Asura_RGBA& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_RGBA& xValue);

	Asura_Chunk_Stream& operator>> (Asura_String& xString){ReadString(&xString); return(*this);}
	Asura_Chunk_Stream& operator<< (const Asura_String& xString);

	Asura_Chunk_Stream& operator>> (Asura_Vector_2& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Vector_2& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Vector_3& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Vector_3& xValue);

#if !defined ( ASURA_USE_HACKED_VECTOR_4 )
	Asura_Chunk_Stream& operator>> (Asura_AVector3& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_AVector3& xValue);
#endif

	Asura_Chunk_Stream& operator>> (Asura_Matrix_3x3& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Matrix_3x3& xValue);

    Asura_Chunk_Stream& operator>> (Asura_AMatrix3x3& xValue);
    Asura_Chunk_Stream& operator<< (const Asura_AMatrix3x3& xValue);

	Asura_Chunk_Stream& operator>> (Asura_Bounding_Box& xValue);
	Asura_Chunk_Stream& operator<< (const Asura_Bounding_Box& xValue);

	// Array-wrapper functions...
	template<typename T> Asura_Chunk_Stream& operator<< (const Asura_WriteToStream_Array_Wrapper<T>& xArray)
	{
		for(u_int u = 0; u < xArray.m_uCount; ++u)
		{
			*this << xArray.m_pxArray[u];
		}
		return	(*this);
	}
	template<typename T> Asura_Chunk_Stream& operator>> (Asura_ReadFromStream_Array_Wrapper<T>& xArray)
	{
		for(u_int u = 0; u < xArray.m_uCount; ++u)
		{
			*this >> xArray.m_pxArray[u];
		}
		return	(*this);
	}

	// Bitfield-wrapper functions...
	template<typename T> Asura_Chunk_Stream& operator<< (const Asura_WriteToStream_Bitfield_Wrapper<T>& xBitfield)
	{
        // (MP - 29.03.2006) In tools we deal with endian swapping (under the
        // assumption that we're running on a little endian platform) and
        // go through the whole process of storing each added field
        // separately, etc. Outside of tools builds we just dump the
        // bitfield as-is, since we shouldn't ever need to worry about
        // swapping endianness, and we skip a lot of the extra logic. However,
        // the interface remains the same in both cases so that only one
        // version of the save code is required.

		// (AAM - 30.03.2006) Allow this in external file tools too,
		// distinguished by not being ASURA_MAIN

#if defined ASURA_TOOLS || !defined ASURA_MAIN

		T	xValue	= 0;

		// Validation...
		Asura_Assert	((0 < xBitfield.m_uNumberOfEntries), "No data has been added to the bitfield being written.");

		// Do we need to swap endianness?
		if (m_bSwapEndian)
		{
			u_int	uCurrentBit	= 8 * sizeof(T);

			// Yes - start from top bit...
			for (u_int u = 0; u < xBitfield.m_uNumberOfEntries; u++)
			{
				uCurrentBit	-= xBitfield.m_auNumberOfBits [u];
				xValue		|= (xBitfield.m_axValues [u] << uCurrentBit);
			}

			#if defined(ASURA_ASSERT)							 
			const u_int uNumBits = ((sizeof(T) * 8) - uCurrentBit);
			const T		xUsedMask				= ((sizeof(T) * 8) == uNumBits)
												? -1
												: static_cast<T>((1 << uNumBits) - 1);
			Asura_Assert((xUsedMask & xBitfield.m_xExpectedValue) == xBitfield.m_xExpectedValue, "Bitfield value uses bits that haven't been defined in the bitfield wrapper.\nThese will behave differently depending on whether a big endian file is being saved");
			#endif
		}

		else
		{
			u_int	uCurrentBit	= 0;

			// No - start from the bottom bit as usual...
			for (u_int u = 0; u < xBitfield.m_uNumberOfEntries; u++)
			{
				xValue		|= (xBitfield.m_axValues [u] << uCurrentBit);
				uCurrentBit	+= xBitfield.m_auNumberOfBits [u];
			}

			#if defined(ASURA_ASSERT)
			const u_int uNumBits = uCurrentBit;
			const T		xUsedMask				= ((sizeof(T) * 8) == uNumBits)
												? -1
												: static_cast<T>((1 << uNumBits) - 1);
			const T		xMaskedExpectedValue	= xBitfield.m_xExpectedValue & xUsedMask;

			// Not swapping - compare with the original value...
			Asura_Assert	((xMaskedExpectedValue == xValue), "Bitfield generated differs from the expected value - is your bitfield correctly defined?");
			Asura_Assert((xUsedMask & xBitfield.m_xExpectedValue) == xBitfield.m_xExpectedValue, "Bitfield value uses bits that haven't been defined in the bitfield wrapper.\nThese will behave differently depending on whether a big endian file is being saved");
			#endif	// defined(ASURA_ASSERT)

			// Force the expected value to be written...
			xValue	= xBitfield.m_xExpectedValue;
		}

		// Write the value...
		return	(*this << xValue);

#else // not def ASURA_TOOLS or def ASURA_MAIN

        return( *this << xBitfield.m_xValue );

#endif // else not def ASURA_TOOLS or def ASURA_MAIN
	}
	template<typename T> Asura_Chunk_Stream& operator>> (Asura_ReadFromStream_Bitfield_Wrapper<T>& xBitfield)
	{
		return	(*this >> xBitfield.m_xValue);
	}

    // Enum-wrapper functions...
    template<typename T> Asura_Chunk_Stream& operator<< (const Asura_WriteToStream_Enum_Wrapper<T>& xEnum)
    {
#if defined ASURA_TOOLS || !defined ASURA_MAIN
        if( m_bSwapEndian )
        {
            T xSwappedValue = Asura_Endian::SwapBytes( xEnum.m_xValue );
            Write( sizeof(T), &xSwappedValue );
        }
        else
#endif
        {
            Write( sizeof(T), &xEnum.m_xValue );
        }

        return *this;
    }
	template<typename T> Asura_Chunk_Stream& operator>> (Asura_ReadFromStream_Enum_Wrapper<T>& xEnum)
	{
		Read	(sizeof(T), &xEnum.m_xValue);
		return	(*this);
	}


protected:

	// protected functions

	//Reads a string. If the pointer is null , then it just skips past the string instead
	void ReadString(Asura_String* pxString);

	// protected member data

	// For reading chunks
	STREAM_STATE m_eState;
	union
	{
		int m_iChunkID;
		char m_acTextID[4];
	};
#ifdef ASURA_CHUNK_STREAM_ALLOW_OTHER_IO
	// We have to do extra error checking if external IO is allowed
	int m_iExpectedEndPosition;
#else
	int m_iByteCounter;
#endif

	// For writing chunks
	u_int m_uChunkStartPosition;
	int m_iChunkSize;
	u_int m_uResourceFileSizeOffset;

	// protected static data
	// Padding...
	static u_char s_ucPaddingValue;

private:
#if defined ASURA_TOOLS || !defined ASURA_MAIN
	// Must be set via SetSwapEndian
	bool m_bSwapEndian;
	// Record whether we have set swap endian yet, in order to
	// catch potential errors where we're exporting but haven't
	// specifically requested one way or the other.
	bool m_bHasSetSwapEndian;
#endif
};

// KOR - used for loading Asura files.
class Asura_Chunk_File_Stream : public Asura_Chunk_Stream
{
public:
	// Constructor from filehandle
	Asura_Chunk_File_Stream(Asura_Handle iFileHandle)
	: m_iFileHandle(iFileHandle)
	{}
	// Destructor
	virtual ~Asura_Chunk_File_Stream() {}

	// Accessors
	virtual Asura_Handle GetFileHandle() const { return m_iFileHandle; }

	// Implementation of pure virtual Asura_Chunk_Stream functions
	u_int Read(u_int uNumberOfBytes, void* pData);
	u_int Skip(u_int uNumberOfBytes);
	u_int Write(u_int uNumberOfBytes, const void* pData);
	u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition);
	u_int GetPosition() const;

	//No peeking on File Stream
	virtual int PeekInt() { Asura_Assert(false, "CHUNK ERROR: Asura_Chunk_File_Stream::PeekInt - Peeking not allowed on File Streams"); return 0; }

private:
	// Private members
	Asura_Handle m_iFileHandle;
};

class Asura_Chunk_SaveBlock_Stream : public Asura_Chunk_Stream
{
public:
	// Constructor from saveblock
	Asura_Chunk_SaveBlock_Stream(Asura_SaveBlock_Base* pxSaveBlock);
#if defined ASURA_TOOLS || !defined ASURA_MAIN
	Asura_Chunk_SaveBlock_Stream(Asura_SaveBlock_Base* pxSaveBlock, const bool bSwapEndian);
#endif	//defined ASURA_TOOLS || !defined ASURA_MAIN
	//Asura_Chunk_SaveBlock_Stream(class Asura_ProtoSaveBlock* pxSaveBlock);
	// Destructor
	virtual ~Asura_Chunk_SaveBlock_Stream() {}

	// Implementation of pure virtual Asura_Chunk_Stream functions
	u_int Read(u_int uNumberOfBytes, void* pData);
	u_int Skip(u_int uNumberOfBytes);
	u_int Write(u_int uNumberOfBytes, const void* pData);
	u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition);
	u_int GetPosition() const;

	//No peeking on writable saveblock Stream
	virtual int PeekInt() { Asura_Assert(false, "CHUNK ERROR: Asura_Chunk_SaveBlock_Stream::PeekInt - Peeking not allowed on SaveBlock Streams"); return 0; }

private:
	// Private members
	Asura_SaveBlock_Base*	m_pxSaveBlock;
	//class Asura_ProtoSaveBlock*	m_pxSaveBlock;
};

class Asura_Chunk_SaveBlockReader_Stream : public Asura_Chunk_Stream
{
public:
	// Constructor from saveblock
	Asura_Chunk_SaveBlockReader_Stream(const Asura_SaveBlock_Base* pxSaveBlock);
	//Asura_Chunk_SaveBlockReader_Stream(const class Asura_ProtoSaveBlock* pxSaveBlock);
	// Destructor
	virtual ~Asura_Chunk_SaveBlockReader_Stream() {}

	// Implementation of pure virtual Asura_Chunk_Stream functions
	u_int Read(u_int uNumberOfBytes, void* pData);
	u_int Skip(u_int uNumberOfBytes);
	u_int Write(u_int uNumberOfBytes, const void* pData);
	u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition);
	u_int GetPosition() const;

	// Implementation of peeking functions
	virtual int PeekInt();

private:
	// Private members
	const Asura_SaveBlock_Base*	m_pxSaveBlock;
	// data to deal with chunk Peeking.
	char m_acPeekedBytes[4];		// assume sizeof(int) == 4
	char* m_pcPeekedDataPtr;
	u_int m_uPeekedBytesRemaining;	// while this is non-zero, read data from above array
};

////////////////////////////////////////////////////////////////////////////////
// ART - a stream for a pre-defined block of memory...
////////////////////////////////////////////////////////////////////////////////

class Asura_Chunk_Memory_Stream : public Asura_Chunk_Stream
{
public:
	// Public functions...

	// Constructor to create a stream that is in charge of it's own memory buffer.
	// Initial size of pool determined by parameter - defaults to 128k.
	Asura_Chunk_Memory_Stream( const u_int uInitialSizeOfBlock = 128*1024 )
	{
		m_pucStartOfBlock	= new u_char[ uInitialSizeOfBlock ];
		m_uCurrentPosition	= 0;
		m_uSizeOfBlock		= uInitialSizeOfBlock;
		m_uPrePeekCurrentPosition = 0;
		m_iPrePeekChunkSize = 0;
		m_bPeekEnabled = false;
		m_bReadOnly = false;
		m_bOwnsBuffer = true;
	}

	Asura_Chunk_Memory_Stream (const u_int uSizeOfBlock, void* pStartOfBlock)
	{
		m_pucStartOfBlock	= static_cast<u_char*>(pStartOfBlock);
		m_uCurrentPosition	= 0;
		m_uSizeOfBlock		= uSizeOfBlock;
		m_uPrePeekCurrentPosition = 0;
		m_iPrePeekChunkSize = 0;
		m_bPeekEnabled = false;
		m_bReadOnly = false;
		m_bOwnsBuffer = false;
	}

	//Constructor for read-only memory streams. RAR 02/11/2005
	Asura_Chunk_Memory_Stream (const u_int uSizeOfBlock, const void* pStartOfBlock)
	{
		m_pucStartOfBlock	= static_cast<u_char*>(const_cast<void*>(pStartOfBlock));
		m_uCurrentPosition	= 0;
		m_uSizeOfBlock		= uSizeOfBlock;
		m_uPrePeekCurrentPosition = 0;
		m_iPrePeekChunkSize = 0;
		m_bPeekEnabled = false;
		m_bReadOnly = true;
		m_bOwnsBuffer = false;
	}


	virtual ~Asura_Chunk_Memory_Stream ()
	{
		// if we're in charge of the buffer, delete it
		if (m_bOwnsBuffer && !m_bReadOnly)
		{
			delete [] m_pucStartOfBlock;		
		}
	}

	// Implementation of pure virtual Asura_Chunk_Stream functions
	virtual u_int Read(u_int uNumberOfBytes, void* pData);
	virtual u_int Skip(u_int uNumberOfBytes);
	virtual u_int Write(u_int uNumberOfBytes, const void* pData);
	virtual u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition);
	virtual u_int GetPosition(void) const;
	virtual int PeekInt();

	void SetPosition(u_int uPosition);
	void* GetAddress(void) const { return (&m_pucStartOfBlock[m_uCurrentPosition]); }
	const void* GetStartAddress( void ) const { return &(m_pucStartOfBlock[0]); }
    void* GetEndAddress( void ) const { return &m_pucStartOfBlock[m_uSizeOfBlock]; }

	//Relinquishes ownership of the memory buffer to the caller
	u_char* TakeOwnershipOfMemory()
	{
		Asura_Assert(m_bOwnsBuffer,"Asura_Chunk_Memory_Stream doesn't own the memory buffer to relinquish it");
		m_bOwnsBuffer = false;
		return m_pucStartOfBlock;
	}

private:

	//For peeking at streams - use legacy peek code.
	void BeginPeek();
	void EndPeek();

	void GrowBuffer( u_int uMinimumRequired );

	// Private variables...
	u_char*	m_pucStartOfBlock;
	u_int	m_uCurrentPosition;
	u_int	m_uSizeOfBlock;

	//For peeking at stream
	u_int	m_uPrePeekCurrentPosition;
	int     m_iPrePeekChunkSize;
	bool	m_bPeekEnabled;

	bool	m_bReadOnly;

	// This flag indicates that the stream has allocated it's own buffer
	// and it can change its size if it wishes.
	bool	m_bOwnsBuffer; 

	// Private functions...
	Asura_Chunk_Memory_Stream(const Asura_Chunk_Memory_Stream& xOther);

	Asura_Chunk_Memory_Stream& operator= (const Asura_Chunk_Memory_Stream& xOther);

};   // End of class 'Asura_Chunk_Memory_Stream'.


////////////////////////////////////////////////////////////////////////////////
// NJD - a stream for a chunk...
////////////////////////////////////////////////////////////////////////////////

class Asura_Chunk_ChunkBuffer_Stream : public Asura_Chunk_Stream
{
public:
    // Public functions...

    Asura_Chunk_ChunkBuffer_Stream (char*& pcStartOfBlock)
    : m_pcStartOfBlock( pcStartOfBlock )
    , m_pcMovingStartOfBlock( pcStartOfBlock )
    , m_uCurrentPosition( 0 )
	, m_pcPrePeekMovingStartOfBlock( 0 )
	, m_uPrePeekCurrentPosition( 0 )
	, m_iPrePeekChunkSize( 0 )
	, m_bPeekEnabled(false)
    , m_bReadOnly( false )
    {
#ifdef ASURA_TOOLS
		SetSwapEndian(Tools_Level_Export::ShouldExportBigEndian());
#endif
    }

    Asura_Chunk_ChunkBuffer_Stream (const char*& pcStartOfBlock)
    : m_pcStartOfBlock( const_cast< char* >( pcStartOfBlock ) )
    , m_pcMovingStartOfBlock( const_cast< char*& >( pcStartOfBlock ) )
    , m_uCurrentPosition( 0 )
	, m_pcPrePeekMovingStartOfBlock( 0 )
	, m_uPrePeekCurrentPosition( 0 )
	, m_iPrePeekChunkSize( 0 )
	, m_bPeekEnabled(false)
    , m_bReadOnly( true )
    {
    }

    virtual ~Asura_Chunk_ChunkBuffer_Stream ()	{}

    // Implementation of pure virtual Asura_Chunk_Stream functions
    virtual u_int Read(u_int uNumberOfBytes, void* pData);
    virtual u_int Skip(u_int uNumberOfBytes);
	virtual u_int Write(u_int uNumberOfBytes, const void* pData);
	virtual u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition);
    virtual u_int GetPosition(void) const { return m_uCurrentPosition; }
	virtual int PeekInt();

private:

    // Private variables...
    char*  m_pcStartOfBlock;
    char*& m_pcMovingStartOfBlock;
    u_int  m_uCurrentPosition;

	//For Peeking at stream
	char*  m_pcPrePeekMovingStartOfBlock;
	u_int  m_uPrePeekCurrentPosition;
	int    m_iPrePeekChunkSize;
	bool   m_bPeekEnabled;

    bool   m_bReadOnly;

    // Private functions...
    Asura_Chunk_ChunkBuffer_Stream(void);
    Asura_Chunk_ChunkBuffer_Stream(const Asura_Chunk_ChunkBuffer_Stream& xOther);

    Asura_Chunk_ChunkBuffer_Stream& operator= (const Asura_Chunk_ChunkBuffer_Stream& xOther);

	//For Peeking at stream - using legacy code
	virtual void BeginPeek();
	virtual void EndPeek();

};   // End of class 'Asura_Chunk_ChunkBuffer_Stream'.


////////////////////////////////////////////////////////////////////////////////
// ART - a dummy stream, used for finding the size of data...
////////////////////////////////////////////////////////////////////////////////

class Asura_Chunk_Dummy_Stream : public Asura_Chunk_Stream
{
public:

	// Public functions...
	Asura_Chunk_Dummy_Stream (void)
	: m_uCurrentPosition( 0 )
	, m_bPeekEnabled(false)
	, m_uPrePeekCurrentPosition( 0 )
	{
#if defined ASURA_TOOLS || !defined ASURA_MAIN
		SetSwapEndian(false);
#endif
	}
	virtual ~Asura_Chunk_Dummy_Stream ()	{}

	// Implementation of pure virtual Asura_Chunk_Stream functions
	virtual u_int Read(u_int uNumberOfBytes, void* pData)							{ m_uCurrentPosition += uNumberOfBytes; m_iChunkSize += uNumberOfBytes; return (uNumberOfBytes); }
	virtual u_int Skip(u_int uNumberOfBytes)										{ m_uCurrentPosition += uNumberOfBytes; m_iChunkSize += uNumberOfBytes;return (uNumberOfBytes); }
	virtual u_int Write(u_int uNumberOfBytes, const void* pData)					{ m_uCurrentPosition += uNumberOfBytes; m_iChunkSize += uNumberOfBytes;return (uNumberOfBytes); }
	virtual u_int Write(u_int uNumberOfBytes, const void* pData, u_int iPosition)	{ return (uNumberOfBytes); }
	virtual u_int GetPosition(void) const { return (m_uCurrentPosition); }
	virtual int PeekInt();

private:

	// Private variables...
	u_int	m_uCurrentPosition;

	//For Peeking at stream
	bool	m_bPeekEnabled;
	u_int	m_uPrePeekCurrentPosition;

	// Private functions... (unimplemented copy ctor and operator=)
	Asura_Chunk_Dummy_Stream(const Asura_Chunk_Dummy_Stream& xOther);
	Asura_Chunk_Dummy_Stream& operator=(const Asura_Chunk_Dummy_Stream& xOther);

	//For peeking at Stream
	void BeginPeek()
	{
		Asura_Assert(!m_bPeekEnabled, "peek already enabled");
		m_bPeekEnabled = true;
		m_uPrePeekCurrentPosition = m_uCurrentPosition;
	}
	void EndPeek()
	{
		Asura_Assert(m_bPeekEnabled, "peek was not already enabled");
		m_bPeekEnabled = false;
		m_uCurrentPosition = m_uPrePeekCurrentPosition;
	}

};   // End of class 'Asura_Chunk_Dummy_Stream'.

#if defined(ASURA_TOOLS) && defined(ASURA_ASSERT) && defined(ASURA_MAIN)
#define TEST_ENTITY_CHUNK_STREAM (1)

template <class T>
void TestStream(Asura_Guid uGuid, const char* pcChunkBuffer)
{
	const char* const pcOriginalChunkBufferStart = pcChunkBuffer;
	const char* pcOriginalChunkBuffer = pcOriginalChunkBufferStart;

	// Create entity via chunk buffer
	Asura_Entity_Server* pxEntity = new T(uGuid, pcOriginalChunkBuffer);
	const u_int uOriginalChunkSize = pcOriginalChunkBuffer - pcOriginalChunkBufferStart;
	const u_int uLatestChunkSize = pxEntity->GetChunkBufferSize();
	char* const pcLatestChunkBufferStart = new char[uLatestChunkSize];
	char* pcLatestChunkBuffer = pcLatestChunkBufferStart;
	pxEntity->FillInChunkBuffer(pcLatestChunkBuffer);
	TestStreamWrite(pxEntity, pcLatestChunkBufferStart, uLatestChunkSize);
	delete pxEntity;

	// Use ReadFromChunkStream on original chunk buffer
	Asura_Chunk_Memory_Stream xOriginalReadStream(uOriginalChunkSize, pcOriginalChunkBufferStart);
	pxEntity = new T(uGuid);
	pxEntity->ReadFromChunkStream(xOriginalReadStream);
	TestStreamWrite(pxEntity, pcLatestChunkBufferStart, uLatestChunkSize);
	delete pxEntity;

	// Use ReadFromChunkStream on latest chunk buffer
	Asura_Chunk_Memory_Stream xLatestReadStream(uLatestChunkSize, pcLatestChunkBufferStart);
	pxEntity = new T(uGuid);
	pxEntity->ReadFromChunkStream(xLatestReadStream);
	TestStreamWrite(pxEntity, pcLatestChunkBufferStart, uLatestChunkSize);
	delete pxEntity;

	delete [] pcLatestChunkBufferStart;
}

void TestStreamWrite(class Asura_Entity_Server* pxEntity, const char* pcChunkBuffer, const u_int uChunkSize);
#endif

#endif//__ASURA_CHUNK_STREAM_H__
