#include <windows.h>
#include "header_mpg.h"
#include <bfc/platform/types.h>

static int frameratecode2framerate[16] = {
  0,
  // Official mpeg1/2 framerates:
  24000*10000/1001, 24*10000,25*10000, 30000*10000/1001, 30*10000,50*10000,60000*10000/1001, 60*10000,
  // libmpeg3's "Unofficial economy rates":
  1*10000,5*10000,10*10000,12*10000,15*10000,0,0
}; 

static const int gaSampleRate[3][4] = 
{
  {44100, 48000, 32000, 0}, // MPEG-1
  {22050, 24000, 16000, 0}, // MPEG-2
  {11025, 12000,  8000, 0}, // MPEG-2.5
};

static const int gaBitrate[2][3][15] =
{
  {
  // MPEG-1
  {  0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, // Layer 1
  {  0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, // Layer 2
  {  0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320}, // Layer 3
  },

  {
  // MPEG-2, MPEG-2.5
  {  0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, // Layer 1
  {  0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, // Layer 2
  {  0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, // Layer 3
  },
};

#define MPEG_SYNCWORD 0x7ff
#define MPEG1          0
#define MPEG2          1
#define MPEG25         2
#define MODE_MONO      3

HeaderMpg::HeaderMpg() : fh(INVALID_HANDLE_VALUE)
{
  memset(buf, 0, sizeof(buf));
  pbuf = end = 0;
  m_BitrateNdx = m_SampleRateNdx = m_Layer = m_Id = m_Mode =
  m_Idex = video_bitrate = audio_bitrate = 0;
}

int HeaderMpg::mp3headerFromInt(unsigned long dwHdrBits)
{
  // header fields
  int m_Syncword;
  /*int m_CrcCheck;
  int m_Padding;
  int m_Private;
  int m_ModeExt;
  int m_Copyright;
  int m_Original;
  int m_Emphasis;*/

  // calculated data
  int   m_HeaderValid;

  // read header fields
  m_Syncword      =     (dwHdrBits >> 21) & 0x000007ff;
  m_Idex          =     (dwHdrBits >> 20) & 0x00000001;
  m_Id            =     (dwHdrBits >> 19) & 0x00000001;
  m_Layer         = 4 -((dwHdrBits >> 17) & 0x00000003);
  //m_CrcCheck      =   !((dwHdrBits >> 16) & 0x00000001);
  m_BitrateNdx    =     (dwHdrBits >> 12) & 0x0000000f;
  m_SampleRateNdx =     (dwHdrBits >> 10) & 0x00000003;
  /*m_Padding       =     (dwHdrBits >>  9) & 0x00000001;
  m_Private       =     (dwHdrBits >>  8) & 0x00000001;*/
  m_Mode          =     (dwHdrBits >>  6) & 0x00000003;
  /*m_ModeExt       =     (dwHdrBits >>  4) & 0x00000003;
  m_Copyright     =     (dwHdrBits >>  3) & 0x00000001;
  m_Original      =     (dwHdrBits >>  2) & 0x00000001;
  m_Emphasis      =     (dwHdrBits      ) & 0x00000003;*/

  // check if header is valid
/*  if ( 
       (m_Syncword      != MPEG_SYNCWORD) ||

#ifndef SYNC_ALL_LAYERS
       (m_Layer         !=  3           ) ||
#else
       (m_Layer         ==  4           ) ||
#endif

       (m_BitrateNdx    ==  15          ) ||
       (m_BitrateNdx    ==  0           ) ||
       (m_SampleRateNdx == 3            ) ||
       (m_Idex == 0 && m_Layer != 3     ) ||
       (m_Idex == 0 && m_Id == 1 && m_Layer == 3)
     )*/

    if ( 
       (m_Syncword      != MPEG_SYNCWORD) ||
       (m_Layer         ==  4           ) ||
       (m_BitrateNdx    ==  15          ) ||
       (m_BitrateNdx    ==  0           ) ||
       (m_SampleRateNdx == 3            ) ||
       (m_Idex == 0 && m_Layer != 3     ) ||
       (m_Idex == 0 && m_Id == 1        ) ||
       (m_Idex == 0 && m_BitrateNdx>8   )
     )
    {
    m_HeaderValid = 0;
//    ResetMembers();
    }
  else
    {
    m_HeaderValid = 1;
//    SetMembers();
    }

  return m_HeaderValid;
}

static __int64 FileSize64(HANDLE file)
{
	LARGE_INTEGER position;
	position.QuadPart=0;
	position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart); 	
	
	if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
		return INVALID_FILE_SIZE;
	else
		return position.QuadPart;
}

int HeaderMpg::getInfos(const wchar_t *filename, bool metadata) 
{
	fh = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  if(fh==INVALID_HANDLE_VALUE) return 0;
  int ret=decodeHeader();
  if(ret) {
    int l=MulDiv(video_bitrate+audio_bitrate,2048,2018);
    l/=8;
		int64_t flen = FileSize64(fh);
    if(l)
			length=(int)((flen*1000ull) / (int64_t)l);
  }
CloseHandle(fh);
  return ret;
}

int HeaderMpg::decodeHeader() 
{
  DWORD bytesRead = 0;
  memset(buf,0,sizeof(buf));
  end=buf+sizeof(buf);
  ReadFile(fh, &buf, sizeof(buf), &bytesRead, NULL);
  
  pbuf=buf;
  while(1) {
    int code;
    code=sync_packet();
    if(!code) return 1;
    switch(code) {
    case 0x1b3: {
      if(has_video) break;

      pbuf+=4;
      if ((pbuf[6] & 0x20) != 0x20){
	      //printf("missing marker bit!\n");
	      //return 1;	/* missing marker_bit */
        break;
      }

      int height = (pbuf[0] << 16) | (pbuf[1] << 8) | pbuf[2];

      video_w = (height >> 12);
      video_h = (height & 0xfff);

      int width = ((height >> 12) + 15) & ~15;
      height = ((height & 0xfff) + 15) & ~15;

      if ((width > 768) || (height > 576)){
	      //printf("size restrictions for MP@ML or MPEG1 exceeded! (%dx%d)\n",width,height);
        //	return 1;	/* size restrictions for MP@ML or MPEG1 */
        break;
      }
    
      has_video=true;

      int bitrate=(pbuf[4]<<10)|(pbuf[5]<<2)|(pbuf[6]>>6);
      if(bitrate==262143) {
        //variable bitrate
        //has_video=false;
        break;
      }
      bitrate=bitrate*2/5;
      video_bitrate=bitrate*1000;
      break;
    }
    case 0x1be: {
      // padding stream
      pbuf+=4;
      int s=pbuf[0]<<8|pbuf[1];
      pbuf+=2+s;
      break;
    }
    }
    if(code>=0x1c0 && code<=0x1df) {
     
      pbuf+=4;
      int len=pbuf[0]<<8|pbuf[1];
      pbuf+=2;
      pbuf+=5;
			if (len > (end-pbuf))
				return 0;
      // decode mpeg audio header
      while(--len) {
        if(mp3headerFromInt((pbuf[0]<<24)|(pbuf[1]<<16)|(pbuf[2]<<8)|pbuf[3])) {
          has_audio=true;
          audio_bps=16;
          int m_MpegVersion = m_Id==1 ? MPEG1 : (m_Idex==1?MPEG2 : MPEG25);
          audio_nch = m_Mode == MODE_MONO ? 1:2;
          audio_srate  = gaSampleRate[m_MpegVersion][m_SampleRateNdx];
          audio_bitrate = gaBitrate[m_MpegVersion==MPEG1?0:1][m_Layer-1][m_BitrateNdx] * 1000;
          break;
        }
        pbuf++;
      }
    }
    if(has_audio && has_video) return 1;
    pbuf++;
  }

  return 1;
}