/*
 * 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.
 */

//:Ignore
//////////////////////////////////////////////////////////////////////////
//
//	FileName:		CVLCCoder.cpp
//	
//	Date Created:	15/01/1999
//
//	Author:			Youssef Ouaghli
//
//	Description:	CVLCCoder class definition.
//
//	Last Modified:	$Dmae: 1999/08/26 15:41:07 $
//
//  RCS Id: $Id: CVLCCoder.cpp,v 1.45 1999/08/26 15:41:07 youag Exp $
//
//////////////////////////////////////////////////////////////////////////
//:End Ignore


#include "CVLCCoder.h"

namespace COMP 
{


void CVLCCoder::CodeQuadrantDC (CWBlock &i_Wblk,
								const unsigned int i_W, const unsigned int i_H,
								const unsigned int i_Quadrant)
{
	COMP_TRYTHIS
	// Get the number of bits needed to code the block coefs
	unsigned int nbBit = speed_csize (i_Wblk.GetQuadrantMaxCoef (0, 0, i_W, i_H));
	m_Ac.CodeBits (nbBit, m_NbBitNbBitCoef);
	if (nbBit)
	{
		m_Mod = m_Models[nbBit++];
		if (! m_Mod[0].IsInitialized ())
			for (unsigned int i=0 ; i<=nbBit ; i++)
				m_Mod[i].Initialize (nbBit + 1);

		int oldCoef = 1UL << (nbBit - 2);
		unsigned int context = nbBit;
		const int *p = i_Wblk.GetData()[0];
		const unsigned int w = i_Wblk.GetW();
		for (int i=i_H ; i>0 ; i--, p+=w)
		{
			for (unsigned int j=i_W ; j ; j--)
			{
				const int c = *p++;
				context = (context + CodeCoef (context, c - oldCoef)) >> 1;
				oldCoef = c;
			}
			if (--i)
			{
				p += w;
				for (unsigned int j=i_W ; j ; j--)
				{
					const int c = *--p;
					context = (context + CodeCoef (context, c - oldCoef)) >> 1;
					oldCoef = c;
				}
			}
		}
	}
	COMP_CATCHTHIS
}

void CVLCCoder::CodeQuadrant (CWBlock &i_Wblk,
							  const unsigned int i_X, const unsigned int i_Y,
							  const unsigned int i_W, const unsigned int i_H,
							  const unsigned int i_Level, const unsigned int i_Quadrant)
{
	COMP_TRYTHIS
	// Get the number of bits needed to code the block coefs
	unsigned int nbBit = speed_csize (i_Wblk.GetQuadrantMaxCoef (i_X, i_Y, i_W, i_H));
	m_Ac.CodeBits (nbBit, m_NbBitNbBitCoef);
	const unsigned int coefShift = i_Level >= m_NbLossyBitPlane
		? 0
		: m_NbLossyBitPlane - i_Level - (i_Quadrant > m_NbLossyQuadrant ? 1 : 0);
	if (nbBit > coefShift)
	{
		nbBit -= coefShift;
		m_Mod = m_Models[nbBit - 1];
		if (! m_Mod[0].IsInitialized ())
			for (unsigned int i=0 ; i<=nbBit ; i++)
				m_Mod[i].Initialize (nbBit + 1);

		unsigned int context = nbBit;
		const int *p = i_Wblk.GetData()[i_Y] + i_X;
		const unsigned int w = i_Wblk.GetW();
		if (! coefShift)
			for (int i=i_H ; i>0 ; i--, p+=w)
			{
				for (unsigned int j=i_W ; j ; j--)
					context = (context + CodeCoef (context, *p++)) >> 1;
				if (--i)
				{
					p += w;
					for (unsigned int j=i_W ; j ; j--)
						context = (context + CodeCoef (context, *--p)) >> 1;
				}
			}
		else
			for (int i=i_H ; i>0 ; i--, p+=w)
			{
				for (unsigned int j=i_W ; j ; j--)
				{
					int cV = *p++;
					cV = cV >= 0 ? cV >> coefShift : -(-cV >> coefShift);
					context = (context + CodeCoef (context, cV)) >> 1;
				}
				if (--i)
				{
					p += w;
					for (unsigned int j=i_W ; j ; j--)
					{
						int cV = *--p;
						cV = cV >= 0 ? cV >> coefShift : -(-cV >> coefShift);
						context = (context + CodeCoef (context, cV)) >> 1;
					}
				}
			}
	}
	COMP_CATCHTHIS
}

void CVLCCoder::Code (CWBlock &i_Wblk, const unsigned int i_NbIteWt, const unsigned int i_NbLossyBp)
{
	COMP_TRYTHIS
	// Get the number of bits needed to code the block coefs
	const unsigned int nbBit = speed_csize (i_Wblk.GetMaxCoef ());
	// To make sure CACCoder::CodeBits() will not fail
	Assert (nbBit <= (c_ACNbBits - 2), Util::CParamException());
	m_Ac.CodeBits (nbBit, 5);
	if (nbBit)
	{
		static const unsigned int bitPlanes[16] = {0, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4};
		static const unsigned int quadrants[16] = {0, 0, 0, 2, 3, 0, 2, 3, 5, 6, 0, 2, 3, 5, 6, 9};

		Assert (i_NbLossyBp < 16, Util::CParamException());
		unsigned int w = i_Wblk.GetW() >> i_NbIteWt;
		unsigned int h = i_Wblk.GetH() >> i_NbIteWt;
		// Check the block dimension against the number of wavelet transform iteration
		Assert ((w << i_NbIteWt) == i_Wblk.GetW() || (h << i_NbIteWt) == i_Wblk.GetH(), Util::CParamException());
		unsigned int m = i_NbIteWt;
		unsigned int q = i_NbIteWt * 3;
		m_NbBitCoef = nbBit;
		m_NbBitNbBitCoef = speed_csize (nbBit);
		m_NbIteWt = i_NbIteWt;
		m_NbLossyBitPlane = bitPlanes[i_NbLossyBp];
		m_NbLossyQuadrant = quadrants[i_NbLossyBp];

		CodeQuadrantDC (i_Wblk, w, h, q--);
		for (unsigned int k=0 ; k<i_NbIteWt ; k++, w<<=1, h<<=1, m--)
		{
			CodeQuadrant (i_Wblk, w, 0, w, h, m, q--);
			CodeQuadrant (i_Wblk, 0, h, w, h, m, q--);
			CodeQuadrant (i_Wblk, w, h, w, h, m - 1, q--);
		}
	}
	COMP_CATCHTHIS
}

} // namespace COMP
