#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "../../../include/v_lib.h"

/* 1 frame = 4608 byte PCM */

#include "mpg123.h"
#include "huffman.h"
#include "mp3.h"
#include "bswap.h"
    
#include "tabinit.c"

static int
mp3_read(MP3_context_p ctx,char *buf,int size)
{
	int len = ctx->audio_read(ctx,buf,size);
	if(len > 0)
		ctx->MP3_fpos += len;
	return len;
}

/*
 * Frame reader
 */

static int tabsel_123[2][3][16] = {
   { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
     {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
     {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },

   { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
     {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
     {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} }
};

static long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };

unsigned int
getbits(MP3_context_p ctx,short number_of_bits)
{
	unsigned long rval;
	if((ctx->bitsleft -= number_of_bits) < 0)
		return 0;
	if(!number_of_bits)
    	return 0;
	rval = ctx->wordpointer[0];
    rval <<= 8;
	rval |= ctx->wordpointer[1];
	rval <<= 8;
	rval |= ctx->wordpointer[2];
	rval <<= ctx->bitindex;
	rval &= 0xffffff;
	ctx->bitindex += number_of_bits;
	rval >>= (24 - number_of_bits);
	ctx->wordpointer += (ctx->bitindex >> 3);
	ctx->bitindex &= 7;
	return rval;
}

unsigned int
getbits_fast(MP3_context_p ctx,short number_of_bits)
{
	unsigned long rval;
	if((ctx->bitsleft -= number_of_bits) < 0)
    	return 0;
	if(!number_of_bits)
    	return 0;
	rval = ctx->wordpointer[0] << 8 | ctx->wordpointer[1];
	rval <<= ctx->bitindex;
	rval &= 0xffff;
	ctx->bitindex += number_of_bits;
	rval >>= (16 - number_of_bits);
	ctx->wordpointer += (ctx->bitindex >> 3);
	ctx->bitindex &= 7;
	return rval;
}

unsigned int
get1bit(MP3_context_p ctx)
{
	unsigned char rval;
	if((--ctx->bitsleft) < 0)
    	return 0;
	rval = *ctx->wordpointer << ctx->bitindex;
	ctx->bitindex++;
	ctx->wordpointer += (ctx->bitindex >> 3);
	ctx->bitindex &= 7;
	return ((rval >> 7) & 1);
}

void
set_pointer(MP3_context_p ctx,long backstep)
{
	ctx->wordpointer = ctx->bsbuf + ctx->ssize - backstep;
	if(backstep)
    	memCopy(ctx->wordpointer,
        	ctx->bsbufold + ctx->fsizeold - backstep,backstep);
	ctx->bitindex = 0;
	ctx->bitsleft += 8*backstep;
}

int
stream_head_read(MP3_context_p ctx,unsigned char *hbuf,unsigned long *newhead)
{
	if(mp3_read(ctx,hbuf,4) != 4)
    	return FALSE;
	*newhead = hbuf[0] << 24 | hbuf[1] << 16 | hbuf[2] <<  8 | hbuf[3];
	return TRUE;
}

int
stream_head_shift(MP3_context_p ctx,unsigned char *hbuf,unsigned long *head)
{
	*((unsigned long *)hbuf) >>= 8;
	if(mp3_read(ctx,hbuf+3,1) != 1)
    	return 0;
	*head <<= 8;
	*head |= hbuf[3];
	return 1;
}

/*
 * decode a header and write the information
 * into the frame structure
 */
int
decode_header(MP3_context_p ctx,struct frame *fr,unsigned long newhead)
{
	CALLER_NAME(MP3_decode_header)
    /* head_check: */
    if( (newhead & 0xffe00000) != 0xffe00000 ||  
        (newhead & 0x0000fc00) == 0x0000fc00) return FALSE;

    fr->lay = 4-((newhead>>17)&3);

    if( newhead & ((long)1<<20) ) {
      fr->lsf = (newhead & ((long)1<<19)) ? 0x0 : 0x1;
      fr->mpeg25 = 0;
    } else {
      fr->lsf = 1;
      fr->mpeg25 = 1;
    }

    if(fr->mpeg25)
      fr->sampling_frequency = 6 + ((newhead>>10)&0x3);
    else
      fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);

    if(fr->sampling_frequency>8) return FALSE;  /* valid: 0..8 */

    fr->error_protection = ((newhead>>16)&0x1)^0x1;
    fr->bitrate_index = ((newhead>>12)&0xf);
    fr->padding   = ((newhead>>9)&0x1);
    fr->extension = ((newhead>>8)&0x1);
    fr->mode      = ((newhead>>6)&0x3);
    fr->mode_ext  = ((newhead>>4)&0x3);
    fr->copyright = ((newhead>>3)&0x1);
    fr->original  = ((newhead>>2)&0x1);
    fr->emphasis  = newhead & 0x3;

    ctx->MP3_channels = fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;

    if(!fr->bitrate_index)
    {
	  ERROR_GENERIC("free format not supported");
      return FALSE;
    }

	switch(fr->lay){
	  case 2:
	    ctx->MP3_bitrate = tabsel_123[fr->lsf][1][fr->bitrate_index];
	    ctx->MP3_samplerate = freqs[fr->sampling_frequency];
	    fr->framesize = (long) ctx->MP3_bitrate * 144000;
	    fr->framesize /= ctx->MP3_samplerate;
	    ctx->MP3_framesize=fr->framesize;
	    fr->framesize += fr->padding - 4;
	    break;
	  case 3:
	    if(fr->lsf)
	      ctx->ssize = (fr->stereo == 1) ? 9 : 17;
	    else
	      ctx->ssize = (fr->stereo == 1) ? 17 : 32;
	    if(fr->error_protection) ctx->ssize += 2;
	
	    ctx->MP3_bitrate=tabsel_123[fr->lsf][2][fr->bitrate_index];
	    ctx->MP3_samplerate=freqs[fr->sampling_frequency];
	    fr->framesize  = (long) ctx->MP3_bitrate * 144000;
	    fr->framesize /= ctx->MP3_samplerate<<(fr->lsf);
	    ctx->MP3_framesize=fr->framesize;
	    fr->framesize += fr->padding - 4;
	    break;
	  default:
	    ERROR_GENERIC("unsupported layer type");
	    return 0;
	}
    if(fr->framesize<=0 || fr->framesize>MAXFRAMESIZE) return FALSE;

    return 1;
}

int
stream_read_frame_body(MP3_context_p ctx,int size)
{
  /* flip/init buffer for Layer 3 */
  ctx->bsbufold = ctx->bsbuf;
  ctx->bsbuf = ctx->bsspace[ctx->bsnum]+512;
  ctx->bsnum = (ctx->bsnum + 1) & 1;

  if( mp3_read(ctx,ctx->bsbuf,size) != size)
    return 0; /* broken frame */

  ctx->bitindex = 0;
  ctx->wordpointer = (unsigned char *) ctx->bsbuf;
  ctx->bitsleft = 8*size;

  return 1;
}


/*****************************************************************
 * read next frame     return number of frames read.
 */
int
read_frame(MP3_context_p ctx,struct frame *fr)
{
  unsigned long newhead;
  unsigned char hbuf[8];
  int skipped,resyncpos;
  int frames=0;

resync:
  skipped = ctx->MP3_fpos;
  resyncpos = ctx->MP3_fpos;

  set_pointer(ctx,512);
  ctx->fsizeold = fr->framesize;       /* for Layer3 */
  if(!stream_head_read(ctx,hbuf,&newhead)) return 0;
  if(!decode_header(ctx,fr,newhead))
  {
    /* invalid header! try to resync stream! */
retry1:
    while(!decode_header(ctx,fr,newhead))
    {
      if(!stream_head_shift(ctx,hbuf,&newhead)) return 0;
    }
    resyncpos = ctx->MP3_fpos - 4;
    /* found valid header */
    if(!stream_read_frame_body(ctx,fr->framesize)) return 0; /* read body */
    set_pointer(ctx,512);
    ctx->fsizeold = fr->framesize;       /* for Layer3 */
    if(!stream_head_read(ctx,hbuf,&newhead)) return 0;
    if(!decode_header(ctx,fr,newhead))
    {
      /* invalid hdr! go back... */
      if(!stream_head_read(ctx,hbuf,&newhead)) return 0;
      goto retry1;
    }
  }

  skipped = resyncpos - skipped;

  /* read main data into memory */
  if(!stream_read_frame_body(ctx,fr->framesize))
  {
    /* printf("\nBroken frame at 0x%X\n",resyncpos); */
    return 0;
  }
  ++frames;

  if(ctx->MP3_resync)
  {
    ctx->MP3_resync = 0;
    if(frames==1) goto resync;
  }

  return frames;
}

#include "layer2.c"
#include "layer3.c"

/******************************************************************************/
/*           PUBLIC FUNCTIONS                  */
/******************************************************************************/

static int tables_done_flag = 0;

MP3_context_p
MP3_CreateContext(int (*audio_read)(struct MP3_context_s *,char*,int))
{
	MP3_context_p ctx = (MP3_context_p)memAllocZ(sizeof(MP3_context_t));
    ctx->MP3_bps = 2;
	ctx->bsbufold = ctx->bsspace[0]+512;
	ctx->bsbuf = ctx->bsspace[1]+512;
	if(!tables_done_flag)
	{
		make_decode_tables();
		tables_done_flag = 1;
	}        
	ctx->audio_read = audio_read;
    ctx->fr.synth = synth_1to1;
    ctx->fr.synth_mono = synth_1to1_mono2stereo;
    ctx->fr.down_sample = 0;
    ctx->fr.down_sample_sblimit = SBLIMIT; /* >> (ctx->fr.down_sample); */
	ctx->synth_1to1_bo = 1;
    init_layer2();
    init_layer3(ctx->fr.down_sample_sblimit);
    return ctx;
}

/*
 * Read & decode a single frame. Called by sound driver.
 */

int MP3_DecodeFrame(MP3_context_p ctx,unsigned char *hova,short single)
{
   ctx->pcm_sample = hova;
   ctx->pcm_point = 0;
   if(!read_frame(ctx,&ctx->fr))
     return 0;
   if(single == -2)
   {
     set_pointer(ctx,512);
     return 1;
   }
   if(ctx->fr.error_protection)
     getbits(ctx,16); /* skip crc */
   ctx->fr.single = single;
   switch(ctx->fr.lay)
   {
     case 2: do_layer2(ctx,&ctx->fr,single); break;
     case 3: do_layer3(ctx,&ctx->fr,single); break;
   }
   return ctx->pcm_point ? ctx->pcm_point : 2;
}

void
MP3_Close(MP3_context_p ctx)
{
	memFree(ctx);
}
