#include "ascript/Stream.h"
#include "ascript/OAL.h"
#include "ascript/String.h"
#include "ascript/Formatter.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Stream
//-----------------------------------------------------------------------------
Stream::Stream(Signal sig, unsigned long attr) : _pDecoder(NULL), _pEncoder(NULL),
		_cntRef(1), _sig(sig), _attr(attr), _offsetCur(0)
{
	_peek.buff = NULL;
	_peek.bytes = 0;
	_peek.offsetRead = 0;
}

Stream::~Stream()
{
	ReleaseCodec();
	delete[] _peek.buff;
}

void Stream::Close()
{
	DoClose(_sig);
	_attr &= ~(ATTR_Readable | ATTR_Writable | ATTR_Append);
}

bool Stream::InstallCodec(const char *encoding, bool processEOLFlag)
{
	if (encoding == NULL) {
		ReleaseCodec();
		return true;
	}
	CodecFactory *pCodecFactory = CodecFactory::Lookup(encoding);
	if (pCodecFactory == NULL) return false;
	ReleaseCodec();
	_encoding = encoding;
	_pDecoder = pCodecFactory->CreateDecoder(true);
	_pEncoder = pCodecFactory->CreateEncoder(processEOLFlag);
	return true;
}

void Stream::ReleaseCodec()
{
	_encoding.clear();
	delete _pDecoder;
	delete _pEncoder;
	_pDecoder = NULL;
	_pEncoder = NULL;
}

void Stream::PutChar(Signal sig, char ch)
{
	if (_pEncoder == NULL) {
		DoPutChar(sig, ch);
	} else {
		char chConv;
		Codec::Result rtn = _pEncoder->FeedChar(ch, chConv);
		if (rtn == Codec::RESULT_Complete) {
			DoPutChar(sig, chConv);
			while (_pEncoder->FollowChar(chConv)) DoPutChar(sig, chConv);
		} else if (rtn == Codec::RESULT_Error) {
			// nothing to do
		}
	}
}

int Stream::GetChar(Signal sig)
{
	if (_pDecoder == NULL) return DoGetChar(sig);
	char chConv;
	if (_pDecoder->FollowChar(chConv)) return static_cast<unsigned char>(chConv);
	for (;;) {
		int ch = DoGetChar(sig);
		if (ch < 0) return ch;
		Codec::Result rtn = _pDecoder->FeedChar(static_cast<char>(ch), chConv);
		if (rtn == Codec::RESULT_Complete) {
			break;
		} else if (rtn == Codec::RESULT_Error) {
			sig.SetError(ERR_CodecError, "not a valid character of %s", _pDecoder->GetName());
			return -1;
		}
	}
	return static_cast<unsigned char>(chConv);
}

void Stream::Print(Signal sig, const char *str)
{
	for ( ; *str != '\0'; str++) PutChar(sig, *str);
	FlushConsole();
}

void Stream::Println(Signal sig, const char *str)
{
	Print(sig, str);
	PutChar(sig, '\n');
	FlushConsole();
}

void Stream::Printf(Signal sig, const char *format, const ValueList &valList)
{
	String str = Formatter::Format(sig, format, valList);
	if (sig.IsSignalled()) return;
	Print(sig, str.c_str());
}

void Stream::PrintSignal(Signal sig, const Signal &sigToPrint)
{
	if (sig.GetSignalType() == SIGTYPE_Error) {
		Println(sig, sigToPrint.GetErrString().c_str());
		Print(sig, sigToPrint.GetErrTrace().c_str());
	} else {
		Println(sig, sigToPrint.GetSignalName());
	}
}

void Stream::FlushConsole()
{
}

size_t Stream::DoRead(Signal sig, void *buff, size_t len)
{
	return 0;
}

size_t Stream::DoWrite(Signal sig, const void *buff, size_t len)
{
	return 0;
}

bool Stream::DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode)
{
	return true;
}

bool Stream::DoFlush(Signal sig)
{
	return true;
}

