#include <ctype.h>
#include <stdlib.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <fcntl.h>

#include "mpg123.h"
#include "genre.h"
#include "common.h"

#include "id3_header.h"
#include <netinet/in.h>

const 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,} }
};

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

struct bitstream_info bsi;

static int fsizeold=0,ssize;
static unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
static unsigned char *bsbuf=bsspace[1],*bsbufold;
static int bsnum=0;

static u_int32_t oldhead = 0;
u_int32_t firsthead=0;

unsigned char *pcm_sample=NULL;
int pcm_point = 0;
int audiobufsize = AUDIOBUFSIZE;


static int decode_header(struct frame *fr,u_int32_t newhead);

int voll,volr;

void audio_flush(void)
{
int16_t *ptr=(int16_t *) pcm_sample;
int i=pcm_point;
int32_t j;

while (i>0) {
j=(*ptr)*voll;
*ptr=(int16_t) (j>>8);
ptr++;
i--;

j=(*ptr)*volr;
*ptr=(int16_t) (j>>8);
ptr++;
i--;
}



    //audio_play_samples (ai, pcm_sample, pcm_point);
   
    write(outfd,pcm_sample,pcm_point);
    pcm_point = 0;
}

void read_frame_init (void)
{
    oldhead = 0;
    firsthead = 0;
}

int head_check(u_int32_t head)
{
    if( (head & 0xffe00000) != 0xffe00000)
	return FALSE;
    if(!((head>>17)&3))
	return FALSE;
    if( ((head>>12)&0xf) == 0xf)
	return FALSE;
    if( ((head>>10)&0x3) == 0x3 )
	return FALSE;
    if ((head & 0xffff0000) == 0xfffe0000)
      return FALSE;

    return TRUE;
}


/*
 * Function read_id3v2_tag (head)
 *
 *    Read ID3v2 tag from stream.  Return TRUE upon success, or FALSE if
 *    an error occurred.
 *
 */
static int read_id3v2_tag(CFH c,unsigned long head)
{
	struct
	{
		char id3[3];
		struct id3_taghdr_t tag;
	} id3header;
	char *id3buf, *songname;
	int hdrsize;
	/*
	 * Read ID3tag header.
	 */
	*(unsigned long *) &id3header = htonl(head);
	if (c->read(c, ((char *) &id3header) + sizeof (head),
		     sizeof (id3header) - sizeof (head))
	    != sizeof (id3header) - sizeof (head))
		return FALSE;

	hdrsize = ID3_GET_SIZE28(ntohl(id3header.tag.th_size));

	/*
	 * A invalid header could fool us into requesting insane
	 * amounts of memory.  Make sure the header size is
	 * reasonable.
	 */
	if (hdrsize > c->size(c))

		return FALSE;

	c->seek(c,hdrsize,SEEK_CUR);

        return TRUE;
}



/*****************************************************************
 * read next frame
 */
int read_frame(CFH c,struct frame *fr)
{
  u_int32_t newhead;
  fsizeold=fr->framesize;       /* for Layer3 */

  if(!head_read(c,&newhead))
    return FALSE;


	if (!head_check(newhead) || !decode_header(fr, newhead))
	{
		int try = 0;

		do
		{
			try++;
			if ((newhead & 0xffffff00) == ('I' << 24) + ('D' << 16) + ('3' << 8))
			{
				read_id3v2_tag(c,newhead);
				if (!head_read(c,&newhead))
					return FALSE;
			}
			else if (!head_shift(c,&newhead))
				return 0;
			
		}
		while ((!head_check(newhead) ||
			!decode_header(fr,newhead)) &&
		       try < (256 * 1024));
		if(try >= (256 * 1024))
			return FALSE;
		
//FIXME:
//		mpg123_info->filesize -= try;
	}
	/* flip/init buffer for Layer 3 */
	bsbufold = bsbuf;
	bsbuf = bsspace[bsnum] + 512;
	bsnum = (bsnum + 1) & 1;

	if (!read_frame_body(c,bsbuf, fr->framesize))
		return 0;

	bsi.bitindex = 0;
	bsi.wordpointer = (unsigned char *) bsbuf;


	return 1;

}

/****************************************
 * HACK,HACK,HACK: step back <num> frames
 * can only work if the 'stream' isn't a real stream but a file
 */
int back_frame(CFH c,struct frame *fr,int num)
{
  int32_t bytes;
  u_int32_t newhead;
  
  if(!firsthead)
    return 0;
  
  bytes = (fr->framesize+8)*(num+2);
  
  if(back_bytes(c,bytes) < 0)
    return -1;
  if(head_read(c,&newhead))
    return -1;
  
  while( (newhead & HDRCMPMASK) != (firsthead & HDRCMPMASK) ) {
    if(!head_shift(c,&newhead))
      return -1;
  }
  
  if(back_bytes(c,4) <0)
    return -1;

  read_frame(c,fr);
  read_frame(c,fr);
  
  if(fr->lay == 3) {
    set_pointer(512);
  }
  
  return 0;
}


