#pragma once

#define STRICT

#include <streams.h>

#include <shlobj.h>
#include <Shlwapi.h>
#include <dmoreg.h>
#include <dmodshow.h>

#include <math.h>

#include <vector>
using namespace std;

#include "resource.h"

#define WM_GRAPHNOTIFY WM_USER+2
#define WAVE_FORMAT_IEEE_FLOAT 3

#define SAFE_RELEASE(p)       { if(p!=NULL) { (p)->Release(); (p)=NULL; } }
#define SAFE_DELETEMEDIATYPE(p) { if(p!=NULL) { ::DeleteMediaType(p); (p)=NULL; } }

#define SAFE_DELETE(p)        { if(p!=NULL) { delete (p); (p)=NULL; } }
#define SAFE_DELETEARRAY(p)   { if(p!=NULL) { delete [](p); (p)=NULL; } }
#define SAFE_DELETEOBJECT(p)  { if(p!=NULL) { ::DeleteObject(p); (p)=NULL; } }
#define SAFE_FREELIBRARY(p)   { if(p!=NULL) { ::FreeLibrary(p); (p)=NULL; } }

#define SAFE_CLOSEHANDLE(p)   { if(p!=NULL) { ::CloseHandle(p); (p)=NULL; } }

#ifdef _DEBUG
   #pragma comment(lib, "strmbasd.lib")
#else
   #pragma comment(lib, "strmbase.lib")
#endif

#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "Dmoguids.lib")


static const GUID CLSID_ConvertWav = { 0xefbf44b7, 0xbb1f, 0x48c8, { 0xba, 0x59, 0xeb, 0x98, 0x3, 0x57, 0x9b, 0x1e } };
static const GUID IID_IConvertWavInterface = { 0xbda4cab0, 0x2eb2, 0x42cb, { 0xa6, 0x31, 0xe8, 0x9e, 0x82, 0x35, 0x2d, 0xd1} };


static const GUID CLSID_EcoDecoWriter         = { 0x4b1abfc2, 0xe95f, 0x4cb3, { 0xbe, 0xc8, 0x65, 0x7e, 0x6d, 0x89, 0x6a, 0x70 } };
static const GUID IID_IEcoDecoWriterInterface = { 0xfb4a5a46, 0x0bb7, 0x4c75, { 0xad, 0xa1, 0xe0, 0x24, 0x1e, 0xc9, 0x4e, 0x9e } };


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 IID_IEncodeLameInterface = { 0x7ff9fbcc, 0x4270, 0x44c6, { 0x90, 0xc5, 0xd4, 0xe2, 0xd1, 0xbe, 0xbf, 0x6a } };
static const GUID IID_IEncodeFraunhoferInterface = { 0xd413bc2f, 0xf8a0, 0x4275, { 0xbe, 0x64, 0x45, 0xad, 0x9, 0x53, 0xdb, 0xa1 } };


static const GUID CLSID_DecodeVorbis = { 0x78090821, 0x8aae, 0x4e21, { 0x81, 0xb2, 0x18, 0x46, 0x82, 0x97, 0xb4, 0x9f } };
static const GUID CLSID_EncodeVorbis = { 0x84563040, 0xdd36, 0x4f00, { 0x8e, 0xea, 0x21, 0x5c, 0xbd, 0xdb, 0x18, 0x14 } };
static const GUID IID_IEncodeVorbisInterface = { 0x44895f57, 0xfe20, 0x4d78, { 0x9f, 0xe3, 0x92, 0x32, 0x61, 0x20, 0xd4, 0xba } };


struct LinkedList
{
   WCHAR *pwText;
   LinkedList *pNextList;
};

enum LANGUAGE
{
   ENGLISH = 0,
   JAPANESE,

   sizeof_LANGUAGE
};

enum CONTAINER
{
   CONTAINER_WAV = 0,
   CONTAINER_AAC,
   CONTAINER_MP3_FRAUNHOFER,
   CONTAINER_MP3_LAME,

   CONTAINER_OGG,

   CONTAINER_RAW,

   sizeof_CONTAINER
};

enum CONVERT_STATE
{

   STATE_START_NEXTFILE = 0,
   STATE_START_NEXTFILE_ALBUM_NORMALIZE,

   STATE_START_SAMEFILE,
   STATE_START_NEXT_FRONTEND,

   STATE_CONVERTING,
   STATE_CONVERTING_ALBUM_NORMALIZE,
   STATE_CONVERTING_SAMEFILE,
   STATE_CONVERTING_FRONTEND,

   STATE_END_SUCCESS,
   STATE_END_SUCCESS_ALBUM_NORMALIZE,
   STATE_END_SUCCESS_FRONTEND,
   STATE_END_ERROR,                            
   STATE_END_ERROR_ALBUM_NORMALIZE,            
   STATE_END_SUCCESS_NEXTFILE,                 
   STATE_END_SUCCESS_NEXTFILE_ALBUM_NORMALIZE, 
   STATE_END_SUCCESS_NEXT_SAMEFILE,            
   STATE_END_SUCCESS_NEXT_FRONTEND             



};

