#pragma once

#include <streams.h>
#include <shlwapi.h>
#include <mmreg.h>
#include <msacm.h>

#include "../CSimpleFilter/CSimpleFilter.h"

#include "BladeMP3EncDLL.h"
#include "mpg123.h"
#include "mpglib.h"
#include "gain_analysis.h"

#define ECODECO_ENCODE_FRAUNHOFER 60
#define ECODECO_ENCODE_LAME       61
#define ECODECO_ENCODE_VORBIS     62

#define ECODECO_ENCODE_MODE_ABR   90
#define ECODECO_ENCODE_MODE_CBR   91
#define ECODECO_ENCODE_MODE_VBR   92


static const GUID CLSID_DecodeMp3 = 
{ 0xda178237, 0x1e6d, 0x4159, { 0xb9, 0xb7, 0xec, 0x32, 0xa, 0x4c, 0xc5, 0x14 } };


static const GUID CLSID_EncodeLame = 
{ 0xed6dabe8, 0x2ad7, 0x465f, { 0xb2, 0xd4, 0x5b, 0x81, 0x13, 0x32, 0x95, 0x19 } };


static const GUID CLSID_EncodeFraunhofer = 
{ 0x6997dde2, 0xcb75, 0x4d03, { 0xb8, 0x9b, 0x39, 0xc0, 0x7a, 0xf6, 0x13, 0x65 } };

static const GUID MEDIASUBTYPE_Mp3 = 
{ 0x00000055, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; 

typedef struct
{
   int nMode;
   int nQuality;

   struct
   {
      DWORD dwBitrate;
   } abr;

   struct
   {
      DWORD dwBitrate;
   } cbr;

   struct
   {
      int nVBRQuality;
      DWORD dwMinBitrate;
      DWORD dwMaxBitrate;
   } vbr;
} LAME_CONFIG, *PLAME_CONFIG;



static const GUID IID_IEncodeLameInterface =
{ 0x7ff9fbcc, 0x4270, 0x44c6, { 0x90, 0xc5, 0xd4, 0xe2, 0xd1, 0xbe, 0xbf, 0x6a } };
__interface
IEncodeLameInterface: public IUnknown
{
public:
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT SetOutFormat(WAVEFORMATEX *, void *);
   HRESULT SetReplaygain(bool, bool);
};



static const GUID IID_IEncodeFraunhoferInterface = 
{ 0xd413bc2f, 0xf8a0, 0x4275, { 0xbe, 0x64, 0x45, 0xad, 0x9, 0x53, 0xdb, 0xa1 } };
__interface
IEncodeFraunhoferInterface: public IUnknown
{
public:
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT SetOutFormat(WAVEFORMATEX *, int);
   HRESULT SetReplaygain(bool, bool);
};

class CDecodeMp3 : public CSimpleTransform , public IDecodeInterface
{
public:
   DECLARE_IUNKNOWN

   
   CDecodeMp3(LPUNKNOWN, HRESULT *);
   ~CDecodeMp3();

   
   static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);

   
   HRESULT OnConnectInPin(const CMediaType *);
   HRESULT OnConnectOutPin(const CMediaType *, int, CMediaType *, int *);

   
   HRESULT GetOutFormat(void *);
   HRESULT SetOutFormat(void *);

   
   HRESULT OnStart();
   HRESULT OnSeek();
   HRESULT OnStop(bool);
   HRESULT OnReceive(IMediaSample *);
   STDMETHODIMP OnQueryInterface(REFIID, void **);

private:

   
   bool ID3v1TagExists(BYTE *, int);
   int GetFramePos(BYTE *, int, int);
   bool GetLameScale(BYTE *, int, double *);

   
   mpg123 *m_pMpg123;
   WAVEFORMATEX m_outFormat;
   bool m_bFirstReceive;
   REFERENCE_TIME m_rtCurrent;

   IMediaSample *m_pOutSample;
   int m_nOutBufferSize;
   int m_nOutActualDataLength;
   BYTE *m_pOutBuffer;
};

class CEncodeLame : public CSimpleWriter , public IEncodeLameInterface
{
public:
   DECLARE_IUNKNOWN

   
   CEncodeLame(IUnknown *, HRESULT *);
   ~CEncodeLame();

   
   static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);

   
   HRESULT OnConnectInPin(const CMediaType *);

   
   HRESULT OnStart(HANDLE);
   HRESULT OnReceive(HANDLE, IMediaSample *);
   HRESULT OnStop(HANDLE, bool);

   STDMETHODIMP OnQueryInterface(REFIID, void **);

   
   void Analyze(BYTE *, int, WAVEFORMATEX *);
   HRESULT EncodeInit(int);
   HRESULT EncodeEnd(HANDLE, WCHAR *);
   void WriteHeader(WCHAR *);

   
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT SetOutFormat(WAVEFORMATEX *, void *);
   HRESULT SetReplaygain(bool, bool);

private:

   
   void LameRelease(void);
   void SetConfig(BE_CONFIG *, LAME_CONFIG *, WAVEFORMATEX *);

   
   int m_nWait;
   LONGLONG m_llInputSize;

   HMODULE m_hDLL;
   HBE_STREAM m_hbeStream;
   BE_CONFIG m_beConfig;
   PBYTE m_pMP3Buffer;
   int m_nMaxBuffer;
   int m_nCurrentPos;

   BEINITSTREAM     beInitStream;
   BEENCODECHUNK    beEncodeChunk;
   BEDEINITSTREAM   beDeinitStream;
   BECLOSESTREAM    beCloseStream;
   BEWRITEVBRHEADER beWriteVBRHeader;
   BEWRITEINFOTAG   beWriteInfoTag;

   WAVEFORMATEX m_inFormat;
   LAME_CONFIG m_outFormat;

   bool m_bCheckVolume;
   bool m_bUseTrackgain;
   bool m_bUseAlbumgain;
   double m_dPeak;
};

class CEncodeFraunhofer : public CSimpleWriter , public IEncodeFraunhoferInterface
{
public:
   DECLARE_IUNKNOWN

   
   CEncodeFraunhofer(IUnknown *, HRESULT *);
   ~CEncodeFraunhofer();

   
   static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);

   
   HRESULT OnConnectInPin(const CMediaType *);

   
   HRESULT OnReceive(HANDLE, IMediaSample *);
   STDMETHODIMP OnQueryInterface(REFIID, void **);
   HRESULT OnStart(HANDLE);
   HRESULT OnStop(HANDLE, bool);

   
   void Analyze(BYTE *, int, WAVEFORMATEX *);
   HRESULT EncodeInit(int);
   HRESULT EncodeEnd(HANDLE);

   
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT SetOutFormat(WAVEFORMATEX *, int);
   HRESULT SetReplaygain(bool, bool);

private:

   
   void AcmRelease(void);
   HRESULT GetOutFortmat(HACMDRIVER, HACMDRIVERID, int, WAVEFORMATEX *, MPEGLAYER3WAVEFORMAT *);

   
   HMODULE m_hDLL;
   HACMDRIVER   m_hDriver;
   HACMDRIVERID m_hDriverID;
   HACMSTREAM   m_hStream;
   ACMSTREAMHEADER m_acmHeader;

   BYTE *m_pInBuffer;
   BYTE *m_pOutBuffer;

   int m_nBitrate;

   WAVEFORMATEX m_inFormat;
   MPEGLAYER3WAVEFORMAT m_outFormat;

   bool m_bCheckVolume;
   bool m_bUseTrackgain;
   bool m_bUseAlbumgain;
   double m_dPeak;
};

