#define WIN32_LEAN_AND_MEAN 1
#define WIN32_EXTRA_LEAN 1
#include <objbase.h>
#include <commdlg.h>
#include <ocidl.h>
#include <stdio.h>
#pragma comment(linker,"/ENTRY:mainCRTStartup")
#pragma comment(linker,"/SUBSYSTEM:CONSOLE")
#pragma comment(lib,"rpcrt4") // UUIDFromString()

// This was compiled with DirectX8 (DirectShow8)
// and Microsoft Visual C/C++ 6.00

#define PLAYWAVE 1

#if PLAYWAVE
#include <dshow.h>
#include <uuids.h>

#pragma comment(lib,"strmiids")

static IGraphBuilder* waveGraph = 0;
static char waveName[1024];

void LoadWave( void )
{
	OPENFILENAME ofn = {sizeof(ofn)};
	ofn.lpstrFile = waveName;
	ofn.nMaxFile = sizeof(waveName);
	GetOpenFileName( &ofn );
}

void PlayStop( void )
{
	if( waveGraph )
	{
		IMediaControl *mc = 0;
		if( waveGraph->QueryInterface( IID_IMediaControl, (void **)&mc ) == S_OK && mc )
		{
			mc->Stop();
			mc->Release();
		}
		waveGraph->Release();
		waveGraph = 0;
	}
}

void PlayWave( IUnknown *filter )
{
	WCHAR wPath[1024];
	HRESULT hr;

	PlayStop();

	waveGraph = 0;
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (LPVOID *)&waveGraph);
    MultiByteToWideChar(CP_ACP, 0, waveName, -1, wPath, sizeof(wPath)/sizeof(wPath[0]));    
	if( filter )
	{
		IBaseFilter *baseFilter;
		hr = filter->QueryInterface( IID_IBaseFilter, (void **)&baseFilter);
		hr = waveGraph->AddFilter( (IBaseFilter *) baseFilter, L"Filter" );
		baseFilter->Release();
	}
    hr = waveGraph->RenderFile(wPath, NULL);

	if( waveGraph )
	{
		IMediaControl *mc = 0;
		if( waveGraph->QueryInterface(IID_IMediaControl, (void **)&mc) == S_OK && mc )
		{
			mc->Run();
			mc->Release();
		}
	}
}

#endif

/* code - malkia@mindless.com */

static struct FILTER {
	char *name;
	UUID uuid;
} filters[ 1024 ];
static int n_filters;

static int FilterNameSortFunc( const void *a, const void *b )
{
	return strcmp( ((FILTER *)a)->name, ((FILTER *)b)->name );
}

static void EnumFilters( void )
{
	HKEY hKeyFilter;
	HKEY hKeyCLSID;
	char keyVal[1024];
	DWORD keySize;
	DWORD keyType;
	FILETIME keyTime;
	int index;

	if( RegOpenKeyEx(HKEY_CLASSES_ROOT,"filter",0,KEY_READ,&hKeyFilter)==S_OK )
	{
		for( index=0; RegEnumKeyEx(hKeyFilter,index,keyVal,&(keySize=sizeof(keyVal)),0,NULL,NULL,&keyTime)==S_OK; index++ )
		{
			char clsidKey[1024];
			char name[1024];

			sprintf( (char*)clsidKey, "CLSID\\%s", keyVal );
			if( RegOpenKeyEx(HKEY_CLASSES_ROOT, (char*)clsidKey, 0, KEY_READ, &hKeyCLSID) == S_OK )
			{
				if(RegQueryValueEx(hKeyCLSID,"",0,&(keyType=REG_SZ),(LPBYTE)name,&(keySize=sizeof(name)))==S_OK)
				{
					if(keyVal[0]=='{') strcpy( keyVal, keyVal + 1 );
					if(keyVal[strlen(keyVal)-1]=='}') keyVal[strlen(keyVal)-1]=0;
					if(UuidFromString( (LPBYTE)keyVal, &filters[n_filters].uuid )==S_OK)
					{
						filters[n_filters].name = strdup( name );
						if(++n_filters == sizeof(filters)/sizeof(filters[0]))
							break;
					}
				}
				RegCloseKey(hKeyCLSID);
			}
		}
	}
	qsort(filters,n_filters,sizeof(filters[0]),FilterNameSortFunc);
}