/*
 * decode a header and write the information
 * into the frame structure
 */
static int decode_header(struct frame *fr,u_int32_t newhead)
{
    if(!head_check(newhead))
      return 0;

    if( newhead & (1<<20) ) {
      fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
      fr->mpeg25 = 0;
    }
    else {
      fr->lsf = 1;
      fr->mpeg25 = 1;
    }
    
    if (!param.tryresync || !oldhead) {
          /* If "tryresync" is true, assume that certain
             parameters do not change within the stream! */
      fr->lay = 4-((newhead>>17)&3);
      if( ((newhead>>10)&0x3) == 0x3) {
        fprintf(stderr,"Stream error\n");
        screw_up();
	return 0;
      }
      if(fr->mpeg25) {
        fr->sampling_frequency = 6 + ((newhead>>10)&0x3);
      }
      else
        fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);
      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;

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

    oldhead = newhead;

    if(!fr->bitrate_index) {
      fprintf(stderr,"Free format not supported: (head %08lx)\n",(long) newhead);
      return (0);
    }

    switch(fr->lay) {
      case 1:
	fr->do_layer = do_layer1;
        fr->framesize  = (int32_t) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
        fr->framesize /= freqs[fr->sampling_frequency];
        fr->framesize  = ((fr->framesize+fr->padding)<<2)-4;
        break;
      case 2:
	fr->do_layer = do_layer2;
        fr->framesize = (int32_t) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
        fr->framesize /= freqs[fr->sampling_frequency];
        fr->framesize += fr->padding - 4;
        break;
      case 3:
        fr->do_layer = do_layer3;
        if(fr->lsf)
          ssize = (fr->stereo == 1) ? 9 : 17;
        else
          ssize = (fr->stereo == 1) ? 17 : 32;
        if(fr->error_protection)
          ssize += 2;
        fr->framesize  = (int32_t) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
        fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
        fr->framesize = fr->framesize + fr->padding - 4;
        break; 
      default:
        fprintf(stderr,"Sorry, unknown layer type.\n"); 
        return (0);
    }
    return 1;
}

void print_header(struct frame *fr)
{
	static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
	static char *layers[4] = { "Unknown" , "I", "II", "III" };

	fprintf(stderr,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF : %d\n", 
		fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
		layers[fr->lay],(long) freqs[fr->sampling_frequency],
		modes[fr->mode],fr->mode_ext,fr->framesize+4);
	fprintf(stderr,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n",
		fr->stereo,fr->copyright?"Yes":"No",
		fr->original?"Yes":"No",fr->error_protection?"Yes":"No",
		fr->emphasis);
	fprintf(stderr,"Bitrate: %d Kbits/s, Extension value: %d\n",
		tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension);
}

void print_header_compact(struct frame *fr)
{
	static char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" };
	static char *layers[4] = { "Unknown" , "I", "II", "III" };
 
	fprintf(stderr,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n",
		fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
		layers[fr->lay],
		tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
		(long) freqs[fr->sampling_frequency], modes[fr->mode]);
}

void print_id3_tag(unsigned char *buf)
{
	struct id3tag {
		char tag[3];
		char title[30];
		char artist[30];
		char album[30];
		char year[4];
		char comment[30];
		unsigned char genre;
	};
	struct id3tag *tag = (struct id3tag *) buf;
	char title[31]={0,};
	char artist[31]={0,};
	char album[31]={0,};
	char year[5]={0,};
	char comment[31]={0,};
	char genre[31]={0,};

	if(param.quiet)
		return;

	strncpy(title,tag->title,30);
	strncpy(artist,tag->artist,30);
	strncpy(album,tag->album,30);
	strncpy(year,tag->year,4);
	strncpy(comment,tag->comment,30);

	if ( tag->genre <= sizeof(genre_table)/sizeof(*genre_table) ) {
		strncpy(genre, genre_table[tag->genre], 30);
	} else {
		strncpy(genre,"Unknown",30);
	}
	
	fprintf(stderr,"Title  : %-30s  Artist: %s\n",title,artist);
	fprintf(stderr,"Album  : %-30s  Year  : %4s\n",album,year);
	fprintf(stderr,"Comment: %-30s  Genre : %s\n",comment,genre);
}

/*
 *   Split "path" into directory and filename components.
 *
 *   Return value is 0 if no directory was specified (i.e.
 *   "path" does not contain a '/'), OR if the directory
 *   is the same as on the previous call to this function.
 *
 *   Return value is 1 if a directory was specified AND it
 *   is different from the previous one (if any).
 */

