/*
 *	StaticCodebook.java
 *
 *	This file is part of Tritonus: http://www.tritonus.org/
 */

/*
 *  Copyright (c) 2005 by Matthias Pfisterer
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
|<---            this code is formatted to fit into 80 columns             --->|
*/

package org.tritonus.lowlevel.pvorbis;

import java.util.ArrayList;
import java.util.List;

//import org.tritonus.lowlevel.pogg.Ogg;
import org.tritonus.lowlevel.pogg.Buffer;
import org.tritonus.share.TDebug;


/** Holds the static part of a codebook.
 */
public class StaticCodebook
implements VorbisConstants
{
	private int m_nDimensions;
	private int m_nEntries;
	private int[] m_anLengths;
	private int m_nMaptype;

	private int m_nQMin;
	private int m_nQDelta;
	private int m_nQQuant;
	private int m_nQSequencep;
	private int[] m_anQuants;


	public StaticCodebook()
	{
		if (TDebug.TraceVorbisNative) { TDebug.out("StaticCodebook.<init>(): begin"); }
		if (TDebug.TraceVorbisNative) { TDebug.out("StaticCodebook.<init>(): end"); }
	}


	/**
	 */
	public void clear()
	{
		m_nDimensions = 0;
		m_nEntries = 0;
		m_anLengths = null;
		m_nMaptype = 0;
		m_nQMin = 0;
		m_nQDelta = 0;
		m_nQQuant = 0;
		m_nQSequencep = 0;
		m_anQuants = null;
	}



	/** Initializes the comment object.
		Sets the vendor string to null and 
		removes all comments.
	 */
	public void init()
	{
// 		m_vendor = null;
// 		m_comments = new ArrayList<String>();
	}





	public int pack(Buffer buffer)
	{
// 		String strVendor = "tritonus.org pvorbis library";
// 		//String strVendor = "Xiph.Org libVorbis I 20030909";

// 		/* preamble */  
// 		buffer.write(0x03, 8);
// 		buffer.write("vorbis");

// 		/* vendor */
// 		buffer.writeWithLength(strVendor);
  
// 		/* comments */
// 		buffer.write(m_comments.size(), 32);
// 		if(m_comments.size() > 0)
// 		{
// 			for(int i = 0; i < m_comments.size(); i++)
// 			{
// 				buffer.writeWithLength(m_comments.get(i));
// 			}
// 		}
// 		buffer.write(1, 1);

		return 0;
	}


	public int unpack(Buffer buffer)
	{
		int nMagic = buffer.read(24);
		if (nMagic != 0x564342)
		{
			clear();
			return -1;
		}
		m_nDimensions = buffer.read(16);
		m_nEntries = buffer.read(24);
		if (m_nEntries == -1)
		{
			clear();
			return -1;
		}

		m_anLengths = new int[m_nEntries];

		/* are the lengths ordered or not? */
		if (! buffer.readFlag()) // unordered
		{
			/* sparse? */
			if (buffer.readFlag()) // yes, sparse
			{
				for (int i = 0; i < m_nEntries; i++)
				{
					if (buffer.readFlag()) // value present
					{
						int nLength = buffer.read(5);
						if (nLength == -1)
						{
							clear();
							return -1;
						}
						m_anLengths[i] = nLength + 1;
					}
					else
					{
						m_anLengths[i] = 0;
					}
				}
			}
			else // not sparse
			{
				for (int i = 0; i < m_nEntries; i++)
				{
					int nLength = buffer.read(5);
					if (nLength == -1)
					{
						clear();
						return -1;
					}
					m_anLengths[i] = nLength + 1;
				}
			}
		}
		else // lengths ordered
		{
			int nLength = buffer.read(5) + 1;
			for (int i = 0; i < m_nEntries;)
			{
				int nCount = buffer.read(ilog(m_nEntries - i));
				if (nCount == -1)
				{
					clear();
					return -1;
				}
				for (int j = 0; j < nCount; j++, i++)
				{
					m_anLengths[i] = nLength;
				}
				nLength++;
			}
		}

		m_nMaptype = buffer.read(4);

		if (m_nMaptype == 1 || m_nMaptype == 2)
		{
			m_nQMin = buffer.read(32);
			m_nQDelta = buffer.read(32);
			m_nQQuant = buffer.read(4) + 1;
			m_nQSequencep = buffer.read(1);
			int nQuantVals = 0;
			if (m_nMaptype == 1)
			{
				nQuantVals = calculateMaptype1Quantvals();
			}
			else if (m_nMaptype == 2)
			{
				nQuantVals = m_nEntries * m_nDimensions;
			}
			m_anQuants = new int[nQuantVals];
			for (int i = 0; i < nQuantVals; i++)
			{
				m_anQuants[i] = buffer.read(m_nQQuant);
			}
			if (nQuantVals != 0 && m_anQuants[nQuantVals - 1] == -1)
			{
				clear();
				return -1;
			}
		}
		else if (m_nMaptype > 2)
		{
			clear();
			return -1;
		}

		// everything ok.
		return 0;
	}


	/** Calculate the number of quants in a maptype 1 codebook.
	 */
	private int calculateMaptype1Quantvals()
	{
		int vals = (int) Math.floor(Math.pow((float) m_nEntries, 1.0f / m_nDimensions));

		/* the above *should* be reliable, but we'll not assume that FP is
		   ever reliable when bitstream sync is at stake; verify via integer
		   means that vals really is the greatest value of dim for which
		   vals^b->bim <= b->entries */
		/* treat the above as an initial guess */
		while (true)
		{
			int acc = 1;
			int acc1 = 1;
			int i;
			for (i = 0; i < m_nDimensions; i++)
			{
				acc *= vals;
				acc1 *= vals + 1;
			}
			if (acc <= m_nEntries && acc1 > m_nEntries)
			{
				return vals;
			}
			else
			{
				if (acc > m_nEntries)
				{
					vals--;
				}
				else
				{
					vals++;
				}
			}
		}
	}



	private static int ilog(int v)
	{
		int ret = 0;
		while (v != 0)
		{
			ret++;
			v >>= 1;
		}
		return ret;
	}
}



/*** StaticCodebook.java ***/
