/*
 * Copyright (c) 2011 Apple Inc. All rights reserved.
 * Portions Copyright (c) 2011-2015 Peter Pawlowski.
 *
 * @APPLE_APACHE_LICENSE_HEADER_START@
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * @APPLE_APACHE_LICENSE_HEADER_END@
 */

/*=============================================================================
    File:		ALACBitUtilities.c

	$NoKeywords: $
=============================================================================*/

#include <stdio.h>
#include "ALACBitUtilities.h"

// BitBufferInit
//
void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize )
{
	bits->cur		= buffer;
	bits->end		= bits->cur + byteSize;
	bits->bitIndex	= 0;
	bits->byteSize	= byteSize;
}

// BitBufferRemaining
//
uint32_t BitBufferRemaining( BitBuffer * bits ) {
	return (uint32_t)(bits->end - bits->cur) * 8 - bits->bitIndex;
}

// BitBufferRead
//
uint32_t BitBufferRead( BitBuffer * bits, uint8_t numBits )
{
	uint32_t		returnBits;
	
	//Assert( numBits <= 16 );

	returnBits = ((uint32_t)bits->cur[0] << 16) | ((uint32_t)bits->cur[1] << 8) | ((uint32_t)bits->cur[2]);
	returnBits = returnBits << bits->bitIndex;
	returnBits &= 0x00FFFFFF;
	
	bits->bitIndex += numBits;
	
	returnBits = returnBits >> (24 - numBits);
	
	bits->cur		+= (bits->bitIndex >> 3);
	bits->bitIndex	&= 7;
	
	//Assert( bits->cur <= bits->end );
	
	return returnBits;
}

// BitBufferReadSmall
//
// Reads up to 8 bits
uint8_t BitBufferReadSmall( BitBuffer * bits, uint8_t numBits )
{
	uint16_t		returnBits;
	
	//Assert( numBits <= 8 );
	
	returnBits = (bits->cur[0] << 8) | bits->cur[1];
	returnBits = returnBits << bits->bitIndex;
	
	bits->bitIndex += numBits;
	
	returnBits = returnBits >> (16 - numBits);
	
	bits->cur		+= (bits->bitIndex >> 3);
	bits->bitIndex	&= 7;
	
	//Assert( bits->cur <= bits->end );
	
	return (uint8_t)returnBits;
}

// BitBufferReadOne
//
// Reads one byte
uint8_t BitBufferReadOne( BitBuffer * bits )
{
	uint8_t		returnBits;

	returnBits = (bits->cur[0] >> (7 - bits->bitIndex)) & 1;

	bits->bitIndex++;
	
	bits->cur		+= (bits->bitIndex >> 3);
	bits->bitIndex	&= 7;
	
	//Assert( bits->cur <= bits->end );
	
	return returnBits;
}

// BitBufferPeek
//
uint32_t BitBufferPeek( BitBuffer * bits, uint8_t numBits )
{
	return ((((((uint32_t) bits->cur[0] << 16) | ((uint32_t) bits->cur[1] << 8) |
			((uint32_t) bits->cur[2])) << bits->bitIndex) & 0x00FFFFFF) >> (24 - numBits));
}

// BitBufferPeekOne
//
uint32_t BitBufferPeekOne( BitBuffer * bits )
{
	return ((bits->cur[0] >> (7 - bits->bitIndex)) & 1);
}

// BitBufferUnpackBERSize
//
uint32_t BitBufferUnpackBERSize( BitBuffer * bits )
{
	uint32_t		size;
	uint8_t		tmp;
	
	for ( size = 0, tmp = 0x80u; tmp &= 0x80u; size = (size << 7u) | (tmp & 0x7fu) )
		tmp = (uint8_t) BitBufferReadSmall( bits, 8 );
	
	return size;
}

// BitBufferGetPosition
//
uint32_t BitBufferGetPosition( BitBuffer * bits )
{
	uint8_t *		begin;
	
	begin = bits->end - bits->byteSize;
	
	return ((uint32_t)(bits->cur - begin) * 8) + bits->bitIndex;
}

// BitBufferByteAlign
//
void BitBufferByteAlign( BitBuffer * bits, int32_t addZeros )
{
	// align bit buffer to next byte boundary, writing zeros if requested
	if ( bits->bitIndex == 0 )
		return;

	if ( addZeros )
		BitBufferWrite( bits, 0, 8 - bits->bitIndex );
	else	
		BitBufferAdvance( bits, 8 - bits->bitIndex );	
}

// BitBufferAdvance
//
void BitBufferAdvance( BitBuffer * bits, uint32_t numBits )
{
	if ( numBits )
	{
		bits->bitIndex += numBits;
		bits->cur += (bits->bitIndex >> 3);
		bits->bitIndex &= 7;
	}
}

// BitBufferRewind
//
void BitBufferRewind( BitBuffer * bits, uint32_t numBits )
{
	uint32_t	numBytes;
	
	if ( numBits == 0 )
		return;
	
	if ( bits->bitIndex >= numBits )
	{
		bits->bitIndex -= numBits;
		return;
	}
	
	numBits -= bits->bitIndex;
	bits->bitIndex = 0;

	numBytes	= numBits / 8;
	numBits		= numBits % 8;
	
	bits->cur -= numBytes;
	
	if ( numBits > 0 )
	{
		bits->bitIndex = 8 - numBits;
		bits->cur--;
	}
	
	if ( bits->cur < (bits->end - bits->byteSize) )
	{
		//DebugCMsg("BitBufferRewind: Rewound too far.");

		bits->cur		= (bits->end - bits->byteSize);
		bits->bitIndex	= 0;
	}
}

// BitBufferWrite
//
void BitBufferWrite( BitBuffer * bits, uint32_t bitValues, uint32_t numBits )
{
	uint32_t				invBitIndex;
	
	RequireAction( bits != nil, return; );
	RequireActionSilent( numBits > 0, return; );

	invBitIndex = 8 - bits->bitIndex;

	while ( numBits > 0 )
	{
		uint32_t		tmp;
		uint8_t		shift;
		uint8_t		mask;
		uint32_t		curNum;

		curNum = MIN( invBitIndex, numBits );

		tmp = bitValues >> (numBits - curNum);

		shift  = (uint8_t)(invBitIndex - curNum);
		mask   = 0xffu >> (8 - curNum);		// must be done in two steps to avoid compiler sequencing ambiguity
		mask <<= shift;

		bits->cur[0] = (bits->cur[0] & ~mask) | (((uint8_t) tmp << shift)  & mask);
		numBits -= curNum;

		// increment to next byte if need be
		invBitIndex -= curNum;
		if ( invBitIndex == 0 )
		{
			invBitIndex = 8;
			bits->cur++;
		}
	}

	bits->bitIndex = 8 - invBitIndex;
}

void	BitBufferReset( BitBuffer * bits )
//void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize )
{
	bits->cur		= bits->end - bits->byteSize;
    bits->bitIndex	= 0;
}

#if PRAGMA_MARK
#pragma mark -
#endif