enum { // These are our ids inside the dialog
	ID_Null,
	ID_FiltersListBox,
	ID_OkButton,
	ID_CancelButton,
	ID_PlayButton,
	ID_LoadButton,
};

// Resizes a window to fit a client area size(width,height)
static void windowSize( HWND wnd, int width, int height )
{
	RECT crect, wrect;
	int screenWidth, screenHeight;
	int windowWidth, windowHeight;
	GetWindowRect( wnd, &wrect );
	GetClientRect( wnd, &crect );
	screenWidth = GetSystemMetrics( SM_CXSCREEN );
	screenHeight = GetSystemMetrics( SM_CYSCREEN );
	windowWidth = wrect.right - wrect.left - crect.right + width,
	windowHeight = wrect.bottom - wrect.top - crect.bottom + height;
	MoveWindow( wnd, (screenWidth - windowWidth)/2,	(screenHeight - windowHeight)/2, windowWidth, windowHeight, TRUE );
}

static void dialogFit( HWND dlg, int propPageWidth, int propPageHeight, RECT *rect )
{
	enum {
		MinDialogHeight = 50, //400,
		ListBoxWidth = 200,
		ButtonWidth = 60,
		ButtonHeight = 20,
		ButtonOffset = 4,
		MinDialogWidth = ListBoxWidth + ButtonOffset + 4*(ButtonOffset + ButtonWidth),
	};
																		
	HWND listBox, button1, button2, button3, button4;
	int screenWidth, screenHeight;
	int dialogWidth, dialogHeight;
	screenWidth = GetSystemMetrics( SM_CXSCREEN );
	screenHeight = GetSystemMetrics( SM_CYSCREEN );

	listBox = GetDlgItem( dlg, ID_FiltersListBox );
	button1 = GetDlgItem( dlg, ID_CancelButton );
	button2 = GetDlgItem( dlg, ID_OkButton );
	button3 = GetDlgItem( dlg, ID_LoadButton );
	button4 = GetDlgItem( dlg, ID_PlayButton );

	dialogWidth  = ListBoxWidth + propPageWidth;
	dialogHeight = propPageHeight + ButtonOffset*2 + ButtonHeight;

	if( dialogWidth < MinDialogWidth )
		dialogWidth = MinDialogWidth;

	if( dialogHeight < MinDialogHeight )
		dialogHeight = MinDialogHeight;

	windowSize( dlg, dialogWidth, dialogHeight );
	MoveWindow( listBox, 0, 0, ListBoxWidth, dialogHeight, TRUE );
	MoveWindow( button1, dialogWidth - 1*(ButtonOffset + ButtonWidth), dialogHeight - ButtonOffset - ButtonHeight, ButtonWidth, ButtonHeight, TRUE );
	MoveWindow( button2, dialogWidth - 2*(ButtonOffset + ButtonWidth), dialogHeight - ButtonOffset - ButtonHeight, ButtonWidth, ButtonHeight, TRUE );
	MoveWindow( button3, dialogWidth - 3*(ButtonOffset + ButtonWidth), dialogHeight - ButtonOffset - ButtonHeight, ButtonWidth, ButtonHeight, TRUE );
	MoveWindow( button4, dialogWidth - 4*(ButtonOffset + ButtonWidth), dialogHeight - ButtonOffset - ButtonHeight, ButtonWidth, ButtonHeight, TRUE );

	if( rect )
	{
		rect->left   = ListBoxWidth;
		rect->top    = 0;
		rect->right  = rect->left + propPageWidth;
		rect->bottom = rect->top + propPageHeight;
	}
}

