/*
  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

 */



#ifndef __MPEGAUDIOSTREAM_H
#define __MPEGAUDIOSTREAM_H

#include "mpegAudioHeader.h"
#include "dxHead.h"
#include "../input/inputStream.h"

// we include this for the big_endian define

#include "mpegAudioBitWindow.h"

#define _MAX_MPEG_BUFFERSIZE    4096

typedef struct {
  char name   [30+1];
  char artist [30+1];
  char album  [30+1];
  char year   [ 4+1];
  char comment[30+1];
  unsigned char genre;
} ID3TAG;

/**
   Here we enhance an inputstream to an mpeg audio stream, on which
   then the mpeg audio decoder works.

   Things are complecated and this stream reflects that.

   We get data from an ordinary inputStream, then we must take
   care, that we really have an audio stream, which is done
   by the initialize routine.
   If this routine reports success, we have found an mpeg audio
   stream and can pass this class to the real decoder.

   The mpeg audio stream usually has to initialize another structure,
   the mpeg audio header info.
*/

class MpegAudioStream {


  // extension for xing vbr
  XHEADDATA* xHeadData;
  ID3TAG* id3;
  InputStream* input;
  int length;
  int lXingVBR;
  unsigned char header[4];

  char* buffer;
  int bitindex;


 public:
  MpegAudioStream(InputStream* input);
  ~MpegAudioStream();

  // returns true if init successfull
  int firstInitialize(MpegAudioHeader* mpegHeader);
  int nextHeader(MpegAudioHeader* mpegHeader);

  // used for seek
  long getSeekPosition(int second);

  // valid after initialize returns true
  int getLength();
  ID3TAG* getID3();


  inline int eof() { return input->eof(); }
  TimeStamp* getCurrentAudioTimeStamp(MpegAudioHeader* header);

  // Bit functions

  inline char* getBuffer()       { return buffer; }
  inline int   getBufferSize()   { return _MAX_MPEG_BUFFERSIZE ;}
  inline void resetBitIndex()    { bitindex=0; }
  inline int  getBitIndex()      { return bitindex; }
  inline void sync()             { bitindex=(bitindex+7)&0xFFFFFFF8; }
  inline int issync()            { return (bitindex&7);};

  /**
     Now follow ugly inline function. The performance gain is 1.5 %
     on a 400 MHz AMD
  */

  inline int getbyte()  {
    int r=(unsigned char)buffer[bitindex>>3];
    bitindex+=8;
    return r;
  }

  inline int getbits9(int bits) {
    register unsigned short a;
    {
      int offset=bitindex>>3;
      
      a=(((unsigned char)buffer[offset])<<8) | 
	((unsigned char)buffer[offset+1]);
    }
    
    a<<=(bitindex&7);
    bitindex+=bits;
    return (int)((unsigned int)(a>>(16-bits))); 
  }

  inline int getbits8() {
    register unsigned short a;
    
    {
      int offset=bitindex>>3;
      
      a=(((unsigned char)buffer[offset])<<8) | 
	((unsigned char)buffer[offset+1]);
    }
    
    a<<=(bitindex&7);
    bitindex+=8;
    return (int)((unsigned int)(a>>8));
  }

  inline int getbit() {
     register int r=(buffer[bitindex>>3]>>(7-(bitindex&7)))&1;

     bitindex++;
     return r;
  }

  inline int getbits(int bits) {
    union
    {
      char store[4];
      int current;
    }u;
    int bi;
    
    if(!bits)return 0;
    
    u.current=0;
    bi=(bitindex&7);
    u.store[_KEY]=buffer[bitindex>>3]<<bi;
    bi=8-bi;
    bitindex+=bi;
    
    while(bits) {
	if(!bi)  {
	    u.store[_KEY]=buffer[bitindex>>3];
	    bitindex+=8;
	    bi=8;
	}
	if(bits>=bi) {
	    u.current<<=bi;
	    bits-=bi;
	    bi=0;
	} else {
	    u.current<<=bits;
	    bi-=bits;
	    bits=0;
	}
    }
    bitindex-=bi;
    return (u.current>>8);
  }




 private:
  int readHeader(unsigned char* dest);
  int fillbuffer(MpegAudioHeader* mpegHeader);
  int getbytedirect();
  int calculateLength(MpegAudioHeader* mpegHeader,unsigned char* header);
  int parseXing(unsigned char* headerArray,char* frameBuffer,
		int size,XHEADDATA *header);

  void parseID3();

};


#endif
