#pragma once

#include <iostream>
#include "style.h"

//##########################################################################
//#######################    MemoryStream    ###############################
//##########################################################################

class MemoryStream
{
	friend MemoryStream& MemoryStream_Write(MemoryStream *stream, const MemoryStream *input_stream);

public:
	MemoryStream(void *stream_start, size_t stream_size, size_t initial_offset=0);

	size_t	GetIndex()		const	{ return currentPosition - streamStart; }
	size_t	GetSize()		const	{ return streamSize; }
	size_t	GetBytesUsed()	const	{ return currentPosition - streamStart; }
	void	Rewind()				{ SetPointer(0U); }
	bool	TestInstance()	const;
	
	virtual	void*	GetPointer()			const	{ return currentPosition; }
	virtual size_t	GetBytesRemaining()		const	{ return streamSize - GetBytesUsed(); }
	virtual void	SetPointer(void *new_pointer)	{ currentPosition = (char*)new_pointer; }
	virtual void	SetPointer(size_t index)		{ currentPosition = streamStart + index; }
	virtual bool	AllocateBytes(size_t count)		{ return GetBytesRemaining() <= count; }
	virtual MemoryStream&	RewindPointer(size_t count)		{ currentPosition -= count; return *this; }
	virtual MemoryStream&	AdvancePointer(size_t count)	{ currentPosition += count; return *this; }
	virtual MemoryStream&	ReadBytes(void *ptr, size_t number_of_bytes);
	virtual MemoryStream&	WriteBytes(const void *ptr, size_t number_of_bytes);
	
	void operator= (void *new_pointer) { SetPointer(new_pointer); }
	void operator= (size_t index) { SetPointer(index); }
	MemoryStream& operator+= (size_t count) { return AdvancePointer(count); }
	MemoryStream& operator-= (size_t count) { return RewindPointer(count); }

protected:
	char *streamStart, *currentPosition;
	size_t streamSize;
};

//--------------------------------------------------------------------------
// Extraction operators
//
template <class T> inline MemoryStream& operator>>(MemoryStream &stream, T &output)
{ return MemoryStream_Read(&stream, &output); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, char *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, unsigned char *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, short *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, unsigned short *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, int *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, unsigned int *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, long *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

inline MemoryStream& MemoryStream_Read(MemoryStream* stream, unsigned long *output)
{ return stream->ReadBytes(output, sizeof(*output)); }

//--------------------------------------------------------------------------
// Insertion operators
//
template <class T> inline MemoryStream& operator<<(MemoryStream &stream, const T &input)
{ return MemoryStream_Write(&stream, &input); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const char *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const unsigned char *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const short *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const unsigned short *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const int *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const unsigned int *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const long *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

inline MemoryStream& MemoryStream_Write(MemoryStream* stream, const unsigned long *input)
{ return stream->WriteBytes(input, sizeof(*input)); }

MemoryStream& MemoryStream_Write(MemoryStream* stream, std::istream *input);
MemoryStream& MemoryStream_Write(MemoryStream* stream, const MemoryStream* input_stream);

#define MEM_STRM_WRITE_ENTRY(stream, name_list, type, name)\
	{\
		type name;\
		const char* data = (const char*)(name_list)->FindData(#name);\
		if (!data)\
		{\
			DEBUG_STREAM << "DYN_MEM_STRM_WRITE_ENTRY - #name == " << std::flush;\
			DEBUG_STREAM << #name << "\n" << std::flush;\
			Fail("DYN_MEM_STRM_WRITE_ENTRY - (name_list)->FindData(#name) == NULL");\
		}\
		Check_Pointer(data);\
		Convert_From_Ascii(data, &name);\
		stream << name;\
	}

//##########################################################################
//###################    DynamicMemoryStream    ############################
//##########################################################################

class DynamicMemoryStream : public MemoryStream
{
public:
	DynamicMemoryStream(size_t stream_size = 0);
	DynamicMemoryStream(void *stream_start, size_t stream_size, size_t initial_offset = 0);
	~DynamicMemoryStream();

	bool	TestInstance()		const	{ return ((size_t)(currentPosition - streamStart) <= streamSize && streamLength <= streamSize); }
	size_t	GetBytesRemaining()	const	{ return streamSize - GetBytesUsed(); }
	bool	AllocateBytes(size_t count);

	MemoryStream& WriteBytes(const void *ptr, size_t number_of_bytes);

private:
	size_t streamLength;
	bool ownsStream;
};