/*
  initializer/resyncer/length detection etc.. for mpeg audio
  Copyright (C) 2000  Martin Vogt

  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.

  For more information look at the file COPYRIGHT in this package

 */



#include "mpegAudioStream.h"


MpegAudioStream::MpegAudioStream(InputStream* input) {
  this->input=input;
  buffer=new char[_MAX_MPEG_BUFFERSIZE];
  xHeadData=new XHEADDATA();
  xHeadData->toc=new unsigned char[101];
  lXingVBR=false;
  id3=new ID3TAG();
  header[1]=0x0; // invalid sync start
}


MpegAudioStream::~MpegAudioStream() {
  delete (xHeadData->toc);
  delete xHeadData;
  delete id3;
  delete buffer;
}


int MpegAudioStream::getbytedirect() {
  unsigned char byte;
  if (input->read((char*)&byte,1) != 1) {
    return -1;
  }
  return (int)byte;
}

int MpegAudioStream::firstInitialize(MpegAudioHeader* mpegHeader) {
  if (nextHeader(mpegHeader) == false) {
    return false;
  }
  if (calculateLength(mpegHeader,(unsigned char*)&header) == false) {
    return false;
  }
  parseID3();

  return true;
}


int MpegAudioStream::nextHeader(MpegAudioHeader* mpegHeader) {
  header[0]=header[1];
  int val=getbytedirect();
  if (val == -1) {
    return false;
  }
  header[1]=val;
  if ((header[0]!=0xff) || ((header[1] & 0xf0)!=0xf0)) {
    header[0]=header[1];
    val=getbytedirect();
    if (val == -1) {
      return false;
    }
    header[1]=val;
    if ((header[0]!=0xff) || ((header[1] & 0xf0)!=0xf0)) {
      /*
	printf("Illegal Audio-MPEG-Header 0x%1x%1x at offset 0x%8x\n",
	header[0],header[1],
	(unsigned long)input->getBytePosition());
      */
      return false;
    }
  }
  if (readHeader((unsigned char*)&header)==false) {
    cout << "readHeader false"<<endl;
    return false;
  }
  if (mpegHeader->parseHeader((unsigned char*)&header)==false) {
    cout << "parseHeader false"<<endl;
    return false;
  }

  if (fillbuffer(mpegHeader)==false) {
    cout << "fillbuffer false"<<endl;
    return false;
  }
  return true;
}


int MpegAudioStream::fillbuffer(MpegAudioHeader* mpegHeaderInfo) {
  int framesize=mpegHeaderInfo->getFramesize();
  int maxSize=getBufferSize();
  int size=framesize-4;
  int back=false;
  resetBitIndex();
  if (size >= maxSize) {
    cout << "size:"<<size<<endl;
    return false;
  }
  if (size < 0) {
    cout << "size:"<<size<<endl;
    return false;
  }
  back=input->read(getBuffer(),size);
  if (back!=size) {
    perror("fillbuffer");
    return false;
  }
  if(mpegHeaderInfo->getProtection()==false)   {

    getbyte();        // CRC, Not check!!
    getbyte();
  }
  return true;

}



int MpegAudioStream::getLength() {
  return length;
}


ID3TAG* MpegAudioStream::getID3() {
  return NULL;
}

/**
   Tries to read a valid mpeg audio header
*/
int MpegAudioStream::readHeader(unsigned char* dest) {
  int val=getbytedirect();
  if (val==-1) {
    return false;
  }
  dest[2]=val;
  val=getbytedirect();
  if (val==-1) {
    return false;
  }
  dest[3]=val;
  return true;
}




long MpegAudioStream::getSeekPosition(int second) {
  float length=getLength();
  long pos=0;
  if (length<1.0) {
    return 0;
  }
  float percent=(float)second/length;
  float size=input->getByteLength();
  
  if (lXingVBR) {
    pos=SeekPoint(xHeadData->toc,(int)size,100.0*percent);
    return pos;
  }
  pos=(long)(percent*size);
  return pos;
}


int MpegAudioStream::calculateLength(MpegAudioHeader* mpegHeaderInfo,
				     unsigned char* header) {
  int totalframe=0;
  int framesize=mpegHeaderInfo->getFramesize();
  if (framesize > 0) {
    totalframe=(input->getByteLength()+framesize-1)/framesize;
    
    if (parseXing(header,getBuffer(),framesize-4,xHeadData)==true) {
      lXingVBR=true;
      totalframe=xHeadData->frames;
    }
  }
  
  float pcm=mpegHeaderInfo->getpcmperframe();
  float wavfilesize=(totalframe*pcm);
  float frequence=(float)mpegHeaderInfo->getFrequencyHz();
  length=0;
  if (frequence != 0) {
    length=(int)(wavfilesize/frequence);
  }
  return true;
}

int MpegAudioStream::parseXing(unsigned char* headerArray,char* frameBuffer,
			int size,XHEADDATA *header) {
  int back=false;
  if (size < 152) {
    return false;
  }
  unsigned char* rawFrame=new unsigned char[152+4];
  memcpy(rawFrame+4,frameBuffer,152);
  rawFrame[0]=headerArray[0];
  rawFrame[1]=headerArray[1];
  rawFrame[2]=headerArray[2];
  rawFrame[3]=headerArray[3];
  
  back=GetXingHeader(header,(unsigned char*)rawFrame);
  delete rawFrame;
  return back;
}



void MpegAudioStream::parseID3() {
  
  int tryflag=0;

  id3->name    [0]=0;
  id3->artist  [0]=0;
  id3->album   [0]=0;
  id3->year    [0]=0;
  id3->comment [0]=0;
  id3->genre      =0;
  
  int pos=input->getBytePosition();
  if (input->seek(input->getByteLength()-128)<0) {
    return;
  }

  for(;;) {
    if(getbytedirect()==0x54)
      if(getbytedirect()==0x41)
        if(getbytedirect()==0x47)
	{
	  input->read((char*)&id3->name    ,30);id3->name[30]=0;
	  input->read((char*)&id3->artist  ,30);id3->artist[30]=0;
	  input->read((char*)&id3->album   ,30);id3->album[30]=0;
	  input->read((char*)&id3->year    , 4);id3->year[4]=0;
	  input->read((char*)&id3->comment ,30);id3->comment[30]=0;
	  input->read((char*)&id3->genre ,1);
          break;
        }

    tryflag++;
    if(tryflag==1) {
      input->seek(input->getByteLength()-125); //for mpd 0.1,Sorry.
    }
    else break;
  }

  input->seek(pos);
}



TimeStamp* MpegAudioStream::getCurrentAudioTimeStamp(MpegAudioHeader* ) {
  long pos=input->getBytePosition();


  TimeStamp* timeStamp=input->getTimeStamp(pos); 
  return timeStamp;
}


