//////////////////////////////////////////////////////////////////////////////
//
// Asura_BitPacker.cpp
//
// Copyright (c) 2004 Rebellion. All rights reserved.
//
// Description:
//
//     class to pack arbitrary-bit-length data into a buffer.
//     based on Jon Blow's May02 Game Developer Mag code.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Core/Asura_BitPacker.h"

#include "Debugging/Asura_Assert.h"

#include <string.h>

Asura_BitPacker::Asura_BitPacker(u_int uBufferSizeInBytes)
{
	m_pucBuffer = new u_char[uBufferSizeInBytes];
	m_uBufferSize = uBufferSizeInBytes;
    Reset();
}


Asura_BitPacker::~Asura_BitPacker()
{
	delete[] m_pucBuffer;
}


void Asura_BitPacker::Reset( void )
{
    memset(m_pucBuffer, 0, m_uBufferSize * sizeof(u_char));
    m_uNextBitToWrite = 0;
	m_uBytesSkipped = 0;
}

// Fast version of pack()
const int BITS_PER_WORD = 32;
void Asura_BitPacker::Pack(u_int value, u_int num_bits_to_pack) 
{
	Asura_Assert(num_bits_to_pack <= 32, "too many bits");
	Asura_Assert( (num_bits_to_pack == 32) || ((value & ((1 << num_bits_to_pack) - 1)) == value), "value out of range");

    // Scoot the value bits up to the top of the word; this makes
    // them easier to work with.

    value <<= (BITS_PER_WORD - num_bits_to_pack);

    // First we do the hard part: pack bits into the first u_char,
    // which may already have bits in it.

    u_int byte_index = (m_uNextBitToWrite / 8);
    u_int bit_index = (m_uNextBitToWrite % 8);
    u_int empty_space_this_byte = (8 - bit_index) & 0x7;

    // Update m_uNextBitToWrite for the next call; we don't need 
    // the old value any more.

    m_uNextBitToWrite += num_bits_to_pack;

    u_char *dest = m_pucBuffer + byte_index;

    if (empty_space_this_byte) 
	{
        u_int to_copy = empty_space_this_byte;
//	    u_int align = 0;

	    if (to_copy > num_bits_to_pack) 
		{
	        // We don't have enough bits to fill up this u_char.
//	        align = to_copy - num_bits_to_pack;
	        to_copy = num_bits_to_pack;
	    }

	    u_int fill_bits = value >> (BITS_PER_WORD - empty_space_this_byte);
	    *dest |= fill_bits;

	    num_bits_to_pack -= to_copy;
	    dest++;
	    value <<= to_copy;
    }

    // Now we do the fast and easy part for what is hopefully
    // the bulk of the data.

    while (value) 
	{
        *dest++ = static_cast<u_char>(value >> (BITS_PER_WORD - 8));
	    value <<= 8;
    }
}

void Asura_BitPacker::GetResult(const u_char** ppucDataReturn, u_int *puLenReturn) const
{
    u_int uLenInBytes = (m_uNextBitToWrite + 7) / 8;
    *puLenReturn = uLenInBytes;

    Asura_Assert(*puLenReturn <= m_uBufferSize, "bit packer overflowed");

    *ppucDataReturn = m_pucBuffer;
}

const void* Asura_BitPacker::GetResult(u_int& uLenReturn) const
{
	u_int uLenInBytes = (m_uNextBitToWrite + 7) / 8;
	uLenReturn = uLenInBytes;

	Asura_Assert(uLenInBytes <= m_uBufferSize, "bit packer overflowed");
	return m_pucBuffer;
}

// SetByte sets specified byte to a specified value. This is to allow
// say, a header to be written after packing all the data in. Obviously
// any bytes you want to set must not be used for data, so you should 
// call e.g. SkipBytes(1) before packing any data to skip a byte to write to.
void Asura_BitPacker::SetByte(u_int uByte, u_char ucValue)
{
	Asura_Assert(uByte < m_uBytesSkipped, "out of range - did you call SkipBytes?");
	m_pucBuffer[uByte] = ucValue;
}

void Asura_BitPacker::SkipBytes(u_int uNumBytes)
{
	Asura_Assert(uNumBytes < m_uBufferSize, "can't skip more than buffersize bytes");
	Asura_Assert( m_uNextBitToWrite == 0, "can only skip bytes at the beginning");
	Asura_Assert( m_uBytesSkipped == 0, "can only skip bytes once");
	m_uNextBitToWrite = uNumBytes * 8;
	m_uBytesSkipped = uNumBytes;
}

// Slow version of Pack()
/*
void Asura_BitPacker::Pack(u_int value, u_int num_bits_to_pack) 
{
	Asura_Assert(num_bits_to_pack <= 32, "value too large");
	Asura_Assert((value & ((1 << num_bits_to_write) - 1)) == value, "value out of range");

	while (num_bits_to_write > 0) {
		u_int byte_index = (m_uNextBitToWrite / 8);
		u_int bit_index =  (m_uNextBitToWrite % 8);
		
		u_int src_mask = (1 << (num_bits_to_write - 1));
		u_char dest_mask = (1 << (7 - bit_index));
		
		if (value & src_mask) m_pucBuffer[byte_index] |= dest_mask;
		m_uNextBitToWrite++;
		num_bits_to_write--;
    }
}
*/


Asura_BitUnpacker::Asura_BitUnpacker(const void* pData, u_int uLen)
: m_pucBuffer(reinterpret_cast<const u_char*>(pData))
{
    m_uNumBitsRemaining	= uLen * 8;
    m_uNextBitToRead = 0;
}

u_int Asura_BitUnpacker::Unpack( u_int uNumBitsToUnpack )
{
	u_int uResult = 0;

    Asura_Assert(uNumBitsToUnpack <= 32, "invalid unpack amount requested");
    Asura_Assert(uNumBitsToUnpack <= m_uNumBitsRemaining, "invalid unpack amount requested");
	if (uNumBitsToUnpack > m_uNumBitsRemaining)
	{
		m_uNumBitsRemaining = 0;
		return 0;
	}

    m_uNumBitsRemaining -= uNumBitsToUnpack;

	while (uNumBitsToUnpack) 
	{
		u_int byte_index = (m_uNextBitToRead / 8);
		u_int bit_index = (m_uNextBitToRead % 8);
		
		u_int src_mask = (1 << (7 - bit_index));
		u_int dest_mask = (1 << (uNumBitsToUnpack - 1));

		if (m_pucBuffer[byte_index] & src_mask) uResult |= dest_mask;
		uNumBitsToUnpack--;
		m_uNextBitToRead++;
	}
    return uResult;
}

u_int Asura_BitUnpacker::Peek(u_int uNumBits)
{
	u_int uNumBitsRemainingBackup = m_uNumBitsRemaining;
	u_int uNextBitToReadBackup = m_uNextBitToRead;

	u_int uResult = Unpack(uNumBits);
	
	// restore the previous values
	m_uNumBitsRemaining = uNumBitsRemainingBackup;
	m_uNextBitToRead = uNextBitToReadBackup;

	return uResult;
}
