#pragma once

#include <streams.h>
#include <time.h>
#include <math.h>
#include "stdio.h"
#include "../CSimpleFilter/CSimpleFilter.h"

#include "codec.h"
#include "vorbisenc.h"
#include "gain_analysis.h"

#pragma comment(lib, "../lib/ogg_static.lib")
#pragma comment(lib, "../lib/vorbis_static.lib")
#pragma comment(lib, "../lib/vorbisenc_static.lib")

#define PACKET_HEADER_BIT            0x01
#define PACKET_HEADER_IDENTIFICATION 0x01
#define PACKET_HEADER_COMMENT        0x03
#define PACKET_HEADER_SETUP          0x05

#ifndef TEST_FILTER
#define WAVE_FORMAT_IEEE_FLOAT 3
#endif


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 MEDIASUBTYPE_Vorbis =
{ 0xcddca2d5, 0x6d75, 0x4f98, { 0x84, 0x0e, 0x73, 0x7b, 0xed, 0xd5, 0xc6, 0x3b } };

static const GUID FORMAT_VorbisFormat =
{ 0x6bddfa7e, 0x9f22, 0x46a9, { 0xab, 0x5e, 0x88, 0x4e, 0xff, 0x29, 0x4d, 0x9f } };

static const GUID IID_IEncodeVorbisInterface =
{ 0x44895f57, 0xfe20, 0x4d78, { 0x9f, 0xe3, 0x92, 0x32, 0x61, 0x20, 0xd4, 0xba } };

static const GUID IID_IReplayGainInterface =
{ 0xa0be000d, 0x186e, 0x44f2, { 0xb9, 0x7c, 0xaa, 0x1f, 0xfe, 0x18, 0xc1, 0xa5 } };

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

typedef struct _INT24
{
   BYTE b[3];
}_int24;

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

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

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

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

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

   
   HRESULT OnTransform(IMediaSample *, IMediaSample *);
   HRESULT OnStart();
   HRESULT OnSeek();
   STDMETHODIMP OnQueryInterface(REFIID, void **);

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

private:

   
   void VorbisClear();
   void HeaderInitialize(void);
   void BlockInitialize(void);
   void ReceiveHeader(BYTE *, int);

   
   WAVEFORMATEX m_outFormat;
   REFERENCE_TIME m_rtCurrent;

   bool m_bHeaderInitialized;
   bool m_bBlockInitialized;

   int m_nPacketNumber;
   bool m_bUseGain;
   float m_fGain;

   vorbis_block     m_vb;
   vorbis_comment   m_vc;
   vorbis_dsp_state m_vd;
   vorbis_info      m_vi;
};

class CEncodeVorbis : public CSimpleWriter , public IEncodeVorbisInterface
{
public:
   DECLARE_IUNKNOWN

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

   
   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 Convert8to32f(BYTE *, float **, int);
   void Convert16to32f(short *, float **, int);
   void Convert24to32f(_int24 *, float **, int);
   void Convert32to32f(int *, float **, int);
   void Convert32fto32f(float *, float **, int);
   void Convert64to32f(double *, float **, int);
   void Analyze(BYTE *, int, WAVEFORMATEX *);

   HRESULT Encode(HANDLE, int);
   HRESULT Receive(HANDLE, IMediaSample *);

   
   HRESULT CheckVolumeMode(bool);
   HRESULT GetReplayGain(double *, double *);
   HRESULT GetGainResult(unsigned int*);
   HRESULT SetOutFormat(void *);
   HRESULT SetReplaygain(bool, bool);

private:

   
   ogg_stream_state m_os;
   ogg_page         m_og;
   ogg_packet       m_op;
   vorbis_info      m_vi;
   vorbis_comment   m_vc;
   vorbis_dsp_state m_vd;
   vorbis_block     m_vb;
   int m_nChannels;


   bool m_bInitialize;
   WAVEFORMATEX m_inFormat;
   VORBISFORMAT m_outFormat;
   double m_dPeak;

   bool m_bCheckVolume;
   bool m_bUseTrackgain;
   bool m_bUseAlbumgain;
};

