#include <assert.h>
#include "Buffer.h"

#if 0
#include "Debug.h"
#endif

#define POLYNOMIAL 0x04c11db7L

static bool bCrcTableGenerated = false;
static uint32_t crc_table[256];

using namespace Sexy;
using namespace std;

static const char* gWebEncodeMap = ".-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

static int gWebDecodeMap[256] = 
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 0, -1, 1, 0, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1
, -1, -1, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29
, 30, 31, 32, 33, 34, 35, 36, 37, -1, -1, -1, -1, -1, -1, 38, 39, 40, 41, 42, 43
, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};

//----------------------------------------------------------------------------
// Generate the table of CRC remainders for all possible bytes.
//----------------------------------------------------------------------------
static void GenerateCRCTable(void)
{
	bCrcTableGenerated = true;

	register int i, j;
	register uint32_t crc_accum;
	for (i = 0;  i < 256;  i++)
	{
		crc_accum = ((uint32_t) i << 24);
		for ( j = 0;  j < 8;  j++ )
		{
			if (crc_accum & 0x80000000L)
				crc_accum = (crc_accum << 1) ^ POLYNOMIAL;
			else
				crc_accum = (crc_accum << 1);
		}
		crc_table[i] = crc_accum;
	}
}

//----------------------------------------------------------------------------
// Update the CRC on the data block one byte at a time.
//----------------------------------------------------------------------------
static uint32_t UpdateCRC(uint32_t crc_accum,
						const char *data_blk_ptr,
						int data_blk_size)
{
	if (!bCrcTableGenerated)
		GenerateCRCTable();
	
	register int i, j;
	for (j = 0; j < data_blk_size; j++)
	{
		i = ((int) (crc_accum >> 24) ^ *data_blk_ptr++) & 0xff;
		crc_accum = (crc_accum << 8) ^ crc_table[i];
	}
	return crc_accum;
}

//----------------------------------------------------------------------------
// Stream UTF8 data in a const char* to keep receiving wchar_ts
//----------------------------------------------------------------------------
static int GetUTF8Char(const char** theBuffer, int theLen, wchar_t* theChar)
{
	static const unsigned short aMaskData[] = {
		0xC0,		// 1 extra byte
		0xE0,		// 2 extra bytes
		0xF0,		// 3 extra bytes
		0xF8,		// 4 extra bytes
		0xFC		// 5 extra bytes
	};

	if (theLen == 0) return 0;

	const char* aBuffer = *theBuffer;

	int aTempChar = int((unsigned char)*aBuffer++);
	if ((aTempChar & 0x80) != 0)
	{
		if ((aTempChar & 0xC0) != 0xC0) return 0; // sanity check: high bit should not be set without the next highest bit being set, too.

		int aBytesRead[6];
		int* aBytesReadPtr = &aBytesRead[0];

		*aBytesReadPtr++ = aTempChar;

		int aLen;
		for (aLen = 0; aLen < (int)(sizeof(aMaskData)/sizeof(*aMaskData)); ++aLen)
		{
			if ( (aTempChar & aMaskData[aLen]) == ((aMaskData[aLen] << 1) & aMaskData[aLen]) ) break;
		}
		if (aLen >= (int)(sizeof(aMaskData)/sizeof(*aMaskData))) return 0;

		aTempChar &= ~aMaskData[aLen];
		int aTotalLen = aLen+1;

		if (aTotalLen < 2 || aTotalLen > 6) return 0;

		int anExtraChar = 0;
		while (aLen > 0 && (aBuffer - *theBuffer) < theLen)
		{
			anExtraChar = int((unsigned char)*aBuffer++);
			if ((anExtraChar & 0xC0) != 0x80) return 0; // sanity check: high bit set, and next highest bit NOT set.

			*aBytesReadPtr++ = anExtraChar;

			aTempChar = (aTempChar << 6) | (anExtraChar & 0x3F);
			--aLen;
		}
		if (aLen > 0) return 0; // ran out of data before ending sequence

		// validate substrings
		bool valid = true;
		switch (aTotalLen)
		{
			case 2:
				valid = !((aBytesRead[0] & 0x3E) == 0);
				break;
			case 3:
				valid = !((aBytesRead[0] & 0x1F) == 0 && (aBytesRead[1] & 0x20) == 0);
				break;
			case 4:
				valid = !((aBytesRead[0] & 0x0F) == 0 && (aBytesRead[1] & 0x30) == 0);
				break;
			case 5:
				valid = !((aBytesRead[0] & 0x07) == 0 && (aBytesRead[1] & 0x38) == 0);
				break;
			case 6:
				valid = !((aBytesRead[0] & 0x03) == 0 && (aBytesRead[1] & 0x3C) == 0);
				break;
		}
		if (!valid) return 0;
	}

	int aConsumedCount = aBuffer - *theBuffer;
	
	if ( (aTempChar >= 0xD800 && aTempChar <= 0xDFFF) || (aTempChar >= 0xFFFE && aTempChar <= 0xFFFF) ) 
		return 0;

	*theChar = (wchar_t)aTempChar;

	*theBuffer = aBuffer;
	return aConsumedCount;
}

