#include "ascript/Object_Binary.h"
#include "ascript/Object_Stream.h"
#include "ascript/Object_Codec.h"
#include "ascript/Codec.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Binary
//-----------------------------------------------------------------------------
Object_Binary::Object_Binary(const Object_Binary &obj) : Object(obj), _binary(obj._binary)
{
}

Object_Binary::~Object_Binary()
{
}

Object *Object_Binary::Clone() const
{
	return new Object_Binary(*this);
}

Value Object_Binary::IndexGet(Signal sig, const Value &valueIdx)
{
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number for binary");
		return Value::Null;
	}
	int idx = valueIdx.GetInt();
	int len = static_cast<int>(_binary.size());
	if (idx >= 0) {
		if (idx >= len) {
			sig.SetError(ERR_IndexError, "index is out of range");
			return Value::Null;
		}
		return Value(static_cast<unsigned char>(_binary[idx]));
	} else {
		if (-idx > len) {
			sig.SetError(ERR_IndexError, "index is out of range");
			return Value::Null;
		}
		return Value(static_cast<unsigned char>(_binary[len + idx]));
	}
}

void Object_Binary::IndexSet(Signal sig, const Value &valueIdx, const Value &value)
{
	if (!valueIdx.IsNumber()) {
		sig.SetError(ERR_IndexError, "index must be a number for binary");
		return;
	}
	if (!value.IsNumber()) {
		sig.SetError(ERR_IndexError, "value must be a number for binary");
		return;
	}
	int idx = valueIdx.GetInt();
	long data = value.GetLong();
	if (data < 0 || data > 255) {
		sig.SetError(ERR_IndexError, "value must be between 0 and 255");
		return;
	}
	int len = static_cast<int>(_binary.size());
	if (idx >= 0) {
		if (idx >= len) {
			sig.SetError(ERR_IndexError, "index is out of range");
			return;
		}
		_binary[idx] = static_cast<unsigned char>(data);
	} else {
		if (-idx > len) {
			sig.SetError(ERR_IndexError, "index is out of range");
			return;
		}
		_binary[len + idx] = static_cast<unsigned char>(data);
	}
}

Iterator *Object_Binary::CreateIterator(Signal sig)
{
	return new IteratorByte(Object_Binary::Reference(this), -1);
}

String Object_Binary::ToString(Signal sig, bool exprFlag)
{
	char buff[64];
	::sprintf(buff, "<binary:%dbyte>", _binary.size());
	return String(buff);
}

