/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************/

package org.xiph.libvorbis;

import static org.xiph.libvorbis.vorbis_constants.integer_constants.*;

public class codebook {

	int dim;				// long dim // codebook dimensions (elements per vector)
	int entries;			// long entries // codebook entries
	int used_entries;		// long used_entries // populated codebook entries
	static_codebook c;

	// for encode, the below are entry-ordered, fully populated
	// for decode, the below are ordered by bitreversed codeword and only used entries are populated

	float[] valuelist;		// list of dim*entries actual entry values
	int[] codelist;			// ogg_uint32_t *codelist // list of bitstream codewords for each entry

	int[] dec_index;		// only used if sparseness collapsed
	char[] dec_codelengths;	// chat *dec_codelengths
	int[] dec_firsttable;	// ogg_uint32_t *dec_firsttable
	int dec_firsttablen;
	int dec_maxlength;
	
	public codebook() {}

	public void vorbis_book_init_encode( static_codebook s ) {

		dim = s.dim;
		entries = s.entries;
		used_entries = s.entries;
		c = s;

		codelist = _make_words( s.lengthlist, s.entries, 0 );
		valuelist = _book_unquantize( s, s.entries, null );
	}

	private int[] _make_words( int[] l, int n, int sparsecount ) {

		int i,j,count=0;
		int[] marker;		// ogg_uint32_t marker[33];

		// ogg_uint32_t *r=_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r));
		int[] r;

		if ( sparsecount != 0 )
			r = new int[ sparsecount ];
		else
			r = new int[ n ];
  
		// memset(marker,0,sizeof(marker));
		marker = new int[ 33 ];	

		for ( i=0; i<n; i++ ) {

			int length=l[i];
			if ( length>0 ) {

				// ogg_uint32_t entry=marker[length];
				int entry = marker[length];
      
				// when we claim a node for an entry, we also claim the nodes below 
				// it (pruning off the imagined tree that may have dangled from it) 
				// as well as blocking the use of any nodes directly above for leaves
      
				// update ourself
				if ( length<32 && ((entry>>>length) != 0) ) {
					// error condition; the lengths must specify an overpopulated tree
					// _ogg_free(r);
					return null;
				}

				r[count++]=entry;
    
				// Look to see if the next shorter marker points to the node above. if so, update it and repeat

				for ( j=length; j>0; j-- ) {
	  
					if ( (marker[j]&1) != 0 ) {
						// have to jump branches
						if (j==1)
							marker[1]++;
						else
							marker[j]=marker[j-1]<<1;
						break;	// invariant says next upper marker would already
							// have been moved if it was on the same path
					}
					marker[j]++;
				}
    
      
				// prune the tree; the implicit invariant says all the longer markers were
				// dangling from our just-taken node.  Dangle them from our *new* node.

				for ( j=length+1; j<33; j++ ) {

					if ((marker[j]>>>1) == entry){
						entry=marker[j];
						marker[j]=marker[j-1]<<1;
					} else
						break;
				}
			} else
				if (sparsecount==0)
					count++;
		}
    
		// bitreverse the words because our bitwise packer/unpacker is LSb endian

		for ( i=0,count=0; i<n; i++ ) {

			// ogg_uint32_t temp=0;
			int temp = 0;
			for ( j=0; j<l[i]; j++ ) {
				temp<<=1;
				temp|=(r[count]>>>j)&1;
			}

			if (sparsecount != 0) {
				if (l[i] != 0)
					r[count++]=temp;
			} else
				r[count++]=temp;
		}

		return r;
	}

	private float ldexp( double value, long exp ) {
  
		return new Double( value * Math.pow( 2, new Long( exp ).intValue() ) ).floatValue();
	}

	private float _float32_unpack( int val ) {

		double mant=val&0x1fffff;
		int sign=val&0x80000000;
		long exp =(val&0x7fe00000L)>>>VQ_FMAN;

		if (sign != 0)
			mant= -mant;

		return ldexp( mant, exp-(VQ_FMAN-1)-VQ_FEXP_BIAS );
	}

	private float[] _book_unquantize( static_codebook b, int n, int[] sparsemap ) {

  		int j,k,count=0;

		if (b.maptype==1 || b.maptype==2) {

			int quantvals;
			float mindel = _float32_unpack(b.q_min);
			float delta = _float32_unpack(b.q_delta);
			// float *r=_ogg_calloc(n*b->dim,sizeof(*r));
			float[] r = new float[ n*b.dim ];

			// maptype 1 and 2 both use a quantized value vector, but different sizes

			switch ( b.maptype ) {

				case 1: {

					quantvals= b._book_maptype1_quantvals();
					for ( j=0; j<b.entries; j++ ) {
						if ( ( (sparsemap != null) && (b.lengthlist[j] != 0) ) || sparsemap == null ) {
							float last=0.f;
							int indexdiv=1;
							for ( k=0; k<b.dim; k++ ) {
								int index= (j/indexdiv)%quantvals;
								float val=b.quantlist[index];
								val = Math.abs(val)*delta+mindel+last;
								if (b.q_sequencep != 0)
									last=val;	  
								if (sparsemap != null)
									r[sparsemap[count]*b.dim+k]=val;
								else
									r[count*b.dim+k]=val;
								indexdiv*=quantvals;
							}
							count++;
						}

					}
					break;
				}

				case 2: {

					for ( j=0; j<b.entries; j++ ) {
						if ( ( (sparsemap != null) && (b.lengthlist[j] != 0) ) || sparsemap == null ) {
							float last=0.f;
							for ( k=0; k<b.dim; k++ ) {
								float val=b.quantlist[j*b.dim+k];
								val = Math.abs(val)*delta+mindel+last;
								if (b.q_sequencep != 0)
									last=val;	  
								if (sparsemap != null)
									r[sparsemap[count]*b.dim+k]=val;
								else
									r[count*b.dim+k]=val;
							}
							count++;
						}
					}
					break;
				}
			}

			return r;
  		}

  		return null;
	}
}