int split_dir_file (const char *path, char **dname, char **fname)
{
	static char *lastdir = NULL;
	char *slashpos;

	if ((slashpos = strrchr(path, '/'))) {
		*fname = slashpos + 1;
		*dname = strdup(path); /* , 1 + slashpos - path); */
		if(!(*dname)) {
			perror("memory");
			screw_up();
			return 0;
		}
		(*dname)[1 + slashpos - path] = 0;
		if (lastdir && !strcmp(lastdir, *dname)) {
			/***   same as previous directory   ***/
			free (*dname);
			*dname = lastdir;
			return 0;
		}
		else {
			/***   different directory   ***/
			if (lastdir)
				free (lastdir);
			lastdir = *dname;
			return 1;
		}
	}
	else {
		/***   no directory specified   ***/
		if (lastdir) {
			free (lastdir);
			lastdir = NULL;
		};
		*dname = NULL;
		*fname = (char *)path;
		return 0;
	}
}

void set_pointer(int32_t backstep)
{
  bsi.wordpointer = bsbuf + ssize - backstep;
  if (backstep)
    memcpy(bsi.wordpointer,bsbufold+fsizeold-backstep,backstep);
  bsi.bitindex = 0; 
}

/********************************/

double compute_bpf(struct frame *fr)
{
	double bpf;

        switch(fr->lay) {
                case 1:
                        bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
                        bpf *= 12000.0 * 4.0;
                        bpf /= freqs[fr->sampling_frequency] <<(fr->lsf);
                        break;
                case 2:
                case 3:
                        bpf = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index];                        bpf *= 144000;
                        bpf /= freqs[fr->sampling_frequency] << (fr->lsf);
                        break;
                default:
                        bpf = 1.0;
        }

	return bpf;
}

double compute_tpf(struct frame *fr)
{
	static int bs[4] = { 0,384,1152,1152 };
	double tpf;

	tpf = (double) bs[fr->lay];
	tpf /= freqs[fr->sampling_frequency] << (fr->lsf);
	return tpf;
}

/*
 * Returns number of frames queued up in output buffer, i.e. 
 * offset between currently played and currently decoded frame.
 */

int32_t compute_buffer_offset(struct frame *fr)
{
	//FIXME
	
	return 0;
}

void print_stat(CFH c,struct frame *fr,int no,int32_t buffsize)
{
	double bpf,tpf,tim1,tim2;
	double dt = 0.0;
	int sno,rno;
	char outbuf[256];

	if(!c || !fr) 
		return;

	outbuf[0] = 0;

	bpf = compute_bpf(fr);
	tpf = compute_tpf(fr);

        rno = 0;
        sno = no;
	if(c->size(c) >= 0) {
          int32_t t = c->tell(c);
	  rno = (int)((double)(c->size(c)-t)/bpf);
          sno = (int)((double)t/bpf);
        }

	sprintf(outbuf+strlen(outbuf),"\rFrame# %5d [%5d], ",sno,rno);

	tim1 = sno*tpf-dt;
	tim2 = rno*tpf+dt;
	tim2 = tim2 < 0 ? 0.0 : tim2;

	sprintf(outbuf+strlen(outbuf),"Time: %02u:%02u.%02u [%02u:%02u.%02u], ",
			(unsigned int)tim1/60,
			(unsigned int)tim1%60,
			(unsigned int)(tim1*100)%100,
			(unsigned int)tim2/60,
			(unsigned int)tim2%60,
			(unsigned int)(tim2*100)%100);

	write(fileno(stderr),outbuf,strlen(outbuf));
}

int get_songlen(CFH c,struct frame *fr,int no)
{
	double tpf;
	
	if(!fr)
		return 0;
	
	if(no < 0) {
		if(!c || c->size(c) < 0)
			return 0;
		no = (double) c->size(c) / compute_bpf(fr);
	}

	tpf = compute_tpf(fr);
	return no*tpf;
}


int check_for_xing_header(CFH c,struct frame *fr, XHEADDATA * xhead)
{
        unsigned char *head_data;
        int ret;

        c->seek(c, -(fr->framesize + 4), SEEK_CUR);
        head_data = malloc(fr->framesize + 4);
        c->read(c,head_data, fr->framesize + 4);
        ret = mpg123_get_xing_header(xhead, head_data);
        free(head_data);
        return ret;

}


void jump_to_frame(CFH c,struct frame *fr, int frame)
{
        read_frame_init();
	printf("Seek to frame %d (byte %d)\n",
			frame,frame * (fr->framesize + 4));

        c->seek(c, frame * (fr->framesize + 4), SEEK_SET);
        read_frame(c,fr);
}

void jump_to_byte(CFH c,struct frame *fr, int byte)
{
	printf("Seek to byte %d\n",byte);
        c->seek(c, byte, SEEK_SET);
        read_frame(c,fr);
}