bool Object_Binary::Pack(Signal sig, size_t &offset,
							const char *format, const ValueList &valList)
{
	enum {
		STAT_Format,
		STAT_Repeat,
		STAT_Encoding,
	} stat = STAT_Format;
	Environment &env = *this;
	ValueList::const_iterator pValue = valList.begin();
	bool bigEndianFlag = IsBigEndian();
	int nRepeat = 1;
	String encoding;
	std::auto_ptr<Codec> pCodec(new Codec_None());
	for (const char *p = format; *p != '\0'; ) {
		char ch = *p;
		bool eatNextFlag = true;
		if (stat == STAT_Repeat) {
			if (IsDigit(ch)) {
				nRepeat = nRepeat * 10 + (ch - '0');
			} else {
				eatNextFlag = false;
				stat = STAT_Format;
			}
		} else if (stat == STAT_Encoding) {
			if (ch == '}') {
				if (encoding.empty()) {
					pCodec.reset(new Codec_None());
				} else {
					CodecFactory *pCodecFactory =
									CodecFactory::Lookup(encoding.c_str());
					if (pCodecFactory == NULL) {
						sig.SetError(ERR_CodecError,
								"unsupported encoding '%s'", encoding.c_str());
						return false;
					}
					pCodec.reset(pCodecFactory->CreateEncoder(false));
				}
				stat = STAT_Format;
			} else {
				encoding.push_back(ch);
			}
		} else if (IsDigit(ch)) {
			nRepeat = 0;
			eatNextFlag = false;
			stat = STAT_Repeat;
		} else if (ch == '*') {
			if (pValue == valList.end()) {
				sig.SetError(ERR_ValueError, "not enough arguments");
				return false;
			}
			if (!pValue->IsNumber()) {
				sig.SetError(ERR_ValueError,
								"repeat specifier requires a number value");
				return false;
			}
			nRepeat = pValue->GetInt();
			pValue++;
		} else if (ch == '{') {
			encoding.clear();
			stat = STAT_Encoding;
		} else if (ch == '@') {
			bigEndianFlag = IsBigEndian();
		} else if (ch == '=') {
			bigEndianFlag = IsBigEndian();
		} else if (ch == '<') {
			bigEndianFlag = false;
		} else if (ch == '>') {
			bigEndianFlag = true;
		} else if (ch == '!') {
			bigEndianFlag = true;
		} else if (ch == 'x') {
			if (!PackForward(sig, offset, nRepeat)) return false;
			offset += nRepeat;
			nRepeat = 1;
		} else if (ch == 'c') {
			if (!PackForward(sig, offset, nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte++, pValue++) {
				if (!CheckString(sig, valList, pValue)) return false;
				*pByte = pValue->GetString()[0];
			}
			nRepeat = 1;
		} else if (ch == 'b') {
			if (!PackForward(sig, offset, nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte++, pValue++) {
				if (!CheckNumber(sig, valList, pValue, -128, 127)) return false;
				*pByte = pValue->GetChar();
			}
			nRepeat = 1;
		} else if (ch == 'B') {
			if (!PackForward(sig, offset, nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte++, pValue++) {
				if (!CheckNumber(sig, valList, pValue, 0, 255)) return false;
				*pByte = pValue->GetUChar();
			}
			nRepeat = 1;
		} else if (ch == 'h') {
			if (!PackForward(sig, offset, 2 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 2 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 2, pValue++) {
				if (!CheckNumber(sig, valList, pValue, -32768, 32767)) return false;
				unsigned short num = pValue->GetShort();
				PackUShort(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'H') {
			if (!PackForward(sig, offset, 2 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 2 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 2, pValue++) {
				if (!CheckNumber(sig, valList, pValue, 0, 65535)) return false;
				unsigned short num = pValue->GetUShort();
				PackUShort(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'i') {
			if (!PackForward(sig, offset, 4 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 4 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 4, pValue++) {
				if (!CheckNumber(sig, valList, pValue, -2147483648., 2147483647.)) return false;
				unsigned long num = pValue->GetInt();
				PackULong(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'I') {
			if (!PackForward(sig, offset, 4 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 4 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 4, pValue++) {
				if (!CheckNumber(sig, valList, pValue, 0, 4294967295.)) return false;
				unsigned long num = pValue->GetUInt();
				PackULong(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'l') {
			if (!PackForward(sig, offset, 4 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 4 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 4, pValue++) {
				if (!CheckNumber(sig, valList, pValue, -2147483648., 2147483647.)) return false;
				unsigned long num = pValue->GetLong();
				PackULong(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'L') {
			if (!PackForward(sig, offset, 4 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 4 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pByte += 4, pValue++) {
				if (!CheckNumber(sig, valList, pValue, 0, 4294967295.)) return false;
				unsigned long num = pValue->GetULong();
				PackULong(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'q') {
			if (!PackForward(sig, offset, 8 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 8 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pValue++) {
				if (!CheckNumber(sig, valList, pValue)) return false;
				int64 num = static_cast<int64>(pValue->GetNumber());
				PackUInt64(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'Q') {
			if (!PackForward(sig, offset, 8 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 8 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pValue++) {
				if (!CheckNumber(sig, valList, pValue)) return false;
				uint64 num = static_cast<uint64>(pValue->GetNumber());
				PackUInt64(pByte, bigEndianFlag, num);
			}
			nRepeat = 1;
		} else if (ch == 'f') {
			if (!PackForward(sig, offset, 4 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 4 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pValue++) {
				if (!CheckNumber(sig, valList, pValue)) return false;
				float num = static_cast<float>(pValue->GetNumber());
				unsigned char *buff = reinterpret_cast<unsigned char *>(&num);
				for (int j = 0; j < 4; j++, pByte++) *pByte = buff[j];
			}
			nRepeat = 1;
		} else if (ch == 'd') {
			if (!PackForward(sig, offset, 8 * nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += 8 * nRepeat;
			for (int i = 0; i < nRepeat; i++, pValue++) {
				if (!CheckNumber(sig, valList, pValue)) return false;
				double num = static_cast<double>(pValue->GetNumber());
				unsigned char *buff = reinterpret_cast<unsigned char *>(&num);
				for (int j = 0; j < 8; j++, pByte++) *pByte = buff[j];
			}
			nRepeat = 1;
		} else if (ch == 's') {
			if (!PackForward(sig, offset, nRepeat)) return false;
			Binary::iterator pByte = _binary.begin() + offset;
			offset += nRepeat;
			if (!CheckString(sig, valList, pValue)) return false;
			const char *p = pValue->GetString();
			int nPacked = 0;
			char chConv;
			for ( ; nPacked < nRepeat && *p != '\0'; p++) {
				Codec::Result result = pCodec->FeedChar(*p, chConv);
				if (result == Codec::RESULT_Error) {
					sig.SetError(ERR_CodecError,
						"encoding error. specify a proper coding name by {coding}");
					return false;
				} else if (result == Codec::RESULT_Complete) {
					*pByte++ = chConv, nPacked++;
					while (pCodec->FollowChar(chConv) && nPacked < nRepeat) {
						*pByte++ = chConv, nPacked++;
					}
				}
			}
			for ( ; nPacked < nRepeat; nPacked++, pByte++) {
				*pByte = '\0';
			}
			pValue++;
			nRepeat = 1;
		} else if (ch == 'p') {
			sig.SetError(ERR_ValueError, "sorry, not implemented yet");
			return false;
		} else if (ch == 'P') {
			sig.SetError(ERR_ValueError, "sorry, not implemented yet");
			return false;
		} else if (IsWhite(ch)) {
			// just ignore white characters
		} else {
			sig.SetError(ERR_ValueError, "invalid character in format");
			return false;
		}
		if (eatNextFlag) p++;
	}
	return true;
}

Value Object_Binary::Unpack(Signal sig,
				size_t &offset, const char *format, bool exceedErrorFlag)
{
	enum {
		STAT_Format,
		STAT_Repeat,
		STAT_Encoding,
	} stat = STAT_Format;
	Environment &env = *this;
	Value result;
	ValueList &valList = result.InitAsList(env);
	bool bigEndianFlag = IsBigEndian();
	int nRepeat = 1;
	String encoding;
	std::auto_ptr<Codec> pCodec(new Codec_None());
	for (const char *p = format; *p != '\0'; ) {
		char ch = *p;
		bool eatNextFlag = true;
		if (stat == STAT_Repeat) {
			if (IsDigit(ch)) {
				nRepeat = nRepeat * 10 + (ch - '0');
			} else {
				eatNextFlag = false;
				stat = STAT_Format;
			}
		} else if (stat == STAT_Encoding) {
			if (ch == '}') {
				if (encoding.empty()) {
					pCodec.reset(new Codec_None());
				} else {
					CodecFactory *pCodecFactory =
									CodecFactory::Lookup(encoding.c_str());
					if (pCodecFactory == NULL) {
						sig.SetError(ERR_CodecError,
								"unsupported encoding '%s'", encoding.c_str());
						return Value::Null;
					}
					pCodec.reset(pCodecFactory->CreateDecoder(false));
				}
				stat = STAT_Format;
			} else {
				encoding.push_back(ch);
			}
		} else if (IsDigit(ch)) {
			nRepeat = 0;
			eatNextFlag = false;
			stat = STAT_Repeat;
		} else if (ch == '{') {
			encoding.clear();
			stat = STAT_Encoding;
		} else if (ch == '@') {
			bigEndianFlag = IsBigEndian();
		} else if (ch == '=') {
			bigEndianFlag = IsBigEndian();
		} else if (ch == '<') {
			bigEndianFlag = false;
		} else if (ch == '>') {
			bigEndianFlag = true;
		} else if (ch == '!') {
			bigEndianFlag = true;
		} else if (ch == 'x') {
			if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			nRepeat = 1;
		} else if (ch == 'c') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			char str[2];
			str[1] = '\0';
			for (int i = 0; i < nRepeat; i++, pByte++) {
				str[0] = *pByte;
				valList.push_back(Value(env, str));
			}
			nRepeat = 1;
		} else if (ch == 'b') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte++) {
				char num = *pByte;
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'B') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte++) {
				unsigned char num = *pByte;
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'h') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 2 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 2) {
				short num = static_cast<short>(UnpackUShort(pByte, bigEndianFlag));
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'H') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 2 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 2) {
				unsigned short num = UnpackUShort(pByte, bigEndianFlag);
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'i') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 4) {
				int num = static_cast<int>(UnpackULong(pByte, bigEndianFlag));
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'I') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 4) {
				unsigned int num = static_cast<unsigned int>(UnpackULong(pByte, bigEndianFlag));
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'l') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 4) {
				long num = static_cast<long>(UnpackULong(pByte, bigEndianFlag));
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'L') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 4) {
				unsigned long num = static_cast<unsigned long>(UnpackULong(pByte, bigEndianFlag));
				valList.push_back(Value(num));
			}
			nRepeat = 1;
		} else if (ch == 'q') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 8 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 8) {
				int64 num = static_cast<int64>(UnpackUInt64(pByte, bigEndianFlag));
				valList.push_back(Value(static_cast<Number>(num)));
			}
			nRepeat = 1;
		} else if (ch == 'Q') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 8 * nRepeat, exceedErrorFlag)) return Value::Null;
			for (int i = 0; i < nRepeat; i++, pByte += 8) {
				uint64 num = static_cast<uint64>(UnpackUInt64(pByte, bigEndianFlag));
				valList.push_back(Value(static_cast<Number>(num)));
			}
			nRepeat = 1;
		} else if (ch == 'f') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			char buff[4];
			for (int i = 0; i < nRepeat; i++) {
				for (int j = 0; j < 4; j++, pByte++) buff[j] = *pByte;
				float num = *(reinterpret_cast<float *>(buff));
				valList.push_back(Value(static_cast<Number>(num)));
			}
			nRepeat = 1;
		} else if (ch == 'd') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, 8 * nRepeat, exceedErrorFlag)) return Value::Null;
			char buff[8];
			for (int i = 0; i < nRepeat; i++) {
				for (int j = 0; j < 8; j++, pByte++) buff[j] = *pByte;
				double num = *(reinterpret_cast<double *>(buff));
				valList.push_back(Value(static_cast<Number>(num)));
			}
			nRepeat = 1;
		} else if (ch == 's') {
			Binary::iterator pByte = _binary.begin() + offset;
			if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			String str;
			//str.reserve(nRepeat);
			char chConv;
			for (int nUnpacked = 0; nUnpacked < nRepeat; nUnpacked++, pByte++) {
				Codec::Result result = pCodec->FeedChar(*pByte, chConv);
				if (result == Codec::RESULT_Error) {
					sig.SetError(ERR_CodecError,
						"decoding error. specify a proper coding name by {coding}");
					return false;
				} else if (result == Codec::RESULT_Complete) {
					str.push_back(chConv);
					while (pCodec->FollowChar(chConv)) str.push_back(chConv);
				}
			}
			// flush unprocessed characters
			if (pCodec->Flush(chConv)) while (pCodec->FollowChar(chConv)) ;
			valList.push_back(Value(env, str.c_str()));
			nRepeat = 1;
		} else if (ch == 'p') {
			//Binary::iterator pByte = _binary.begin() + offset;
			//if (!UnpackForward(sig, offset, nRepeat, exceedErrorFlag)) return Value::Null;
			//for (int i = 0; i < nRepeat; i++, pByte++) {
			//}
			//nRepeat = 1;
			sig.SetError(ERR_ValueError, "sorry, not implemented yet");
			return Value::Null;
		} else if (ch == 'P') {
			//Binary::iterator pByte = _binary.begin() + offset;
			//if (!UnpackForward(sig, offset, 4 * nRepeat, exceedErrorFlag)) return Value::Null;
			//for (int i = 0; i < nRepeat; i++, pByte += 4) {
			//}
			//nRepeat = 1;
			sig.SetError(ERR_ValueError, "sorry, not implemented yet");
			return Value::Null;
		} else if (IsWhite(ch)) {
			// just ignore white characters
		} else {
			sig.SetError(ERR_ValueError, "invalid character in format");
			return Value::Null;
		}
		if (eatNextFlag) p++;
	}
	return result;
}

bool Object_Binary::PackForward(Signal sig, size_t offset, size_t size)
{
	if (offset + size > _binary.size()) {
		_binary.append(offset + size - _binary.size(), '\0');
	}
	return true;
}

void Object_Binary::PackUShort(Binary::iterator pByte,
									bool bigEndianFlag, unsigned short num)
{
	unsigned char byte0 = static_cast<unsigned char>(num >> 8);
	unsigned char byte1 = static_cast<unsigned char>(num >> 0);
	if (bigEndianFlag) {
		*(pByte + 0) = byte0;
		*(pByte + 1) = byte1;
	} else {
		*(pByte + 0) = byte1;
		*(pByte + 1) = byte0;
	}
}

void Object_Binary::PackULong(Binary::iterator pByte,
									bool bigEndianFlag, unsigned long num)
{
	unsigned char byte0 = static_cast<unsigned char>(num >> 24);
	unsigned char byte1 = static_cast<unsigned char>(num >> 16);
	unsigned char byte2 = static_cast<unsigned char>(num >> 8);
	unsigned char byte3 = static_cast<unsigned char>(num >> 0);
	if (bigEndianFlag) {
		*(pByte + 0) = byte0;
		*(pByte + 1) = byte1;
		*(pByte + 2) = byte2;
		*(pByte + 3) = byte3;
	} else {
		*(pByte + 0) = byte3;
		*(pByte + 1) = byte2;
		*(pByte + 2) = byte1;
		*(pByte + 3) = byte0;
	}
}

void Object_Binary::PackUInt64(Binary::iterator pByte,
									bool bigEndianFlag, uint64 num)
{
	unsigned char byte0 = static_cast<unsigned char>(num >> 56);
	unsigned char byte1 = static_cast<unsigned char>(num >> 48);
	unsigned char byte2 = static_cast<unsigned char>(num >> 40);
	unsigned char byte3 = static_cast<unsigned char>(num >> 32);
	unsigned char byte4 = static_cast<unsigned char>(num >> 24);
	unsigned char byte5 = static_cast<unsigned char>(num >> 16);
	unsigned char byte6 = static_cast<unsigned char>(num >> 8);
	unsigned char byte7 = static_cast<unsigned char>(num >> 0);
	if (bigEndianFlag) {
		*(pByte + 0) = byte0;
		*(pByte + 1) = byte1;
		*(pByte + 2) = byte2;
		*(pByte + 3) = byte3;
		*(pByte + 4) = byte4;
		*(pByte + 5) = byte5;
		*(pByte + 6) = byte6;
		*(pByte + 7) = byte7;
	} else {
		*(pByte + 0) = byte7;
		*(pByte + 1) = byte6;
		*(pByte + 2) = byte5;
		*(pByte + 3) = byte4;
		*(pByte + 4) = byte3;
		*(pByte + 5) = byte2;
		*(pByte + 6) = byte1;
		*(pByte + 7) = byte0;
	}
}

bool Object_Binary::UnpackForward(Signal sig,
							size_t &offset, int distance, bool exceedErrorFlag)
{
	if ((distance < 0 && offset < static_cast<size_t>(-distance)) ||
					(distance > 0 && offset + distance > _binary.size())) {
		if (exceedErrorFlag) {
			sig.SetError(ERR_IndexError, "pointer exceeds the range of binary");
		}
		return false;
	}
	offset += distance;
	return true;
}

unsigned short Object_Binary::UnpackUShort(Binary::iterator pByte, bool bigEndianFlag)
{
	unsigned char byte0 = static_cast<unsigned char>(*(pByte + 0));
	unsigned char byte1 = static_cast<unsigned char>(*(pByte + 1));
	if (bigEndianFlag) {
		return (static_cast<unsigned short>(byte0) << 8) + byte1;
	} else {
		return (static_cast<unsigned short>(byte1) << 8) + byte0;
	}
}

unsigned long Object_Binary::UnpackULong(Binary::iterator pByte, bool bigEndianFlag)
{
	unsigned char byte0 = static_cast<unsigned char>(*(pByte + 0));
	unsigned char byte1 = static_cast<unsigned char>(*(pByte + 1));
	unsigned char byte2 = static_cast<unsigned char>(*(pByte + 2));
	unsigned char byte3 = static_cast<unsigned char>(*(pByte + 3));
	if (bigEndianFlag) {
		return (static_cast<unsigned long>(byte0) << 24) +
				(static_cast<unsigned long>(byte1) << 16) +
				(static_cast<unsigned long>(byte2) << 8) +
				byte3;
	} else {
		return (static_cast<unsigned long>(byte3) << 24) +
				(static_cast<unsigned long>(byte2) << 16) +
				(static_cast<unsigned long>(byte1) << 8) +
				byte0;
	}
}

uint64 Object_Binary::UnpackUInt64(Binary::iterator pByte, bool bigEndianFlag)
{
	unsigned char byte0 = static_cast<unsigned char>(*(pByte + 0));
	unsigned char byte1 = static_cast<unsigned char>(*(pByte + 1));
	unsigned char byte2 = static_cast<unsigned char>(*(pByte + 2));
	unsigned char byte3 = static_cast<unsigned char>(*(pByte + 3));
	unsigned char byte4 = static_cast<unsigned char>(*(pByte + 4));
	unsigned char byte5 = static_cast<unsigned char>(*(pByte + 5));
	unsigned char byte6 = static_cast<unsigned char>(*(pByte + 6));
	unsigned char byte7 = static_cast<unsigned char>(*(pByte + 7));
	if (bigEndianFlag) {
		return (static_cast<uint64>(byte0) << 56) +
				(static_cast<uint64>(byte1) << 48) +
				(static_cast<uint64>(byte2) << 40) +
				(static_cast<uint64>(byte3) << 32) +
				(static_cast<uint64>(byte4) << 24) +
				(static_cast<uint64>(byte5) << 16) +
				(static_cast<uint64>(byte6) << 8) +
				byte7;
	} else {
		return (static_cast<uint64>(byte7) << 56) +
				(static_cast<uint64>(byte6) << 48) +
				(static_cast<uint64>(byte5) << 40) +
				(static_cast<uint64>(byte4) << 32) +
				(static_cast<uint64>(byte3) << 24) +
				(static_cast<uint64>(byte2) << 16) +
				(static_cast<uint64>(byte1) << 8) +
				byte0;
	}
}

bool Object_Binary::CheckString(Signal sig,
					const ValueList &valList, ValueList::const_iterator pValue)
{
	if (pValue == valList.end()) {
		sig.SetError(ERR_ValueError, "not enough arguments");
		return false;
	} else if (!pValue->IsString()) {
		sig.SetError(ERR_ValueError, "string value is expected");
		return false;
	}
	return true;
}

bool Object_Binary::CheckNumber(Signal sig,
					const ValueList &valList, ValueList::const_iterator pValue)
{
	if (pValue == valList.end()) {
		sig.SetError(ERR_ValueError, "not enough arguments");
		return false;
	} else if (!pValue->IsNumber()) {
		sig.SetError(ERR_ValueError, "number value is expected");
		return false;
	}
	return true;
}

bool Object_Binary::CheckNumber(Signal sig, const ValueList &valList,
					ValueList::const_iterator pValue, Number numMin, Number numMax)
{
	if (!CheckNumber(sig, valList, pValue)) return false;
	Number num = pValue->GetNumber();
	if (num < numMin || num > numMax) {
		sig.SetError(ERR_ValueError, "number is out of range");
		return false;
	}
	return true;
}

//-----------------------------------------------------------------------------
// Object_Binary::IteratorByte
//-----------------------------------------------------------------------------
Object_Binary::IteratorByte::IteratorByte(Object_Binary *pObj, int cntMax) :
			Iterator(false), _pObj(pObj), _cnt(cntMax), _cntMax(cntMax),
			_pCur(pObj->GetBinary().begin())
{
}

Object_Binary::IteratorByte::~IteratorByte()
{
	Object::Delete(_pObj);
}

bool Object_Binary::IteratorByte::DoNext(Environment &env, Signal sig, Value &value)
{
	const Binary &binary = _pObj->GetBinary();
	if (_pCur == binary.end() || _cnt == 0) return false;
	if (_cnt > 0) _cnt--;
	value = Value(static_cast<unsigned char>(*_pCur));
	_pCur++;
	return true;
}

String Object_Binary::IteratorByte::ToString(Signal sig) const
{
	return String("<iterator:binary>");
}

//-----------------------------------------------------------------------------
// Object_Binary::IteratorUnpack
//-----------------------------------------------------------------------------
Object_Binary::IteratorUnpack::IteratorUnpack(Object_Binary *pObj,
						const char *format, size_t offset, int cntMax) :
		Iterator(false), _pObj(pObj), _format(format),
		_offset(offset), _offsetInit(offset),
		_cntMax(cntMax), _cnt(cntMax)
{
}

Object_Binary::IteratorUnpack::~IteratorUnpack()
{
	Object::Delete(_pObj);
}

bool Object_Binary::IteratorUnpack::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_cnt == 0) return false;
	if (_cnt > 0) _cnt--;
	value = _pObj->Unpack(sig, _offset, _format.c_str(), false);
	return value.IsValid();
}

String Object_Binary::IteratorUnpack::ToString(Signal sig) const
{
	return String("<iterator:unpacks>");
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Binary
//-----------------------------------------------------------------------------
// binary#len()
AScript_DeclareMethod(Binary, len)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the length of the buffer in binary.");
}

AScript_ImplementMethod(Binary, len)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	return Value(static_cast<unsigned int>(pSelf->GetBinary().size()));
}

// binary#each() {block?}
AScript_DeclareMethod(Binary, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp("Returns an iterator picking up each byte in the buffer");
}

AScript_ImplementMethod(Binary, each)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	Object_Binary *pObj = Object_Binary::Reference(pSelf);
	Iterator *pIterator = new Object_Binary::IteratorByte(pObj, -1);
	return ReturnIterator(env, sig, args, pIterator);
}

// binary#pointer(offset:number => 0)
AScript_DeclareMethod(Binary, pointer)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "offset", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	SetHelp(
	"Returns a binaryptr instance that has an initial offset specified\n"
	"by the argument.");
}

AScript_ImplementMethod(Binary, pointer)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	Object_Binary *pObj = Object_Binary::Reference(pSelf);
	size_t offset = args.GetSizeT(0);
	Value result;
	result.InitAsBinaryPtr(env, pObj, offset);
	return result;
}

// binary#unpack(format:string, offset:number => 0)
AScript_DeclareMethod(Binary, unpack)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "offset", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
}

AScript_ImplementMethod(Binary, unpack)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	size_t offset = args.GetSizeT(1);
	return pSelf->Unpack(sig, offset, args.GetString(0), true);
}

// binary#unpacks(format:string, offset:number => 0, cnt?:number)
AScript_DeclareMethod(Binary, unpacks)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "offset", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "cnt", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Binary, unpacks)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	Object_Binary *pObj = Object_Binary::Reference(pSelf);
	const char *format = args.GetString(0);
	size_t offset = args.GetSizeT(1);
	int cntMax = args.IsNumber(2)? args.GetInt(2) : -1;
	Iterator *pIterator =
			new Object_Binary::IteratorUnpack(pObj, format, offset, cntMax);
	return ReturnIterator(env, sig, args, pIterator);
}

