/*
 * Copyright 2011-2019, European Organisation for the Exploitation of Meteorological Satellites (EUMETSAT)
 *
 * 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.
 */

#include "RMAErrorHandling.h"
#include "CBitBuffer.h"		// COMP\T4


void COMP::CBitBuffer::SetNextNBit
(
	unsigned __int64 i_N
)
{
	COMP_TRYTHIS
	Assert( i_N >= 0, Util::CParamException());
	Assert( m_bitIndex + i_N > sizeMaxBit(),COutOfBufferException());

	unsigned long byteIndex = m_bitIndex >> 3;   // bit position in byte

	// first, try to fill the remaining bits of the byte
	unsigned char filledbits = (unsigned char)(m_bitIndex & 7);  // bitindex % 8 = bits already used
	unsigned char bitstofill = 8 - filledbits;

	if ( i_N > bitstofill ) 
	{
		// fill the remaining bits of the byte
		unsigned char mask = (0xFF >> filledbits);
		get()[ byteIndex ] |= mask; //set the remaining bits
		i_N -= bitstofill;
		m_bitIndex += bitstofill;
		// now fill by entire bytes
		while (i_N >= 8)
		{
			byteIndex++;
			get()[ byteIndex ] = 0xFF;  // all bits = 1
			i_N -= 8;
			m_bitIndex += 8;
		}
		byteIndex++;
		// and terminate the fill;
		unsigned char negmask = (unsigned char)(0xFF >> i_N);
		get()[ byteIndex ] |= ~(negmask);
		m_bitIndex += (unsigned long)i_N;
	}
	else
	{
		// since i_N < bitstofill, revert to looping
		for(; i_N > 0 ; i_N --)
			SetNextBit();
	}
	COMP_CATCHTHIS
}




void COMP::CBitBuffer::ResetNextNBit
(
	unsigned __int64 i_N
)
{
	COMP_TRYTHIS
		Assert(i_N >= 0, Util::CParamException());
		Assert(m_bitIndex + i_N <= sizeMaxBit(), COMP::COutOfBufferException());

		unsigned long byteIndex = m_bitIndex >> 3;   // bit position in byte

		// first, try to fill the remaining bits of the byte
		unsigned char filledbits = (unsigned char)(m_bitIndex & 7);  // bitindex % 8 = bits already used
		unsigned char bitstofill = (unsigned char)(8 - filledbits);

		if ( i_N > bitstofill ) 
		{
			// fill the remaining bits of the byte
			unsigned char mask = (0xFF >> filledbits);
			get()[ byteIndex ] &= ~mask; // reset the bits
			i_N -= bitstofill;
			m_bitIndex += bitstofill;
			// fill by entire bytes
			while (i_N >= 8)
			{
				byteIndex++;
				get()[ byteIndex ] = 0; // all bits = 0
				i_N -= 8;
				m_bitIndex += 8;
			}
			byteIndex++;
			// now, terminate the fill;
			unsigned char negmask = (unsigned char)(0xFF >> i_N);
			get()[ byteIndex ] &= (negmask);
			m_bitIndex += (unsigned long)i_N;
		}
		else
		{
			// since i_N < bitstofill, revert to looping
			for(; i_N > 0 ; i_N --)
				ResetNextBit();
		}
	COMP_CATCHTHIS
}




void COMP::CBitBuffer::WriteLSb
(
	unsigned short	i_data,
	unsigned char	i_length
)
{
	COMP_TRYTHIS
		Assert(m_bitIndex + i_length < sizeMaxBit(), COMP::COutOfBufferException());
		unsigned long byteIndex = m_bitIndex / 8;

		unsigned short mask = 1 << (i_length - 1);
		while (mask !=0)
		{
			if ( i_data & mask)
				SetNextBit();
			else
				ResetNextBit();
			mask >>= 1;
		}
	COMP_CATCHTHIS
}




unsigned long COMP::CBitBuffer::CountNextSetRun
(
	unsigned long i_maxRun
)
{
	COMP_TRYTHIS
	Assert( i_maxRun > 0, Util::CParamException());
	// not sure this if is needed... 
	Assert(m_bitIndex + i_maxRun <= sizeMaxBit(), COMP::COutOfBufferException());

	unsigned long count = 0;  // Run length
	unsigned long byteIndex = m_bitIndex >> 3;  // bit position in byte

	unsigned char filledbits = (unsigned char)(m_bitIndex & 7);  // bitindex % 8 = bits already used
	unsigned char bitstofill = 8 - filledbits;  
	unsigned char mask = (0xFF >> filledbits);  // mask of the bits to consider
	unsigned char bits = get()[ byteIndex ];

	// Are we allowed to read that many bits && do all those bits == 1 ?
	if( (bitstofill <= i_maxRun) && ( (bits & mask)==mask) )
	{
		count      += bitstofill;
		m_bitIndex += bitstofill;
		byteIndex  ++;
		i_maxRun   -= bitstofill;

		// now, read by entire bytes as long as we find entire bytes
		// == 1
		while (( i_maxRun >= 8) && ( get()[byteIndex] == 0xFF))
		{
			count      += 8;
			m_bitIndex += 8;
			byteIndex  ++;
			i_maxRun   -= 8;
		}
		// get ready for manual finish
		filledbits =0;
		bits = get()[ byteIndex ];
	}
	// finish by hand
	mask = (0x80 >> filledbits);
	while ( (bits & mask) && ( i_maxRun > 0))
	{
		count      ++;
		m_bitIndex ++;
		i_maxRun   --;
		mask >>= 1;
	}
	return count;
	COMP_CATCHTHIS
}




unsigned long COMP::CBitBuffer::CountNextResetRun
(
	unsigned long i_maxRun
)
{
	COMP_TRYTHIS
	Assert( i_maxRun > 0, Util::CParamException());
	// not sure this if is needed... 
	Assert(m_bitIndex + i_maxRun <= sizeMaxBit(), COMP::COutOfBufferException());

	unsigned long count = 0;  // Run length
	unsigned long byteIndex = m_bitIndex >> 3;   // bit position in byte

	unsigned char filledbits = (unsigned char)(m_bitIndex & 7);  // bitindex % 8 = bits already used
	unsigned char bitstofill = 8 - filledbits;
	unsigned char mask = (0xFF >> filledbits);
	unsigned char bits = get()[ byteIndex ];

	if( (bitstofill <= i_maxRun) && !( bits & mask))
	{
		count      += bitstofill;
		m_bitIndex += bitstofill;
		byteIndex  ++;
		i_maxRun   -= bitstofill;

		while (( i_maxRun >= 8) && ( get()[byteIndex] == 0x00))
		{
			count      += 8;
			m_bitIndex += 8;
			byteIndex  ++;
			i_maxRun   -= 8;
		}
		filledbits =0;
		bits = get()[ byteIndex ];
	}
	// finish by hand
	mask = (0x80 >> filledbits);
	while ( !(bits & mask) && ( i_maxRun > 0))
	{
		count      ++;
		m_bitIndex ++;
		i_maxRun   --;
		mask >>= 1;
	}
	return count;
	COMP_CATCHTHIS
}