enum GAINMODE
{
   GAIN_TRACK_NORMALIZE = 1,
   GAIN_TRACK_GAIN,
   GAIN_ALBUM_GAIN,
   GAIN_ALBUM_NORMALIZE
};







typedef struct tagVORBISFORMAT
{
	WORD  nChannels;
	long  nSamplesPerSec;
	long  nMinBitsPerSec;
	long  nAvgBitsPerSec;
	long  nMaxBitsPerSec;
	float fQuality;
} VORBISFORMAT;

struct CONVERTDATA2
{
   WAVEFORMATEX wf;
   int nEncodeMode;
   int nAdvancedMode;
   int nABRCBR;
   int nVBR;
   float fVBR;

   int nGainMode;
   bool bUseFrontEnd;
   bool bNormalize;
   double dTargetGain;
   double dNormalize;

   int nPass;
   double dAlbumPeak;
   double dAlbumGain;

   unsigned int anGainResult[12000];
};

struct SETTINGDATA
{
   int nContainerType;


   int nOutputFolder;
   WCHAR awOutputFolder[MAX_PATH];

   WAVEFORMATEX wf;
   int nEncodeMode;
   int nAdvancedMode;
   int nABRCBR;
   int nVBR;
   float fVBR;

   int nGainMode;
   double dGain;
};

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;

__interface IConvertWavInterface : public IUnknown
{
public:
   HRESULT GetInFormat(WAVEFORMATEX *);
   HRESULT GetOutFormat(WAVEFORMATEX *);
   HRESULT GetTransformedBytes(LONGLONG *);
   HRESULT SetOutFormat(WAVEFORMATEX *);
   HRESULT SetNormalize(double);
   HRESULT SetWait(int);
};

__interface IEcoDecoWriterInterface : public IUnknown
{
public:
   int GetOutputMode(void);
   void SetOutputMode(int);
   HRESULT CheckVolumeMode(bool);
   HRESULT GetInFormat(WAVEFORMATEX *);
   HRESULT GetReplayGain(double *, double *);
   HRESULT GetGainResult(unsigned int*);
   HRESULT SetReplaygain(bool, bool);
};

__interface IDecodeInterface : public IUnknown
{
public:
   HRESULT GetOutFormat(void *);
   HRESULT SetOutFormat(void *);
};

__interface IEncodeLameInterface: public IUnknown
{
public:
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT GetGainResult(unsigned int*);
   HRESULT SetOutFormat(WAVEFORMATEX *, void *);
   HRESULT SetReplaygain(bool, bool);
};

__interface IEncodeFraunhoferInterface: public IUnknown
{
public:
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT GetGainResult(unsigned int*);
   HRESULT SetOutFormat(WAVEFORMATEX *, int);
   HRESULT SetReplaygain(bool, bool);
};

__interface IEncodeVorbisInterface : public IUnknown
{
public:
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT GetGainResult(unsigned int*);
   HRESULT SetOutFormat(void *);
   HRESULT SetReplaygain(bool, bool);
};





class CLinkedList
{
public:
   CLinkedList();
   ~CLinkedList();

   void Clear(void);
   void AddList(WCHAR *);
   int GetCount(void);
   WCHAR* GetCurrentText(void);
   WCHAR* GetNextText(void);
   void SetFirstList(void);

private:
   LinkedList *m_pFirstLinkedList;
   LinkedList *m_pCurrentLinkedList;
   LinkedList *m_pEndLinkedList;
   int m_nCount;
   bool m_bFirst;
};



class CConvert
{
public:

   
   CConvert(int, HRESULT *);
   ~CConvert();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT WriteAlbumGain(WCHAR *, WCHAR *, double, double);
   int GraphNotify(void);
   void SetWait(int);
   int ConvertEnd(CONVERTDATA2 *);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   void ConvertExit(void);

private:

   void *m_pConvert;
   int m_nContainerType;

};

class CConvertDirectShow
{
public:

   
   CConvertDirectShow();
   ~CConvertDirectShow();

   
   void SetWait(int);
   int GraphNotify(void);

protected:

   HRESULT GetDirectShowFormat(WAVEFORMATEX *, WAVEFORMATEX *);
   int GetPercent(void);
   void ReleaseDirectShow(void);
   HRESULT GetOutputFileName(WCHAR *, WCHAR *, WCHAR *, WCHAR *, bool);
   HRESULT Render(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);