// binary#dump():void:[upper]
AScript_DeclareMethod(Binary, dump)
{
	SetMode(RSLTMODE_Void, FLAG_None);
	DeclareAttr(AScript_Symbol(upper));
}

AScript_ImplementMethod(Binary, dump)
{
	Stream *pConsole = env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	int iCol = 0;
	String strHex, strASCII;
	bool upperFlag = args.IsSet(AScript_Symbol(upper));
	foreach_const (Binary, p, pSelf->GetBinary()) {
		unsigned char ch = static_cast<unsigned char>(*p);
		char buff[8];
		if (upperFlag) {
			::sprintf(buff, (iCol > 0)? " %02X" : "%02X", ch);
		} else {
			::sprintf(buff, (iCol > 0)? " %02x" : "%02x", ch);
		}
		strHex += buff;
		strASCII += (0x20 <= ch && ch < 0x7f)? ch : '.';
		iCol++;
		if (iCol == 16) {
			String strLine = strHex;
			strLine += "  ";
			strLine += strASCII;
			pConsole->Println(sig, strLine.c_str());
			if (sig.IsSignalled()) return Value::Null;
			strHex.clear();
			strASCII.clear();
			iCol = 0;
		}
	}
	if (iCol > 0) {
		String strLine = strHex;
		for ( ; iCol < 16; iCol++) strLine += "   ";
		strLine += "  ";
		strLine += strASCII;
		pConsole->Println(sig, strLine.c_str());
		if (sig.IsSignalled()) return Value::Null;
	}
	return Value::Null;
}

