/**********************************************************************
MPEG-4 Audio VM

This software module was originally developed by
  Y.B. Thomas Kim and S.H. Park (Samsung AIT)
and edited by
  Y.B. Thomas Kim (Samsung AIT) on 1997-11-06

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation. Copyright is not released for non MPEG-2
NBC/MPEG-4 Audio conforming products. The original developer retains
full right to use the code for his/her own purpose, assign or donate
the code to a third party and to inhibit third party from using the
code for non MPEG-2 NBC/MPEG-4 Audio conforming products. This
copyright notice must be included in all copies or derivative works.

Copyright (c) 1997.

**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "sam_encode.h"

static int			index2cband[2][1024];

#ifdef __cplusplus
extern "C" {
#endif

static int get_region_info(int groupInfo[], int s[], int e[]);
static int my_log2(int value);

#ifdef __cplusplus
}
#endif

int sam_encode_data(
		int bitrate,
		int common_window,
		int	windowSequence[],
		int scaleFactors[][MAX_SCFAC_BANDS],
		int groupInfo[][8],
		int quant[][1024],
		int maxSfb[],
		int swb_offset[][MAX_SCFAC_BANDS],
		int nr_of_sfs,
		int	stereo_mode,
		int	stereo_info[],
		int pns_sfb_start,
		int pns_sfb_flag[][SFB_NUM_MAX],
		int pns_sfb_nrg[][SFB_NUM_MAX],
		int pns_sfb_mode[],
		int block_size_samples,
		int	ubits,
		int abits,
		int i_ch,
		int nch,
		int w_flag)
{
	int		i, j, k, ch;
	int		b, w;
	int		qband, cband;
	int		frame_length;
	int		band_snf[2][200];
	int		nr_of_sfb;
	int		max;
	int		end_cband;
	int		start_i, end_i;
	int		num_window_group[2], s[2][8], e[2][8];
	int		which_model[2][200];
	int		scf[2][MAX_SCFAC_BANDS];
	int		sample[2][1024];


	for(ch = i_ch; ch < nch; ch++) {

		for (i = 0; i < 200; i++)
			band_snf[ch][i] = 0;

		if(windowSequence[ch] == EIGHT_SHORT_SEQUENCE) {
			int short_block_size=block_size_samples/8;
			int	tmpspl[1024];
			int	tempband[1024];
			int	swb_offset_s[20];
			int width;

			num_window_group[ch] = get_region_info(groupInfo[ch]+1, s[ch], e[ch]);

			for(i = 0;i < 1024; i++) {
				tempband[i] = -1;
				index2cband[ch][i] = -1;
			}

			nr_of_sfb = nr_of_sfs / num_window_group[ch];

			for(i = 0; i < nr_of_sfb; i++)
				swb_offset_s[i] = swb_offset[ch][i] / e[ch][0];
			swb_offset_s[nr_of_sfb] = short_block_size;

			b = 0;
			swb_offset[ch][b++] = 0;
			for(i = 0; i < nr_of_sfb; i++) {
				for(w = 0; w < num_window_group[ch]; w++, b++) {
					width = (swb_offset_s[i+1] - swb_offset_s[i]) * (e[ch][w]-s[ch][w]);
					swb_offset[ch][b] = swb_offset[ch][b-1] + width;
				}
			}

			/* reshuffling */
			k = 0;
			for(w = 0; w < num_window_group[ch]; w++)
				for(qband = 0; qband < nr_of_sfb; qband++)
					for(b = s[ch][w]; b < e[ch][w]; b++)
   					for (i=swb_offset_s[qband]; i<swb_offset_s[qband+1]; i++)
						tmpspl[128*b+i] = quant[ch][k++];

    		for(qband = 0; qband < maxSfb[ch]; qband++)  {
    			for (i=swb_offset_s[qband]; i<swb_offset_s[qband+1]; i+=4) 
       			for(w=0; w<num_window_group[ch]; w++)  {
					cband = i*(e[ch][w]-s[ch][w])/32;
					max = 0;
					for (b=s[ch][w]; b<e[ch][w]; b++) {
						for (k=0; k<4; k++) {
							if (abs(tmpspl[short_block_size*b+i+k])>max) 
								max = abs(tmpspl[short_block_size*b+i+k]);
							tempband[short_block_size*b+i+k] = 24*w+cband;
						}
					}
					if (max > 0) max = my_log2(max);
					if(max > band_snf[ch][24*w+cband])
						band_snf[ch][24*w+cband] = max;
    			}
			}

			j = 0;
			for(i = 0; i < swb_offset_s[maxSfb[ch]]; i+=4) {
				for(b = 0; b < 8; b++) {
					for(k = 0; k < 4; k++, j++) {
						sample[ch][j] = tmpspl[short_block_size*b+i+k];
						index2cband[ch][j] = tempband[128*b+i+k];
					}
				}
			}

		} else {

			nr_of_sfb = nr_of_sfs;
			num_window_group[ch] = 1;
			for (i = 0; i < block_size_samples; i++)
				sample[ch][i] = quant[ch][i];

			end_cband = (swb_offset[ch][maxSfb[ch]]+31)/32;
			for (cband = 0; cband < end_cband; cband++) {
				start_i = cband * 32;
				end_i =   (cband+1) * 32;
				max = 0;
				for (i = start_i; i < end_i; i++) {
					if (abs(sample[ch][i])>max) 
						max = abs(sample[ch][i]);
				}
				band_snf[ch][cband] = 0;
				if(max > 0) band_snf[ch][cband] = my_log2(max);
			}
		}

		j = 0;
		for (w = 0; w < num_window_group[ch]; w++) {
			for (i = 0; i < maxSfb[ch]; i++) {
				scf[ch][j++] = scaleFactors[ch][(w*nr_of_sfb)+i];
			}
		}

		for (i = 0; i < 200; i++) 
			which_model[ch][i] = 0;
	}


	/********** B I T S T R E A M   M A K I N G *********/
	frame_length = sam_encode_bsac(bitrate, common_window, windowSequence, sample,
			band_snf, scf, maxSfb, num_window_group, swb_offset, which_model,
			stereo_mode, stereo_info, pns_sfb_start, pns_sfb_flag, pns_sfb_nrg,
			pns_sfb_mode, block_size_samples, ubits, abits, i_ch, nch, w_flag);


	return frame_length;
}

int
sam_i2cb(int ch, int i)
{
	return index2cband[ch][i];
}

static int get_region_info(int groupInfo[], int s[], int e[])
{
	int reg, b;

	reg = 0;
	s[0] = 0;
	for (b=0; b<7; b++) {
		if (groupInfo[b]==0) {
			s[reg+1] = e[reg] = b+1;
			reg++;
		}
	}
	e[reg] = 8;

	return (reg + 1);
}

static int my_log2(int value)
{
	int	i, step;

	if(value < 0) {
		fprintf(stderr, "my_log2:error : %d\n", value);
		return 0;
	}
	if(value == 0)	return 0;

	step = 2;
	for(i = 1; i < 24; i++) {
		if(value < step) return i;
		step *= 2;
	}
	return ( 1 + (int)(log10((double)value)/log10(2.0)));
}