Buffer::Buffer()
{
	mDataBitSize = 0;
	mReadBitPos = 0;
	mWriteBitPos = 0;	
}

Buffer::~Buffer()
{
}

std::string Buffer::ToWebString() const
{
	std::string aString;
	int aSizeBits = mWriteBitPos;
	
	int anOldReadBitPos = mReadBitPos;
	mReadBitPos = 0;

	char aStr[256];
	sprintf(aStr, "%08X", aSizeBits);
	aString += aStr;

	int aNumChars = (aSizeBits + 5) / 6;
	for (int aCharNum = 0; aCharNum < aNumChars; aCharNum++)
		aString += gWebEncodeMap[ReadNumBits(6, false)];
	
	mReadBitPos = anOldReadBitPos;
	
	return aString;
}

std::wstring Buffer::UTF8ToWideString() const
{
	const char* aData = (const char*)GetDataPtr();
	int aLen = GetDataLen();

	bool firstChar = true;

	std::wstring aString;
	aString.reserve(aLen); // worst case
	while (aLen > 0)
	{
		wchar_t aChar;
		int aConsumed = GetUTF8Char(&aData, aLen, &aChar);
		if (aConsumed == 0) break;
		aLen -= aConsumed;

		if (firstChar)
		{
			firstChar = false;
			if (aChar == 0xFEFF) continue;
		}

		aString += aChar;
	}
	return aString;
}

void Buffer::FromWebString(const std::string& theString)
{
	Clear();

	if (theString.size() < 4)
		return;
	
	int aSizeBits = 0;

	for (int aDigitNum = 0; aDigitNum < 8; aDigitNum++)
	{
		char aChar = theString[aDigitNum];
		int aVal = 0;

		if ((aChar >= '0') && (aChar <= '9'))
			aVal = aChar - '0';
		else if ((aChar >= 'A') && (aChar <= 'F'))
			aVal = (aChar - 'A') + 10;
		else if ((aChar >= 'a') && (aChar <= 'f'))
			aVal = (aChar - 'f') + 10;

		aSizeBits += (aVal << ((7 - aDigitNum) * 4));
	}

	int aCharIdx = 8;
	int aNumBitsLeft = aSizeBits;
	while (aNumBitsLeft > 0)
	{
		uchar aChar = theString[aCharIdx++];
		int aVal = gWebDecodeMap[aChar];
		int aNumBits = min(aNumBitsLeft, 6);
		WriteNumBits(aVal, aNumBits);
		aNumBitsLeft -= aNumBits;		
	}

	SeekFront();
}

void Buffer::SeekFront() const
{
	mReadBitPos = 0;	
}

void Buffer::Clear()
{
	mReadBitPos = 0;
	mWriteBitPos = 0;
	mDataBitSize = 0;
	mData.clear();
}

void Buffer::WriteByte(uchar theByte)
{	
	if (mWriteBitPos % 8 == 0)
		mData.push_back((char) theByte);
	else
	{		
		int anOfs = mWriteBitPos  % 8;
		mData[mWriteBitPos /8] |= theByte << anOfs;
		mData.push_back((char) (theByte >> (8-anOfs)));		
	}

	mWriteBitPos += 8;
	if (mWriteBitPos > mDataBitSize)
		mDataBitSize = mWriteBitPos;
}