// binary#add(buff+:binary)
AScript_DeclareMethod(Binary, add)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "buff", VTYPE_Binary, OCCUR_OnceOrMore);
}

AScript_ImplementMethod(Binary, add)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	foreach_const (ValueList, pValue, args.GetList(0)) {
		pSelf->GetBinary() += pValue->GetBinary();
	}
	return Value::Null;
}

// binary#store(offset:number, buff+:binary)
AScript_DeclareMethod(Binary, store)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "offset", VTYPE_Number);
	DeclareArg(env, "buff", VTYPE_Binary, OCCUR_OnceOrMore);
}

AScript_ImplementMethod(Binary, store)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	size_t offset = args.GetSizeT(0);
	Binary &binary = pSelf->GetBinary();
	if (offset > binary.size()) {
		binary.append(offset - binary.size(), '\0');
	}
	foreach_const (ValueList, pValue, args.GetList(1)) {
		size_t sizeEach = pValue->GetBinary().size();
		if (offset >= binary.size()) {
			binary += pValue->GetBinary();
		} else {
			binary.replace(offset, sizeEach, pValue->GetBinary());
		}
		offset += sizeEach;
	}
	return Value::Null;
}

// binary#stream()
AScript_DeclareMethod(Binary, stream)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Binary, stream)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	Object_Binary *pObjBinary = Object_Binary::Reference(pSelf);
	Object *pObj = new Object_Stream(env, new BinaryStream(sig, pObjBinary));
	return Value(pObj);
}

