#pragma once

#include "common/IErrors.h"

/**
 *	An arbitrary data stream
 */
class IDataStream
{
	public:
		IDataStream();
		virtual ~IDataStream();

		// read
		virtual UInt8	Read8(void);
		virtual UInt16	Read16(void);
		virtual UInt32	Read32(void);
		virtual UInt64	Read64(void);
		virtual float	ReadFloat(void);
		virtual UInt32	ReadString(char * buf, UInt32 bufLength, char altTerminator = 0, char altTerminator2 = 0);
		virtual void	ReadBuf(void * buf, UInt32 inLength) = 0;

		// peek
		virtual UInt8	Peek8(void);
		virtual UInt16	Peek16(void);
		virtual UInt32	Peek32(void);
		virtual UInt64	Peek64(void);
		virtual float	PeekFloat(void);
		virtual void	PeekBuf(void * buf, UInt32 inLength);

		virtual void	Skip(SInt64 inBytes);

		// write
		virtual void	Write8(UInt8 inData);
		virtual void	Write16(UInt16 inData);
		virtual void	Write32(UInt32 inData);
		virtual void	Write64(UInt64 inData);
		virtual void	WriteFloat(float inData);
		virtual void	WriteString(const char * buf);
		virtual void	WriteBuf(const void * buf, UInt32 inLength) = 0;
		
		SInt64			GetLength(void);
		SInt64			GetRemain(void);
		SInt64			GetOffset(void);
		bool			HitEOF(void);

		virtual void	SetOffset(SInt64 inOffset);
		void			Rewind(void)	{ SetOffset(0); }

		void			SwapBytes(bool inSwapBytes);

		virtual SInt64			GetParentOffset(void)	{ return GetOffset(); }
		virtual IDataStream *	GetParent(void)			{ return NULL; }

		IDataStream *			GetRootParent(void);

		static void		CopyStreams(IDataStream * out, IDataStream * in, UInt64 bufferSize = 1024 * 1024, UInt8 * buf = NULL);
		static void		CopySubStreams(IDataStream * out, IDataStream * in, UInt64 remain, UInt64 bufferSize = 1024 * 1024, UInt8 * buf = NULL);

	protected:
		SInt64			streamLength;
		SInt64			streamOffset;
		bool			swapBytes;

	public:
		DEF_EXCEPTION(EOFException);
};

/**
 *	A utility class to automatically save and restore the current position of an IDataStream
 */
class IDataStream_PositionSaver
{
	public:
		IDataStream_PositionSaver(IDataStream * tgt);
		~IDataStream_PositionSaver();

	private:
		IDataStream	* stream;
		SInt64		offset;
};

class IDataSubStream : public IDataStream
{
	public:
		IDataSubStream();
		IDataSubStream(IDataStream * inStream, SInt64 inOffset, SInt64 inLength);
		~IDataSubStream();

		void	Attach(IDataStream * inStream, SInt64 inOffset, SInt64 inLength);

		void	ReadBuf(void * buf, UInt32 inLength);
		void	WriteBuf(const void * buf, UInt32 inLength);
		void	SetOffset(SInt64 inOffset);

		virtual SInt64			GetParentOffset(void)	{ return stream->GetOffset(); }
		virtual IDataStream *	GetParent(void)			{ return stream; }

		SInt64	GetSubBase(void)	{ return subBase; }

	private:
		IDataStream	* stream;

		SInt64	subBase;
};