void Buffer::WriteNumBits(int theNum, int theBits)
{
	for (int aBitNum = 0; aBitNum < theBits; aBitNum++)
	{
		if (mWriteBitPos % 8 == 0)
			mData.push_back(0);
		if ((theNum & (1<<aBitNum)) != 0)
			mData[mWriteBitPos/8] |= 1 << (mWriteBitPos  % 8);
		mWriteBitPos++;
	}

	if (mWriteBitPos > mDataBitSize)
		mDataBitSize = mWriteBitPos;
}

int Buffer::GetBitsRequired(int theNum, bool isSigned)
{
	if (theNum < 0) // two's compliment stuff
		theNum = -theNum - 1;
	
	int aNumBits = 0;
	while (theNum >= 1<<aNumBits)
		aNumBits++;
		
	if (isSigned)
		aNumBits++;
		
	return aNumBits;
}

void Buffer::WriteBoolean(bool theBool)
{
	WriteByte(theBool ? 1 : 0);
}

void Buffer::WriteShort(short theShort)
{
	WriteByte((uchar)theShort);
	WriteByte((uchar)(theShort >> 8));
}

void Buffer::WriteLong(int32_t theLong)
{
	WriteByte((uchar)theLong);
	WriteByte((uchar)(theLong >> 8));
	WriteByte((uchar)(theLong >> 16));
	WriteByte((uchar)(theLong >> 24));
}

void Buffer::WriteString(const std::string& theString)
{
	WriteShort((short) theString.length());
	for (int i = 0; i < (int)theString.length(); i++)
		WriteByte(theString[i]);
}

void Buffer::WriteUTF8String(const std::wstring& theString)
{
	if ((mWriteBitPos & 7) != 0) // boo! let's get byte aligned.
		mWriteBitPos = (mWriteBitPos + 8) & ~7;

	WriteShort((short) theString.length());
	for (int i = 0; i < (int)theString.length(); ++i)
	{
		const unsigned int c = (unsigned int)theString[i]; // just in case wchar_t is only 16 bits, and it generally is in visual studio
		if (c < 0x80)
		{
			WriteByte((uchar)c);
		}
		else if (c < 0x800) 
		{
			WriteByte((uchar)(0xC0 | (c>>6)));
			WriteByte((uchar)(0x80 | (c & 0x3F)));
		}
		else if (c < 0x10000) 
		{
			WriteByte((uchar)(0xE0 | c>>12));
			WriteByte((uchar)(0x80 | ((c>>6) & 0x3F)));
			WriteByte((uchar)(0x80 | (c & 0x3F)));
		}
		else if (c < 0x110000) 
		{
			WriteByte((uchar)(0xF0 | (c>>18)));
			WriteByte((uchar)(0x80 | ((c>>12) & 0x3F)));
			WriteByte((uchar)(0x80 | ((c>>6) & 0x3F)));
			WriteByte((uchar)(0x80 | (c & 0x3F)));
		} // are the remaining ranges really necessary? add if so!
	}
}

void Buffer::WriteLine(const std::string& theString)
{
	WriteBytes((const uchar*) (theString + "\r\n").c_str(), (int) theString.length() + 2);
}

void Buffer::WriteBuffer(const ByteVector& theBuffer)
{
	WriteLong((short) theBuffer.size());
	for (int i = 0; i < (int)theBuffer.size(); i++)
		WriteByte(theBuffer[i]);
}

void Buffer::WriteBytes(const uchar* theByte, int theCount)
{
	for (int i = 0; i < theCount; i++)
		WriteByte(theByte[i]);
}

void Buffer::SetData(const ByteVector& theBuffer)
{
	mData = theBuffer;
	mDataBitSize = mData.size() * 8;
}

void Buffer::SetData(uchar* thePtr, int theCount)
{
	mData.clear();
	mData.insert(mData.begin(), thePtr, thePtr + theCount);
	mDataBitSize = mData.size() * 8;
}