// binary#decode(codec:codec)
AScript_DeclareMethodPrimitive(Binary, decode)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "codec", VTYPE_Codec);
}

AScript_ImplementMethod(Binary, decode)
{
	Object_Binary *pSelf = Object_Binary::GetSelfObj(args);
	Object_Codec *pObjCodec = dynamic_cast<Object_Codec *>(args.GetObject(0));
	String str;
	if (!pObjCodec->GetDecoder()->Decode(sig, str, pSelf->GetBinary())) {
		return Value::Null;
	}
	return Value(env, str.c_str());
}

// Assignment
Class_Binary::Class_Binary(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Binary, len);
	AScript_AssignMethod(Binary, each);
	AScript_AssignMethod(Binary, pointer);
	AScript_AssignMethod(Binary, unpack);
	AScript_AssignMethod(Binary, unpacks);
	AScript_AssignMethod(Binary, dump);
	AScript_AssignMethod(Binary, add);
	AScript_AssignMethod(Binary, store);
	AScript_AssignMethod(Binary, stream);
	AScript_AssignMethod(Binary, decode);
}

bool Class_Binary::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsString()) {
		Object_Binary *pObjBinary = new Object_Binary(env, value.GetStringSTL());
		value = Value(pObjBinary);
		return true;
	}
	return false;
}

