#ifndef __STREAM_H__
#define __STREAM_H__

#include "Signal.h"
#include "OAL.h"
#include "Codec.h"
#include "Algorithm.h"

namespace AScript {

class Object;
class ValueList;

//-----------------------------------------------------------------------------
// Stream
//-----------------------------------------------------------------------------
class DLLDECLARE Stream {
public:
	enum SeekMode { SeekSet, SeekCur };
	enum Error {
		ERROR_None,
		ERROR_Codec,
	};
	enum {
		ATTR_None			= 0,
		ATTR_Infinite		= (1 << 0),
		ATTR_BwdSeekable	= (1 << 1),
		ATTR_Readable		= (1 << 2),
		ATTR_Writable		= (1 << 3),
		ATTR_Append			= (1 << 4),
	};
protected:
	int _cntRef;
	Signal _sig;
	unsigned long _attr;
	size_t _offsetCur;
	String _encoding;
	Codec_Decoder *_pDecoder;
	Codec_Encoder *_pEncoder;
	struct {
		char *buff;
		size_t bytes;
		size_t offsetRead;
	} _peek;
public:
	inline static Stream *Reference(const Stream *pStream) {
		if (pStream == NULL) return NULL;
		Stream *pStreamCasted = const_cast<Stream *>(pStream);
		pStreamCasted->_cntRef++;
		return pStreamCasted;
	}
	inline static void Delete(Stream *pStream) {
		if (pStream == NULL) return;
		pStream->_cntRef--;
		if (pStream->_cntRef <= 0) delete pStream;
	}
protected:
	virtual ~Stream();
public:
	Stream(Signal sig, unsigned long attr);
	void Close();

	bool InstallCodec(const char *encoding, bool processEOLFlag);
	void ReleaseCodec();
	inline bool IsCodecInstalled() const { return !_encoding.empty(); }
	inline const char *GetEncoding() const { return _encoding.c_str(); }
	inline Codec_Decoder *GetDecoder() { return _pDecoder; }
	inline Codec_Encoder *GetEncoder() { return _pEncoder; }
	int GetChar(Signal sig);
	void PutChar(Signal sig, char ch);
	void Print(Signal sig, const char *str);
	void Printf(Signal sig, const char *format, const ValueList &valList);
	void Println(Signal sig, const char *str);
	void PrintSignal(Signal sig, const Signal &sigToPrint);
	virtual void FlushConsole();

	virtual const char *GetName() const = 0;
	virtual size_t DoRead(Signal sig, void *buff, size_t len);
	virtual size_t DoWrite(Signal sig, const void *buff, size_t len);
	virtual bool DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode);
	virtual bool DoFlush(Signal sig);
	virtual bool DoClose(Signal sig);
	virtual size_t DoGetSize();
	virtual int DoGetChar(Signal sig);
	virtual void DoPutChar(Signal sig, char ch);
	virtual Object *DoGetStatObj(Signal sig);
	inline int GetRefCount() const { return _cntRef; }
	inline unsigned long GetAttr() const { return _attr; }
	inline bool IsInfinite() const { return (_attr & ATTR_Infinite)? true : false; }
	inline bool IsReadable() const { return (_attr & ATTR_Readable)? true : false; }
	inline bool IsWritable() const { return (_attr & ATTR_Writable)? true : false; }
	inline bool IsAppend() const { return (_attr & ATTR_Append)? true : false; }
	inline bool IsBwdSeekable() const { return (_attr & ATTR_BwdSeekable)? true : false; }
	inline void SetReadable(bool flag) {
		_attr = (_attr & ~ATTR_Readable) | (flag? ATTR_Readable : 0);
	}
	inline void SetWritable(bool flag) {
		_attr = (_attr & ~ATTR_Writable) | (flag? ATTR_Writable : 0);
	}
	inline void SetAppend(bool flag) {
		_attr = (_attr & ~ATTR_Append) | (flag? ATTR_Append : 0);
	}
	size_t Read(Signal sig, void *buff, size_t len);
	size_t Write(Signal sig, const void *buff, size_t len);
	size_t Peek(Signal sig, void *buff, size_t len);
	bool Seek(Signal sig, long offset, SeekMode seekMode);
	inline size_t Tell() { return _offsetCur; }
	inline size_t GetSize() { return DoGetSize(); }
	inline Object *GetStatObj(Signal sig) { return DoGetStatObj(sig); }
	bool Flush(Signal sig);
	bool HasNameSuffix(const char *suffix, bool ignoreCase = true) const;
	bool CheckReadable(Signal sig) const;
	bool CheckWritable(Signal sig) const;
	bool CheckBwdSeekable(Signal sig) const;
	bool Compare(Signal sig, Stream &stream);
	bool ReadToStream(Signal sig, Stream &streamDst);
public:
	static Stream *Prefetch(Signal sig, Stream *pStreamSrc,
							bool deleteSrcFlag, size_t bytesUnit = 0x10000);
};

//-----------------------------------------------------------------------------
// Stream_Prefetch
//-----------------------------------------------------------------------------
class DLLDECLARE Stream_Prefetch : public Stream {
public:
	typedef std::vector<OAL::Memory *> MemoryList;
private:
	MemoryList _memoryList;
	size_t _offset;
	size_t _bytesAll;
	size_t _bytesUnit;
public:
	Stream_Prefetch(Signal sig, size_t bytesUnit);
	virtual ~Stream_Prefetch();
	virtual const char *GetName() const;
	virtual size_t DoRead(Signal sig, void *buff, size_t len);
	virtual size_t DoWrite(Signal sig, const void *buff, size_t len);
	virtual bool DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode);
	virtual bool DoFlush(Signal sig);
	virtual bool DoClose(Signal sig);
	virtual size_t DoGetSize();
	bool DoPrefetch(Signal sig, Stream *pStream);
};

//-----------------------------------------------------------------------------
// Stream_CRC32
//-----------------------------------------------------------------------------
class DLLDECLARE Stream_CRC32 : public Stream {
private:
	Stream *_pStreamDst;
	CRC32 _crc32;
public:
	Stream_CRC32(Signal sig, Stream *pStreamDst);
	virtual ~Stream_CRC32();
	virtual const char *GetName() const;
	virtual size_t DoRead(Signal sig, void *buff, size_t len);
	virtual size_t DoWrite(Signal sig, const void *buff, size_t len);
	virtual bool DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode);
	virtual bool DoFlush(Signal sig);
	virtual bool DoClose(Signal sig);
	virtual size_t DoGetSize();
	inline void Initialize() { _crc32.Initialize(); }
	inline unsigned long GetCRC32() const { return _crc32.GetResult(); }
	inline size_t GetBytes() const { return _crc32.GetBytes(); }
};

}

#endif