static struct {
	IUnknown *filter;
	IPropertyPage *propPage;
	int propWidth, propHeight;
} activeFilter;

static void SetActiveFilter( HWND wnd, int item )
{
	CAUUID caGUID={0};
	ISpecifyPropertyPages *pSpecProp=0;
	try
	{
		HRESULT hr;
		PROPPAGEINFO pPageInfo;

		if( activeFilter.propPage )
		{
			activeFilter.propPage->Deactivate();
			activeFilter.propPage->Release();
			activeFilter.propPage=0;
		}

		if( activeFilter.filter )
		{
			activeFilter.filter->Release();
			activeFilter.filter = 0;
		}

		if(	(hr=CoCreateInstance(filters[item].uuid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&activeFilter.filter))==S_OK
		&&	(hr=activeFilter.filter->QueryInterface(IID_ISpecifyPropertyPages,(void**)&pSpecProp))==S_OK 
		&&	(hr=pSpecProp->GetPages(&caGUID))==S_OK
		&&	(caGUID.cElems >= 1) // but actually we are showing only the first page
		&&	(hr=CoCreateInstance(caGUID.pElems[0],NULL,CLSCTX_INPROC_SERVER,IID_IPropertyPage,(void**)&activeFilter.propPage))==S_OK
		&&	(hr=activeFilter.propPage->SetObjects(1,(IUnknown **)&activeFilter.filter))==S_OK
		&&	(hr=activeFilter.propPage->GetPageInfo(&pPageInfo))==S_OK)
		{
			RECT rect;
			dialogFit( wnd, pPageInfo.size.cx, pPageInfo.size.cy, &rect );
			activeFilter.propWidth = pPageInfo.size.cx;
			activeFilter.propHeight = pPageInfo.size.cy;
			activeFilter.propPage->Activate( wnd, &rect, FALSE );
			activeFilter.propPage->Show( SW_SHOW );
		}
		else
		{
			dialogFit( wnd, 0, 400, NULL );
		}
	}
	catch( ... )
	{
	}
	if( pSpecProp ) pSpecProp->Release();
	if( caGUID.pElems ) CoTaskMemFree(caGUID.pElems);
}

static INT_PTR CALLBACK dialogProc( HWND dlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	switch( uMsg )
	{
		HGDIOBJ font;
		int i;

		case WM_INITDIALOG:
			SetWindowText( dlg, "DirectX audio plug-ins viewer 0.10 - " __DATE__ "/" __TIME__ " malkia@mindless.com");
			font = GetStockObject( DEFAULT_GUI_FONT );
			SendDlgItemMessage( dlg, ID_FiltersListBox,	WM_SETFONT, (WPARAM)font, FALSE );
			SendDlgItemMessage( dlg, ID_OkButton,		WM_SETFONT, (WPARAM)font, FALSE );
			SendDlgItemMessage( dlg, ID_CancelButton,	WM_SETFONT, (WPARAM)font, FALSE );
			SendDlgItemMessage( dlg, ID_PlayButton,		WM_SETFONT, (WPARAM)font, FALSE );
			SendDlgItemMessage( dlg, ID_LoadButton,		WM_SETFONT, (WPARAM)font, FALSE );

			SetDlgItemText( dlg, ID_OkButton,		"Ok" );
			SetDlgItemText( dlg, ID_CancelButton,	"Cancel" );
			SetDlgItemText( dlg, ID_PlayButton,		"Play" );
			SetDlgItemText( dlg, ID_LoadButton,		"Load" );

			for( i=0; i<n_filters; i++ )
				SendDlgItemMessage( dlg, ID_FiltersListBox, LB_ADDSTRING, 0, (LPARAM)filters[i].name);

			dialogFit( dlg, 0, 400, NULL );
			return FALSE;

		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				case ID_LoadButton:
					#if PLAYWAVE
					EnableWindow(dlg,FALSE);
					LoadWave();
					EnableWindow(dlg,TRUE);
					SetForegroundWindow( dlg );
					#endif
					break;

				case ID_PlayButton:
					#if PLAYWAVE
					EnableWindow(dlg,FALSE);
					PlayWave(activeFilter.filter);
					EnableWindow(dlg,TRUE);
					#endif
					break;

				case ID_OkButton:
					EndDialog( dlg, 1 );
					break;

				case ID_CancelButton:
					EndDialog( dlg, 0 );
					break;

				case ID_FiltersListBox:
					switch(HIWORD(wParam))
					{
						case LBN_DBLCLK:
							i = SendDlgItemMessage(dlg, ID_FiltersListBox,LB_GETCURSEL,0,0);
							if( i != LB_ERR )
							{
								// Some DEMO versions of various plugin are displaying a MessageBox before
								// inializing the object - Enable/DisableWindow is for them.
								// Example: FASOFT.COM n-Track trial version plug-ins
								EnableWindow( dlg, FALSE );
								SetActiveFilter(dlg, i );
								EnableWindow( dlg, TRUE );
								SetForegroundWindow( dlg );
							}
							break;
					}
					break;
			}
			return TRUE;
	}
	return FALSE;
}