Object *Class_Binary::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return new Object_Binary((pClass == NULL)? this : pClass);
}

//-----------------------------------------------------------------------------
// Object_BinaryPtr
//-----------------------------------------------------------------------------
Object_BinaryPtr::~Object_BinaryPtr()
{
	Object::Delete(_pObjBinary);
}

Object *Object_BinaryPtr::Clone() const
{
	return new Object_BinaryPtr(*this);
}

String Object_BinaryPtr::ToString(Signal sig, bool exprFlag)
{
	char buff[64];
	::sprintf(buff, "<binaryptr:%d>", _offset);
	return String(buff);
}

bool Object_BinaryPtr::UnpackForward(Signal sig, int distance, bool exceedErrorFlag)
{
	return _pObjBinary->UnpackForward(sig, _offset, distance, exceedErrorFlag);
}

Value Object_BinaryPtr::Unpack(Signal sig,
					bool forwardFlag, const char *format, bool exceedErrorFlag)
{
	size_t offset = _offset;
	Value value = _pObjBinary->Unpack(sig, offset, format, exceedErrorFlag);
	if (forwardFlag) _offset = offset;
	return value;
}

bool Object_BinaryPtr::Pack(Signal sig,
					bool forwardFlag, const char *format, const ValueList &valList)
{
	size_t offset = _offset;
	if (!_pObjBinary->Pack(sig, offset, format, valList)) return false;
	if (forwardFlag) _offset = offset;
	return true;
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_BinaryPtr
//-----------------------------------------------------------------------------
// binaryptr#reset()
AScript_DeclareMethod(BinaryPtr, reset)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(BinaryPtr, reset)
{
	Object_BinaryPtr *pSelf = Object_BinaryPtr::GetSelfObj(args);
	pSelf->Reset();
	return Value::Null;
}

// binaryptr#pack(format:string, value+):reduce:[stay]
AScript_DeclareMethod(BinaryPtr, pack)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_OnceOrMore);
	DeclareAttr(AScript_Symbol(stay));
}