bool Stream::DoClose(Signal sig)
{
	return true;
}

size_t Stream::DoGetSize()
{
	return InvalidSize;
}

int Stream::DoGetChar(Signal sig)
{
	unsigned char ch;
	if (Read(sig, &ch, 1) == 0) return -1;
	return ch;
}

void Stream::DoPutChar(Signal sig, char ch)
{
	Write(sig, &ch, 1);
}

Object *Stream::DoGetStatObj(Signal sig)
{
	sig.SetError(ERR_IOError, "can't retrieve stat object");
	return NULL;
}

size_t Stream::Read(Signal sig, void *buff, size_t len)
{
	char *p = reinterpret_cast<char *>(buff);
	size_t bytesFromPeek = 0;
	if (_peek.buff != NULL) {
		bytesFromPeek = _peek.bytes - _peek.offsetRead;
		if (bytesFromPeek > len) bytesFromPeek = len;
		::memcpy(p, _peek.buff + _peek.offsetRead, bytesFromPeek);
		p += bytesFromPeek;
		_peek.offsetRead += bytesFromPeek;
		if (_peek.offsetRead >= _peek.bytes) {
			delete[] _peek.buff;
			_peek.buff = NULL;
			_peek.bytes = 0;
			_peek.offsetRead = 0;
		}
		len -= bytesFromPeek;
	}
	if (len == 0) {
		_offsetCur += bytesFromPeek;
		return bytesFromPeek;
	}
	size_t bytesRead = bytesFromPeek + DoRead(sig, p, len);
	_offsetCur += bytesRead;
	return bytesRead;
}

size_t Stream::Write(Signal sig, const void *buff, size_t len)
{
	size_t rtn = DoWrite(sig, buff, len);
	_offsetCur += rtn;
	return rtn;
}

size_t Stream::Peek(Signal sig, void *buff, size_t len)
{
	if (len == 0) return 0;
	if (_peek.buff == NULL) {
		_peek.buff = new char [len];
		_peek.bytes = DoRead(sig, _peek.buff, len);
		_peek.offsetRead = 0;
		if (sig.IsSignalled()) return 0;
	} else if (_peek.bytes < len) {
		char *buffNew = new char [len];
		::memcpy(buffNew, _peek.buff, _peek.bytes);
		size_t bytesAdd = DoRead(sig, buffNew + _peek.bytes, len - _peek.bytes);
		if (sig.IsSignalled()) return 0;
		delete[] _peek.buff;
		_peek.buff = buffNew;
		_peek.bytes += bytesAdd;
		_peek.offsetRead = 0;
	}
	size_t bytesToPeek = ChooseMin(_peek.bytes, len);
	::memcpy(buff, _peek.buff, bytesToPeek);
	return bytesToPeek;
}

bool Stream::Seek(Signal sig, long offset, SeekMode seekMode)
{
	size_t offsetPrev = _offsetCur;
	if (seekMode == SeekSet) {
		_offsetCur = static_cast<size_t>(offset);
	} else if (seekMode == SeekCur) {
		_offsetCur = _offsetCur + offset;
	} else {
		// this must not happen because illegal value has to be rejected before.
		return false;
	}
	if (_peek.buff == NULL) return DoSeek(sig, offset, offsetPrev, seekMode);
	if (_offsetCur < offsetPrev) {
		size_t bytesPeeked = _peek.bytes;
		if (_peek.offsetRead >= offsetPrev - _offsetCur) {
			_peek.offsetRead -= (offsetPrev - _offsetCur);
			return true;
		}
		delete[] _peek.buff;
		_peek.buff = NULL;
		_peek.bytes = 0;
		_peek.offsetRead = 0;
		if (seekMode == SeekSet) return DoSeek(sig, offset, offsetPrev, SeekSet);
		offset -= static_cast<long>(bytesPeeked);
		return DoSeek(sig, offset, offsetPrev, SeekCur);
	} else {
		if (_peek.offsetRead + _offsetCur - offsetPrev <= _peek.bytes) {
			_peek.offsetRead += _offsetCur - offsetPrev;
			return true;
		}
		size_t bytesTrail = _peek.bytes - _peek.offsetRead;
		delete[] _peek.buff;
		_peek.buff = NULL;
		_peek.bytes = 0;
		_peek.offsetRead = 0;
		if (seekMode == SeekSet) return DoSeek(sig, offset, offsetPrev, SeekSet);
		offset -= static_cast<long>(bytesTrail);
		return DoSeek(sig, offset, offsetPrev, SeekCur);
	}
}