   int GetPinCount(IBaseFilter *, PIN_DIRECTION);
   IPin* GetPin(IBaseFilter *, PIN_DIRECTION, int);
   IPin* GetPin(IBaseFilter *, PIN_DIRECTION, GUID);
   HRESULT ConnectWithDecoder(IGraphBuilder *, IBaseFilter *, IBaseFilter *, bool);
   HRESULT ConnectDirect(IGraphBuilder *, IBaseFilter *, IBaseFilter *, AM_MEDIA_TYPE *);
   IBaseFilter *AddFilter(IGraphBuilder *, CLSID);
   IBaseFilter *AddDmoFilter(IGraphBuilder *, CLSID, CLSID);
   IBaseFilter *AddSourceFilter(IGraphBuilder *, CLSID, WCHAR *);
   IBaseFilter *AddWriteFilter(IGraphBuilder *, CLSID, WCHAR *);
   HRESULT RenderByFileExt(IGraphBuilder *, WCHAR *, IBaseFilter *, bool);
   HRESULT RenderByAllFilter(IGraphBuilder *, WCHAR *, IBaseFilter *, bool);

private:

   
   virtual void Release(void) PURE;
   virtual HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *) PURE;

   
   HRESULT Render(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int, bool);

   
   IGraphBuilder *m_pGraphBuilder;
   IMediaEventEx *m_pMediaEventEx;
   IMediaControl *m_pMediaControl;

   LONGLONG m_llTotalBytes;
   IConvertWavInterface *m_pConvertWavInterface;

};

class CConvertToWav : public CConvertDirectShow
{
public:

   
   CConvertToWav();
   ~CConvertToWav();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT GetReplayGain(double *, double *);
   HRESULT WriteAlbumGain(WCHAR *, WCHAR *, double, double);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *);

   
   HRESULT WriteReplayGain(WCHAR *, int, double, double);

   
   WCHAR m_awOutputFileName[MAX_PATH];
   IEcoDecoWriterInterface *m_pEcoDecoWriterInterface;
};

class CConvertToAac : public CConvertDirectShow
{
public:

   
   CConvertToAac();
   ~CConvertToAac();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT GetReplayGain(double *, double *);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *);

   
   HRESULT NeroAacEnc(WCHAR *, WCHAR *, CONVERTDATA2 *);

   
   WCHAR m_awTmpFileName[MAX_PATH];
   WCHAR m_awOutputFileName[MAX_PATH];
   IEcoDecoWriterInterface *m_pEcoDecoWriterInterface;
   PROCESS_INFORMATION m_pi;
};

class CConvertToMp3Fraunhofer : public CConvertDirectShow
{
public:

   
   CConvertToMp3Fraunhofer();
   ~CConvertToMp3Fraunhofer();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT GetReplayGain(double *, double *);
   HRESULT WriteAlbumGain(WCHAR *, WCHAR *, double, double);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *);

   
   HRESULT WriteReplayGain(WCHAR *, int, double, double, double, double);

   
   WCHAR m_awOutputFileName[MAX_PATH];
   IEncodeFraunhoferInterface *m_pEncodeFraunhoferInterface;
};

class CConvertToMp3Lame : public CConvertDirectShow
{
public:

   
   CConvertToMp3Lame();
   ~CConvertToMp3Lame();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT GetReplayGain(double *, double *);
   HRESULT WriteAlbumGain(WCHAR *, WCHAR *, double, double);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *);

   
   HRESULT WriteReplayGain(WCHAR *, int, double, double, double, double);

   
   WCHAR m_awOutputFileName[MAX_PATH];
   IEncodeLameInterface *m_pEncodeLameInterface;
};

class CConvertToOgg : public CConvertDirectShow
{
public:

   
   CConvertToOgg();
   ~CConvertToOgg();

   
   void GetFormat(WCHAR *, WCHAR *);
   HRESULT GetReplayGain(double *, double *);
   HRESULT WriteAlbumGain(WCHAR *, WCHAR *, double, double);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT RenderGraph(IGraphBuilder *, WCHAR *, WCHAR *, IBaseFilter *, IConvertWavInterface * , CONVERTDATA2 *);

   
   HRESULT WriteReplayGain(WCHAR *, int, double, double, double, double);
   void GetCRC32(DWORD *, BYTE *, int);

   
   WCHAR m_awOutputFileName[MAX_PATH];
   IEncodeVorbisInterface *m_pEncodeVorbisInterface;
};

class CConvertRaw
{
public:

   
   CConvertRaw();
   ~CConvertRaw();

   
   void SetWait(int);
   int GraphNotify(void);
   void GetFormat(WCHAR *, WCHAR *);
   int ConvertStart(HWND, WCHAR *, WCHAR *, CONVERTDATA2 *, int);
   int Converting(int *);
   int ConvertEnd(CONVERTDATA2 *);
   void ConvertExit(void);

private:

   
   void Release(void);
   HRESULT Render(HWND, WCHAR *, WCHAR *);