AScript_ImplementMethod(BinaryPtr, pack)
{
	Object_BinaryPtr *pSelf = Object_BinaryPtr::GetSelfObj(args);
	bool forwardFlag = !args.IsSet(AScript_Symbol(stay));
	pSelf->Pack(sig, forwardFlag, args.GetString(0), args.GetList(1));
	return args.GetSelf();
}

// binaryptr#unpack(format:string):[stay]
AScript_DeclareMethod(BinaryPtr, unpack)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareAttr(AScript_Symbol(stay));
}

AScript_ImplementMethod(BinaryPtr, unpack)
{
	Object_BinaryPtr *pSelf = Object_BinaryPtr::GetSelfObj(args);
	bool forwardFlag = !args.IsSet(AScript_Symbol(stay));
	return pSelf->Unpack(sig, forwardFlag, args.GetString(0), false);
}

// binaryptr#unpacks(format:string, cnt?:number)
AScript_DeclareMethod(BinaryPtr, unpacks)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "cnt", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(BinaryPtr, unpacks)
{
	Object_BinaryPtr *pSelf = Object_BinaryPtr::GetSelfObj(args);
	Object_Binary *pObj = Object_Binary::Reference(pSelf->GetBinaryObj());
	const char *format = args.GetString(0);
	int cntMax = args.IsNumber(1)? args.GetInt(1) : -1;
	Iterator *pIterator =
		new Object_Binary::IteratorUnpack(pObj, format, pSelf->GetOffset(), cntMax);
	return ReturnIterator(env, sig, args, pIterator);
}