#define	DLG_ITEM(type,id,style,styleEx,x,y,cx,cy) style,styleEx,x,y,cx,cy,id,0xFFFF,type,0,0,0
#define	DLG_BUTTON(   id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0080,id,style,styleEx,x,y,cx,cy)
#define	DLG_EDIT(     id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0081,id,style,styleEx,x,y,cx,cy)
#define	DLG_STATIC(   id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0082,id,style,styleEx,x,y,cx,cy)
#define	DLG_LISTBOX(  id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0083,id,style,styleEx,x,y,cx,cy)
#define DLG_SCROLLBAR(id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0084,id,style,styleEx,x,y,cx,cy)
#define DLG_COMBOBOX( id,style,styleEx,x,y,cx,cy) DLG_ITEM(0x0085,id,style,styleEx,x,y,cx,cy)

typedef struct {
	DLGITEMTEMPLATE item;
	WORD  _0xFFFF;
	WORD  type;
	WORD  _zero0;
	WORD  _zero1;
	WORD  _zero2;
} DIALOG_ITEM;

typedef struct {
	DLGTEMPLATE dialogTemplate;
	WORD _zer0,_zero1, zero2;
	DIALOG_ITEM items[5];
} DIALOG;

static DIALOG dialogRes =
{
	{ WS_CAPTION|DS_CENTER,0,sizeof(dialogRes.items)/sizeof(dialogRes.items[0]),0,0,0,0 }, 0,0, 0,
	{																   
		DLG_LISTBOX(ID_FiltersListBox,	WS_BORDER|WS_CHILD|WS_VISIBLE|WS_VSCROLL|LBS_NOINTEGRALHEIGHT|LBS_NOTIFY,0,0,0,0,0),
		DLG_BUTTON(	ID_OkButton,		WS_CHILD|WS_VISIBLE|BS_TEXT|BS_CENTER|BS_DEFPUSHBUTTON,0,0,0,0,0),
		DLG_BUTTON(	ID_CancelButton,	WS_CHILD|WS_VISIBLE|BS_TEXT|BS_CENTER|BS_PUSHBUTTON,0,0,0,0,0),
		DLG_BUTTON(	ID_LoadButton,		WS_CHILD|WS_VISIBLE|BS_TEXT|BS_CENTER|BS_PUSHBUTTON,0,0,0,0,0),
		DLG_BUTTON(	ID_PlayButton,		WS_CHILD|WS_VISIBLE|BS_TEXT|BS_CENTER|BS_PUSHBUTTON,0,0,0,0,0),
	}
};

void main( int argc, char *argv[])
{
	CoInitialize(NULL);
	EnumFilters();
	DialogBoxIndirect( NULL, &dialogRes.dialogTemplate, NULL, dialogProc );
	CoUninitialize();
}