bool Stream::Flush(Signal sig)
{
	return DoFlush(sig);
}

bool Stream::HasNameSuffix(const char *suffix, bool ignoreCase) const
{
	return EndsWith(GetName(), suffix, ignoreCase);
}

bool Stream::CheckReadable(Signal sig) const
{
	if (IsReadable()) return true;
	sig.SetError(ERR_IOError, "stream is not readable");
	return false;
}

bool Stream::CheckWritable(Signal sig) const
{
	if (IsWritable()) return true;
	sig.SetError(ERR_IOError, "stream is not writable");
	return false;
}

bool Stream::CheckBwdSeekable(Signal sig) const
{
	if (IsBwdSeekable()) return true;
	sig.SetError(ERR_IOError, "stream is not capable of backward seeking");
	return false;
}

bool Stream::Compare(Signal sig, Stream &stream)
{
	if (!CheckReadable(sig) || !stream.CheckReadable(sig)) return false;
	const size_t bytesBuff = 1024 * 16;
	OAL::Memory memory(bytesBuff * 2);
	void *buff1 = memory.GetPointer(0);
	void *buff2 = memory.GetPointer(bytesBuff);
	bool sameFlag = false;
	for (;;) {
		size_t bytesRead1 = Read(sig, buff1, bytesBuff);
		if (sig.IsSignalled()) return false;
		size_t bytesRead2 = stream.Read(sig, buff2, bytesBuff);
		if (sig.IsSignalled()) return false;
		if (bytesRead1 != bytesRead2) {
			break;
		} else if (bytesRead1 == 0) {
			sameFlag = true;
			break;
		} else if (::memcmp(buff1, buff2, bytesRead1) != 0) {
			break;
		}
	}
	return sameFlag;
}

bool Stream::ReadToStream(Signal sig, Stream &streamDst)
{
	if (!CheckReadable(sig) || !streamDst.CheckWritable(sig)) return false;
	const size_t bytesBuff = 1024 * 16;
	OAL::Memory memory(bytesBuff);
	void *buff = memory.GetPointer();
	for (;;) {
		size_t bytesRead = Read(sig, buff, bytesBuff);
		if (bytesRead == 0) break;
		streamDst.Write(sig, buff, bytesRead);
		if (sig.IsSignalled()) return false;
	}
	if (sig.IsSignalled()) return false;
	return streamDst.Flush(sig);
}

Stream *Stream::Prefetch(Signal sig, Stream *pStreamSrc,
										bool deleteSrcFlag, size_t bytesUnit)
{
	Stream_Prefetch *pStreamPrefetch = new Stream_Prefetch(sig, bytesUnit);
	pStreamPrefetch->DoPrefetch(sig, pStreamSrc);
	if (deleteSrcFlag) Stream::Delete(pStreamSrc);
	if (sig.IsSignalled()) {
		Stream::Delete(pStreamPrefetch);
		return NULL;
	}
	return pStreamPrefetch;
}

//-----------------------------------------------------------------------------
// Stream_Prefetch
//-----------------------------------------------------------------------------
Stream_Prefetch::Stream_Prefetch(Signal sig, size_t bytesUnit) :
	Stream(sig, ATTR_Readable), _offset(0), _bytesAll(0), _bytesUnit(bytesUnit)
{
}

Stream_Prefetch::~Stream_Prefetch()
{
	foreach (MemoryList, ppMemory, _memoryList) {
		OAL::Memory *pMemory = *ppMemory;
		delete pMemory;
	}
}