   HRESULT ConnectDirect(IGraphBuilder *, IBaseFilter *, IBaseFilter *, AM_MEDIA_TYPE *);
   int GetPinCount(IBaseFilter *, PIN_DIRECTION);
   IPin *GetPin(IBaseFilter *, PIN_DIRECTION, int);
   IPin *GetPin(IBaseFilter *, PIN_DIRECTION, GUID);
   IBaseFilter *AddFilter(IGraphBuilder *, CLSID);
   IBaseFilter *AddSourceFilter(IGraphBuilder *, CLSID, WCHAR *);

   
   int m_nRawFileType;
   WCHAR m_awOutputFileName[MAX_PATH];
   IGraphBuilder *m_pGraphBuilder;
   IMediaEventEx *m_pMediaEventEx;
   IMediaControl *m_pMediaControl;
};

class CMainDialog
{
public:

   
   CMainDialog();
   ~CMainDialog();

   bool LoadSetting(SETTINGDATA *);
   void ShowDialog(SETTINGDATA *, int, CLinkedList *);

private:

   WCHAR *GetLocalText(int);

   bool ExistsNeroAAC(void);
   bool ExistsFraunhoferPro(void);
   bool ExistsLameEnc(void);
   bool DropFiles(HWND, HDROP);
   bool GetReplaygain(HWND, int, double *);
   void DrawHoverButton(DRAWITEMSTRUCT *, int);
   void PrepareComboBox(HWND, int);
   int GetPrivateProfileInt(WCHAR *, WCHAR *, int, int, int, WCHAR *);
   void SetComboBoxCursel(WCHAR *, HWND, int);
   void WriteIniFile(WCHAR *, HWND, int, WCHAR *);
   void SelectFolder(HWND);
   void SelectMode(HWND, int, int);
   void SelectContainer(HWND, int);
   bool GetSettingData(HWND);
   void OnCreate(HWND);
   void OnClose(HWND);
   void DrawItem(DRAWITEMSTRUCT *);
   void OnPaint(HDC);
   void DrawCustomRoundRect(HDC, RECT *);
   static int CALLBACK SHBrowseProc(HWND, UINT, LPARAM,  LPARAM);
   static LRESULT CALLBACK DialogWaveFormatProc(HWND, UINT, WPARAM, LPARAM);
   static LRESULT CALLBACK CustomProc(HWND, UINT, WPARAM, LPARAM);
   static LRESULT CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);

   int m_nLanguage;
   bool m_bExistsNeroAacEnc;
   bool m_bExistsFraunhoferPro;
   bool m_bExistsLameEnc;
   bool m_bExistsThomsonEnc;

   struct PROC
   {
      int nItemType;
      int nResourceID;
      WNDPROC proc;
   };

   enum{ sizeof_MYPROC = 10 };
   PROC m_myProc[sizeof_MYPROC];

   int m_nMinResourceID;
   int m_nCloseResourceID;
   WAVEFORMATEX m_waveFormat;

   HRGN m_hRgn;
   SETTINGDATA *m_pSettingData;
   CLinkedList *m_pDropLinkedList;
};

class CConvertDialog
{
public:

   
   CConvertDialog();
   ~CConvertDialog();

   void GetSubString(WCHAR *, WCHAR *, int);
   void ShowDialog(SETTINGDATA *, int, CLinkedList *);

private:

   void AddListFromFolder(HWND, WCHAR *);
   void DropFiles(HWND);

   void ConvertExit(HWND);

   void ConvertStart(HWND hWnd);
   void Converting(HWND hWnd);
   void ConvertEnd(HWND hWnd);

   void DrawCustomRoundRect(HDC, RECT *);
   void DrawItem(DRAWITEMSTRUCT *);
   void DrawHoverButton(DRAWITEMSTRUCT *, int);

   void OnCreate(HWND);
   void OnPaint(HDC);
   void OnClose(HWND);
   static LRESULT CALLBACK CustomProc(HWND, UINT, WPARAM, LPARAM);
   static LRESULT CALLBACK DialogProc(HWND, UINT, WPARAM, LPARAM);

   bool m_bExit;
   int m_nLanguage;
   HRGN m_hRgn;
   int m_nMinResourceID;
   int m_nCloseResourceID;

   WNDPROC m_myProc[2];

   int m_nConvertState;
   SETTINGDATA m_settingData;
   CONVERTDATA2 m_convertData;

   bool m_bRunningConvert;
   bool m_bGraphNotify;
   int m_nCurrentPercent;
   int m_nAnimationCount;

   int m_nCurrentCpuUsage;
   int m_nNewCpuUsage;
   int m_nSuccessCount;
   int m_nErrorCount;
   CLinkedList *m_pDropLinkedList;
   CLinkedList *m_pInputLinkedList;

   CConvert *m_pConvert;
};