uchar Buffer::ReadByte() const
{
	if ((mReadBitPos + 7)/8 >= (int)mData.size())
	{		
		return 0; // Underflow
	}

	if (mReadBitPos % 8 == 0)
	{
		uchar b = mData[mReadBitPos/8];
		mReadBitPos += 8;
		return b;
	}
	else
	{
		int anOfs = mReadBitPos % 8;
			
		uchar b = 0;
		
		b = mData[mReadBitPos/8] >> anOfs;
		b |= mData[(mReadBitPos/8)+1] << (8 - anOfs);
		
		mReadBitPos += 8;		
		
		return b;
	}
}

int Buffer::ReadNumBits(int theBits, bool isSigned) const
{	
	int aByteLength = (int) mData.size();

	int theNum = 0;
	bool bset = false;
	for (int aBitNum = 0; aBitNum < theBits; aBitNum++)
	{
		int aBytePos = mReadBitPos/8;

		if (aBytePos >= aByteLength)
			break;

        bset = (mData[aBytePos] & (1<<(mReadBitPos%8)));
		if (bset)			
			theNum |= 1<<aBitNum;
		
		mReadBitPos++;
	}
	
	if ((isSigned) && (bset)) // sign extend
		for (int aBitNum = theBits; aBitNum < 32; aBitNum++)
			theNum |= 1<<aBitNum;
	
	return theNum;
}

bool Buffer::ReadBoolean() const
{
	return ReadByte() != 0;
}

short Buffer::ReadShort() const
{
	short aShort = ReadByte();
	aShort |= ((short) ReadByte() << 8);
	return aShort;	
}

int32_t Buffer::ReadLong() const
{
	int32_t aLong = ReadByte();
	aLong |= ((int32_t) ReadByte()) << 8;
	aLong |= ((int32_t) ReadByte()) << 16;
	aLong |= ((int32_t) ReadByte()) << 24;

	return aLong;
}

std::string	Buffer::ReadString() const
{
	std::string aString;
	int aLen = ReadShort();

	for (int i = 0; i < aLen; i++)
		aString += (char) ReadByte();

	return aString;
}

std::wstring Buffer::ReadUTF8String() const
{
	if ((mReadBitPos & 7) != 0)
		mReadBitPos = (mReadBitPos + 8) & ~7; // byte align the read position

	std::wstring aString;
	int aLen = ReadShort();

	const char* aData = (const char*)(&mData[mReadBitPos/8]);
	int aDataSizeBytes = (mDataBitSize - mReadBitPos)/8;

	int i;
	for (i = 0; aDataSizeBytes > 0 && i < aLen; ++i)
	{
		wchar_t aChar;
		int aConsumed = GetUTF8Char(&aData, aDataSizeBytes, &aChar);
		if (aConsumed == 0) break;
		aDataSizeBytes -= aConsumed;

		aString += aChar;
	}
	assert(i == aLen); // if this fires, the UTF-8 data was malformed.

	return aString;
}


std::string Buffer::ReadLine() const
{
	std::string aString;

	for (;;)
	{
		char c = ReadByte();

		if ((c == 0) || (c == '\n'))
			break;

		if (c != '\r')
			aString += c;
	}

	return aString;
}

void Buffer::ReadBytes(uchar* theData, int theLen) const
{
	for (int i = 0; i < theLen; i++)
		theData[i] = ReadByte();
}

void Buffer::ReadBuffer(ByteVector* theByteVector) const
{
	theByteVector->clear();
	
	uint32_t aLength = ReadLong();
	theByteVector->resize(aLength);
	ReadBytes(&(*theByteVector)[0], aLength);
}

const uchar* Buffer::GetDataPtr() const
{
	if (mData.size() == 0)
		return NULL;
	return &mData[0];
}

int Buffer::GetDataLen() const
{
	return (mDataBitSize + 7) / 8; // Round up
}

int Buffer::GetDataLenBits() const
{
	return mDataBitSize;
}

uint32_t Buffer::GetCRC32(uint32_t theSeed) const
{	
	uint32_t aCRC = theSeed;
	aCRC = UpdateCRC(aCRC, (const char*) &mData[0], (int) mData.size());	
	return aCRC;
}

bool Buffer::AtEnd() const
{ 
	//return mReadBitPos >= (int)mData.size()*8;
	return mReadBitPos >= mDataBitSize;
}

bool Buffer::PastEnd() const
{
	return mReadBitPos > mDataBitSize;
}