// binaryptr#forward(distance:number):reduce
AScript_DeclareMethod(BinaryPtr, forward)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "distance", VTYPE_Number);
}

AScript_ImplementMethod(BinaryPtr, forward)
{
	Object_BinaryPtr *pSelf = Object_BinaryPtr::GetSelfObj(args);
	bool exeedErrorFlag = true;
	pSelf->UnpackForward(sig, args.GetInt(0), exeedErrorFlag);
	return args.GetSelf();
}

// Assignment
Class_BinaryPtr::Class_BinaryPtr(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(BinaryPtr, reset);
	AScript_AssignMethod(BinaryPtr, pack);
	AScript_AssignMethod(BinaryPtr, unpack);
	AScript_AssignMethod(BinaryPtr, unpacks);
	AScript_AssignMethod(BinaryPtr, forward);
}

Object *Class_BinaryPtr::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return NULL;
}

//-----------------------------------------------------------------------------
// BinaryStream
//-----------------------------------------------------------------------------
BinaryStream::BinaryStream(Signal sig, Object_Binary *pObjBinary) :
	Stream(sig, ATTR_Readable | ATTR_Writable), _pObjBinary(pObjBinary), _offset(0)
{
	//GetBinary().reserve(100000);
}

BinaryStream::~BinaryStream()
{
	Object::Delete(_pObjBinary);
}

const char *BinaryStream::GetName() const
{
	return "<binarystream>";
}

size_t BinaryStream::DoRead(Signal sig, void *buff, size_t len)
{
	const Binary &binary = GetBinary();
	if (_offset > binary.size()) {
		sig.SetError(ERR_IndexError, "out of range");
		return 0;
	}
	len = ChooseMin(binary.size() - _offset, len);
	::memcpy(buff, binary.data() + _offset, len);
	_offset += len;
	return len;
}

size_t BinaryStream::DoWrite(Signal sig, const void *buff, size_t len)
{
	Binary &binary = GetBinary();
	const char *buffp = reinterpret_cast<const char *>(buff);
	if (_offset < binary.size()) {
		size_t lenReplace = ChooseMin(binary.size() - _offset, len);
		binary.replace(binary.begin() + _offset, binary.begin() + _offset + lenReplace,
													buffp, len);
	} else {
		if (_offset > binary.size()) {
			binary.append(_offset - binary.size(), 0);
		}
		binary.append(buffp, len);
	}
	_offset += len;
	return len;
}

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

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

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

size_t BinaryStream::DoGetSize()
{
	return GetBinary().size();
}

}
