#pragma once

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

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

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

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

#define WAVE_FORMAT_IEEE_FLOAT 3

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 } };
typedef struct tagVORBISFORMAT
{
	WORD  nChannels;
	long  nSamplesPerSec;
	long  nMinBitsPerSec;
	long  nAvgBitsPerSec;
	long  nMaxBitsPerSec;
	float fQuality;
} VORBISFORMAT;
typedef struct _INT24
{
   BYTE b[3];
}_int24;
static const GUID IID_IEcoDecoInterface =
{ 0x1bb79127, 0xe01d, 0x4f40, { 0x92, 0x57, 0xbb, 0xc6, 0x22, 0x43, 0x7f, 0xb7 } };

__interface IEcoDecoInterface : public IUnknown
{
public:
   HRESULT SetWait(int);
   HRESULT GetInFormat(WAVEFORMATEX *);
   HRESULT GetTransformedBytes(LONGLONG *);
};
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 IDecodeInterface , public IEcoDecoInterface
{
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);

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

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

   HRESULT SetWait(int);
   HRESULT GetInFormat(WAVEFORMATEX *);
   HRESULT GetTransformedBytes(LONGLONG *);

private:

   int m_nWait;
   LONGLONG m_llInputSize;

   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;
};