const char *Stream_Prefetch::GetName() const
{
	return "<stream:prefetch>";
}

size_t Stream_Prefetch::DoRead(Signal sig, void *buff, size_t len)
{
	MemoryList::iterator ppMemory = _memoryList.begin() + _offset / _bytesUnit;
	char *buffDst = reinterpret_cast<char *>(buff);
	size_t offsetUnit = _offset % _bytesUnit;
	size_t bytesCopied = 0;
	size_t bytesRest = len;
	for ( ; ppMemory != _memoryList.end() && bytesRest > 0; ppMemory++) {
		OAL::Memory *pMemory = *ppMemory;
		size_t bytesToCopy = _bytesUnit - offsetUnit;
		bytesToCopy = ChooseMin(bytesToCopy, bytesRest);
		::memcpy(buffDst + bytesCopied, pMemory->GetPointer(offsetUnit), bytesToCopy);
		offsetUnit = 0;
		bytesCopied += bytesToCopy;
		bytesRest -= bytesToCopy;
	}
	return bytesCopied;
}

size_t Stream_Prefetch::DoWrite(Signal sig, const void *buff, size_t len)
{
	return 0;
}

bool Stream_Prefetch::DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode)
{
	if (seekMode == SeekSet) {
		_offset = static_cast<size_t>(offset);
	} else if (seekMode == SeekCur) {
		_offset += offset;
	}
	return true;
}

bool Stream_Prefetch::DoFlush(Signal sig)
{
	return false;
}

bool Stream_Prefetch::DoClose(Signal sig)
{
	return true;
}

size_t Stream_Prefetch::DoGetSize()
{
	return _bytesAll;
}

bool Stream_Prefetch::DoPrefetch(Signal sig, Stream *pStream)
{
	_bytesAll = 0;
	for (;;) {
		OAL::Memory *pMemory = new OAL::Memory(_bytesUnit);
		size_t bytes = pStream->Read(sig, pMemory->GetPointer(), _bytesUnit);
		if (sig.IsSignalled()) {
			delete pMemory;
			return false;
		} else if (bytes == 0 || sig.IsSignalled()) {
			delete pMemory;
			break;
		}
		_memoryList.push_back(pMemory);
		_bytesAll += bytes;
		if (bytes < _bytesUnit) break;
	}
	return true;
}

//-----------------------------------------------------------------------------
// Stream_CRC32
//-----------------------------------------------------------------------------
Stream_CRC32::Stream_CRC32(Signal sig, Stream *pStreamDst) :
		Stream(sig, (pStreamDst == NULL)? ATTR_Writable : pStreamDst->GetAttr()),
		_pStreamDst(pStreamDst)
{
}

Stream_CRC32::~Stream_CRC32()
{
	Stream::Delete(_pStreamDst);
}

const char *Stream_CRC32::GetName() const
{
	return (_pStreamDst == NULL)? "" : _pStreamDst->GetName();
}

size_t Stream_CRC32::DoRead(Signal sig, void *buff, size_t len)
{
	return (_pStreamDst == NULL)? 0 : _pStreamDst->Read(sig, buff, len);
}

size_t Stream_CRC32::DoWrite(Signal sig, const void *buff, size_t len)
{
	_crc32.Update(buff, len);
	return (_pStreamDst == NULL)? len : _pStreamDst->Write(sig, buff, len);
}

bool Stream_CRC32::DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode)
{
	return (_pStreamDst == NULL)? true : _pStreamDst->Seek(sig, offset, seekMode);
}

bool Stream_CRC32::DoFlush(Signal sig)
{
	return (_pStreamDst == NULL)? true : _pStreamDst->Flush(sig);
}

bool Stream_CRC32::DoClose(Signal sig)
{
	return (_pStreamDst == NULL)? 0 : _pStreamDst->DoClose(sig);
}

size_t Stream_CRC32::DoGetSize()
{
	return (_pStreamDst == NULL)? 0 : _pStreamDst->GetSize();
}

}
