//
// Asura_UnitTest_BitPacker.cpp
//
// Created by TP - 03 July 2008
//
// Copyright (C) 2008 Rebellion,
//
// Description:
//			Unit tests for the Asura_BitPacker class.
//			This tests the packing and unpacking of the data.
// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_SUPPORT_UNITTEST

#include "Core/Asura_Core.h"
#include "Debugging/Asura_Assert.h"

#include "Core/Asura_BitPacker.h"

#include "UnitTest/Asura_UnitTest.h"

//////////////////////////////////////////////////////////////////////////////
// Most of the first lot of tests are to check that the packer packs the bits in correctly
// This doesn't actually matter all that much, it only matters that the unpacker
// gets the right result. If the implementation of either class changes these tests
// will become useless. The more important tests are in the second set.
//////////////////////////////////////////////////////////////////////////////

ASURA_UNITTEST(GeneralTests, BitPacker)
{
	Asura_BitPacker xBitPackerOneByte(1);
	Asura_BitPacker xBitPackerTwoBytes(2);
	const u_char* pucDataReturn = NULL;
	u_int uLenReturn;

	// constant used in tests
	const u_int uByteLength = 8;

	{
		// We will first check the GetResult()/Pack() functions,
		// as checking everything else assumes that these work
		// properly
		u_int uTestNum = 200;
		u_int uTestLength = 8;

		xBitPackerOneByte.Pack(uTestNum, uTestLength);
		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uTestNum << (uByteLength - uTestLength));
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));
	}
	{
		// To save us deleteing and recreating this BitPacker lets test the reset function now:
		xBitPackerOneByte.Reset();

		u_int uTestNum = 0;
		u_int uTestLength = 0;

		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uTestNum << (uByteLength - uTestLength));
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));
	}
	{
		// Now we have an empty 1 byte buffer again. Lets use a number that doesn't
		// take up a whole byte

		u_int uTestNum = 37;
		u_int uTestLength = 6;

		xBitPackerOneByte.Pack(uTestNum, uTestLength);
		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uTestNum << (uByteLength - uTestLength));
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));
	}
	xBitPackerOneByte.Reset();
	{
		// Lets try a number that goes over the byte size

		u_int uTestNum = 7654;
		u_int uTestLength = 13;

		xBitPackerTwoBytes.Pack(uTestNum, uTestLength);
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));

		u_int i = 0;

		// Lets go along the bytes checking the entry is correct
		while (i < uLenReturn)
		{
			// We want to shift the number so that it starts at the beginning of a byte.
			// This means we shift it by the total length minus the byte size.
			// After each check we shift the whole number up a byte length, so that the
			// next 8 bits of the number are in the correct place to be shifted.
			u_char ucTest = static_cast<u_char>(uTestNum >> (uTestLength - uByteLength));
			ASURA_UNITTEST_CHECK(*pucDataReturn == ucTest);
			pucDataReturn++;
			uTestNum <<= uByteLength;
			i++;
		}		
	}
	{
		// Just a quick check that Reset() works with a buffer of more then 1 byte
		xBitPackerTwoBytes.Reset();
		
		u_int uTestNum = 0;
		u_int uTestLength = 0;

		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uTestNum << (uByteLength - uTestLength));
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));
	}
	{
		// Now lets makes sure the length is no longer then it has to be

		u_int uTestNum = 200;
		u_int uTestLength = 8;

		xBitPackerTwoBytes.Pack(uTestNum, uTestLength);
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uTestNum << (uByteLength - uTestLength));
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength));		// Making sure that the returned length is less then the max buffer size if the data does not fill all the bytes
	}

	// Now we have checked that Pack() works (at least for single entries) will check the RoomForXXX functions
	xBitPackerTwoBytes.Reset();
	{
		// Lets check when the buffer is still empty
		ASURA_UNITTEST_CHECK(xBitPackerTwoBytes.RoomForBytes(2));
		ASURA_UNITTEST_CHECK(!xBitPackerTwoBytes.RoomForBytes(3));
		ASURA_UNITTEST_CHECK(xBitPackerTwoBytes.RoomForBits(2*uByteLength));
		ASURA_UNITTEST_CHECK(!xBitPackerTwoBytes.RoomForBits(2*uByteLength+1));

		// Now lets fill it up a bit and check
		u_int uTestNum = 21;
		u_int uTestLength = 5;
		xBitPackerTwoBytes.Pack(uTestNum,uTestLength);

		ASURA_UNITTEST_CHECK(xBitPackerTwoBytes.RoomForBytes((2*uByteLength-uTestLength)/uByteLength));
		ASURA_UNITTEST_CHECK(!xBitPackerTwoBytes.RoomForBytes((2*uByteLength-uTestLength)/uByteLength + 1));
		ASURA_UNITTEST_CHECK(xBitPackerTwoBytes.RoomForBits(2*uByteLength-uTestLength));
		ASURA_UNITTEST_CHECK(!xBitPackerTwoBytes.RoomForBits(2*uByteLength-uTestLength + 1));
	}

	xBitPackerTwoBytes.Reset();
	{
		// Now we need to test packing bools
		const int iSize = 3;
		bool bTests[iSize] = {1,0,0};

		int i = 0;

		while (i < iSize)
		{
			xBitPackerOneByte.PackBool(bTests[i]);
			i++;
		}

		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);

		u_int uResult = *pucDataReturn >> (uByteLength-iSize);
		
		i = iSize-1;

		while (i >= 0)
		{
			if(bTests[i])
			{
				ASURA_UNITTEST_CHECK((uResult & 1));
			}
			else
			{
				ASURA_UNITTEST_CHECK(!(uResult & 1));
			}
			uResult >>= 1;
			i--;
		}
	}
	xBitPackerOneByte.Reset();
	// Lets try multiple numbers before we check the unpacker
	{
		const int iSize = 4;
		u_int uTestNum[iSize] = {13,3,0,1};
		u_int uTestLength[iSize] = {4,2,1,1};

		int i = 0;

		while (i < iSize)
		{
			xBitPackerOneByte.Pack(uTestNum[i],uTestLength[i]);
			i++;
		}

		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);

		u_int uResult = *pucDataReturn;

		i = iSize-1;

		while ( i >= 0)
		{
			int iTemp = (1 << uTestLength[i]) - 1;
			ASURA_UNITTEST_CHECK((uResult & iTemp) == uTestNum[i]);
			uResult >>= uTestLength[i];
			i--;
		}
	}
	xBitPackerTwoBytes.Reset();
	// Now we just need to check the SkipByte and SetByte functions
	xBitPackerTwoBytes.Reset();
	{
		// Back to using variables rather thenhardcoding
		u_int uNumOfSkippedBytes = 1;
		u_char uSkippedByte = 130;
		u_int uTestInt = 128;
		u_int uTestLength = 8;

		xBitPackerTwoBytes.SkipBytes(uNumOfSkippedBytes);
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == 0);		// Nothing has been written yet
		ASURA_UNITTEST_CHECK(uLenReturn == uNumOfSkippedBytes);

		xBitPackerTwoBytes.Pack(uTestInt,uTestLength );						
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == 0);			// Nothing has been written to this byte yet
		ASURA_UNITTEST_CHECK(*(pucDataReturn+uNumOfSkippedBytes) == uTestInt);	
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength) + uNumOfSkippedBytes);				

		xBitPackerTwoBytes.SetByte(0, uSkippedByte);					
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);

		ASURA_UNITTEST_CHECK(*pucDataReturn == uSkippedByte);			
		ASURA_UNITTEST_CHECK(*(pucDataReturn+uNumOfSkippedBytes) == uTestInt);	
		ASURA_UNITTEST_CHECK(uLenReturn == ((uTestLength+7)/uByteLength) + uNumOfSkippedBytes);
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// We have now checked that the packer packs correctly, knowing this we can check to see if the unpacker unpacks correctly
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ASURA_UNITTEST(GeneralTests, BitUnpacker)
{
	// The Packers we are going to use with diffeent size buffers
	Asura_BitPacker xBitPackerOneByte(1);
	Asura_BitPacker xBitPackerTwoBytes(2);
	Asura_BitPacker xBitPackerTenBytes(10);

	// The variables we'll be using to test
	const u_char* pucDataReturn = NULL;
	u_int uLenReturn;
	u_int uUnpackedInt;
	u_int uPeekedInt;
	bool bUnpackedBool;
	u_int uNextBit;
	u_int uRemainingBits;

	// Constants we'll be using
	const u_int uByteLength = 8;
	const u_int uTwoByteLength = 16;

	// We will first check the functions when we only have one number being used
	{
		u_int uTestInt = 200;
		u_int uTestLength = 8;

		xBitPackerOneByte.Pack(uTestInt,uTestLength);
		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		uNextBit = xBitUnpacker.GetNextBitToRead();
		uRemainingBits = xBitUnpacker.GetNumBitsRemaining();

		// Nothing read yet
		ASURA_UNITTEST_CHECK(uNextBit == 0);		
		ASURA_UNITTEST_CHECK(uRemainingBits == uByteLength);	

		uPeekedInt = xBitUnpacker.Peek(uTestLength);
		uUnpackedInt = xBitUnpacker.Unpack(uTestLength);

		ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt);		
		ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt);	

		uNextBit = xBitUnpacker.GetNextBitToRead();
		uRemainingBits = xBitUnpacker.GetNumBitsRemaining();

		// nothing left to read, but there may be "spare" bits
		ASURA_UNITTEST_CHECK(uNextBit == uTestLength);		
		ASURA_UNITTEST_CHECK(uRemainingBits == uByteLength - uTestLength);	

		xBitPackerOneByte.Reset();
	}

	// Lets use a number that doesn't take up a whole byte
	{
		u_int uTestInt = 37;
		u_int uTestLength = 6;

		xBitPackerOneByte.Pack(uTestInt,uTestLength);
		xBitPackerOneByte.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		uPeekedInt = xBitUnpacker.Peek(uTestLength);
		uUnpackedInt = xBitUnpacker.Unpack(uTestLength);

		ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt);		
		ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt);	

		uNextBit = xBitUnpacker.GetNextBitToRead();
		uRemainingBits = xBitUnpacker.GetNumBitsRemaining();

		ASURA_UNITTEST_CHECK(uNextBit == uTestLength);		
		ASURA_UNITTEST_CHECK(uRemainingBits == uByteLength - uTestLength);	
	}

	// Ok lets move onto a larger buffer. We want to make sure we can unpack numbers larger then 1 byte
	{
		u_int uTestInt = 300;
		u_int uTestLength = 9;

		xBitPackerTwoBytes.Pack(uTestInt,uTestLength);
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		uNextBit = xBitUnpacker.GetNextBitToRead();
		uRemainingBits = xBitUnpacker.GetNumBitsRemaining();

		// nothing read yet
		ASURA_UNITTEST_CHECK(uNextBit == 0);		
		ASURA_UNITTEST_CHECK(uRemainingBits == uTwoByteLength);

		uPeekedInt = xBitUnpacker.Peek(uTestLength);
		uUnpackedInt = xBitUnpacker.Unpack(uTestLength);

		ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt);		
		ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt);

		uNextBit = xBitUnpacker.GetNextBitToRead();
		uRemainingBits = xBitUnpacker.GetNumBitsRemaining();

		// nothig left to read, but there may be "spare" bits
		ASURA_UNITTEST_CHECK(uNextBit == uTestLength);		
		ASURA_UNITTEST_CHECK(uRemainingBits == uTwoByteLength - uTestLength);	

		xBitPackerTwoBytes.Reset();
	}

	// Now we need to test unpacking bools
	{
		const int iSize = 3;
		bool bTestBools[iSize] = {1,0,1};

		int i = 0;

		while (i < iSize)
		{
			xBitPackerTwoBytes.PackBool(bTestBools[i]);
			i++;
		}

		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		i = 0;

		while (i < iSize)
		{
			uPeekedInt = xBitUnpacker.Peek(1);
			bUnpackedBool = xBitUnpacker.UnpackBool();
			if(bTestBools[i])
			{
				ASURA_UNITTEST_CHECK(uPeekedInt);
			}
			else
			{
				ASURA_UNITTEST_CHECK(!uPeekedInt);
			}
			ASURA_UNITTEST_CHECK(bTestBools[i] == bUnpackedBool);

			i++;
		}

		xBitPackerTwoBytes.Reset();
	}

	// Lets try multiple numbers
	{
		const int iSize = 4;
		u_int uTestInt[iSize] = {13,3,0,1};
		u_int uTestLength[iSize] = {4,2,1,1};

		int i = 0;

		while (i < iSize)
		{
			xBitPackerTwoBytes.Pack(uTestInt[i],uTestLength[i]);
			i++;
		}
			
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		i = 0;

		while ( i < iSize)
		{
			uPeekedInt = xBitUnpacker.Peek(uTestLength[i]);
			uUnpackedInt = xBitUnpacker.Unpack(uTestLength[i]);

			ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt[i]);		
			ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt[i]);
			
			i++;
		}
		
		xBitPackerTwoBytes.Reset();
	}
	{
		const int iSize = 2;
		u_int uTestInt[iSize] = {45,61};
		u_int uTestLength[iSize] = {6,6};

		int i = 0;

		while (i < iSize)
		{
			xBitPackerTwoBytes.Pack(uTestInt[i],uTestLength[i]);
			i++;
		}
			
		xBitPackerTwoBytes.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		i = 0;

		while ( i < iSize)
		{
			uPeekedInt = xBitUnpacker.Peek(uTestLength[i]);
			uUnpackedInt = xBitUnpacker.Unpack(uTestLength[i]);

			ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt[i]);		
			ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt[i]);

			i++;
		}
	}
	xBitPackerTwoBytes.Reset();
	{
		// A final test to make sure the packer and unpacker can handle a large (10 byte) buffer
		const int iSize = 12;
		u_int uTestInt[iSize] = {198,154,13,1,7,114,87,467,127,789,56,200};
		u_int uTestLength[iSize] = {8,8,4,1,3,7,7,9,7,10,6,8};

		int i = 0;

		while (i < 12)
		{
			xBitPackerTenBytes.Pack(uTestInt[i],uTestLength[i]);
			i++;
		}

		xBitPackerTenBytes.GetResult(&pucDataReturn,&uLenReturn);
		Asura_BitUnpacker xBitUnpacker(pucDataReturn,uLenReturn);

		i = 0;

		while ( i < 12)
		{
			uPeekedInt = xBitUnpacker.Peek(uTestLength[i]);
			uUnpackedInt = xBitUnpacker.Unpack(uTestLength[i]);

			ASURA_UNITTEST_CHECK(uPeekedInt == uTestInt[i]);		
			ASURA_UNITTEST_CHECK(uUnpackedInt == uTestInt[i]);

			i++;
		}
		xBitPackerTenBytes.Reset();
	}
}

#endif // #ifdef ASURA_SUPPORT_UNITTEST
