#include <atlbase.h>
#include <windows.h>
#include <dshow.h>
#include <stdio.h>
#include <qedit.h>
#include <initguid.h>
#include <vfw.h>
#include <mtype.h>
#include <ks.h>
#include "dsutil.h"
#include "..\..\Include\ieibt878.h"
#include "playcap.h"
#include "resource.h"
#include "status.h"

#define VF_NTSC

#define CAP_W	160
#define CAP_H	120
#define CAP_W1	320
#define CAP_H1	240

int g_FrameWidth = CAP_W;
int g_FrameHeight = CAP_H;
int g_nCurrentSize = 0;
HWND ghApp = 0, ghwndStatus = 0;
HINSTANCE g_hInstance = 0;

int g_nDeviceCount=0;
int g_nCurrentDevice=0;
//int g_nCount=0;
bool g_bLimitSize = false;
IMediaControl * g_pMC = NULL;
IMediaEventEx * g_pME = NULL;
IGraphBuilder * g_pGraph = NULL;
ICaptureGraphBuilder2 * g_pCapture = NULL;
PLAYSTATE g_psCurrent = Stopped;

long g_lCapStartTime = 0;

ISampleGrabber  *g_pGrab = NULL;
IBaseFilter     *g_pBF = NULL;
IBaseFilter     *g_pNullRender = NULL;
IAMAnalogVideoDecoder *g_pVdoDec[MAX_DEVICE] = {0};
IAMStreamConfig	*g_pVSC[MAX_DEVICE] = {0};
IKsPropertySet *g_pKPS[MAX_DEVICE]={0};
IBaseFilter *g_pSrcFilter[MAX_DEVICE]={0};
BOOL g_SetAutoSwitch = false;
BOOL g_SetAutoDetect = false;
BOOL g_ViewChannel[4] = {1, 1, 1, 1};
char g_szDeviceText[64];
int g_IgnoreCount[4] = {0};

DWORD g_dwTestOutput[10][4] = {
	{0, 0, 0, 0},
	{1, 0, 0, 0},
	{0, 1, 0, 0},
	{0, 0, 1, 0},
	{0, 0, 0, 1},
	{1, 1, 1, 0},
	{1, 1, 0, 1},
	{1, 0, 1, 1},
	{0, 1, 1, 1},
	{1, 1, 1, 1}
};

double g_dwChannelFrameCount[4] = {0.0, 0.0, 0.0, 0.0};
double g_fTotalByteReceived = 0.0;

HBRUSH g_hBrush = NULL;

//IAMCrossbar		*g_pXbar;

//long			g_lXBarInputCnt, g_lXBarOutputCnt;
//long			g_lPinCounter = 4;

//long		g_lSampleWidth, g_lSampleHeight;
//BITMAPINFO	g_BitmapInfo;

static	HDRAWDIB	hDrawDib;
static	BITMAPINFOHEADER BmpInfHdr;

class CFakeCallback : public ISampleGrabberCB
{
public:
    STDMETHODIMP_(ULONG) AddRef() { return 2; }
    STDMETHODIMP_(ULONG) Release() { return 1; }

    STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
    {
        if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
        {
            *ppv = (void *) static_cast<ISampleGrabberCB *>(this);
            return NOERROR;
        }
        return E_NOINTERFACE;
    }

    STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample )
    {
        return 0;
    }

    STDMETHODIMP BufferCB( double SampleTime, BYTE * pBuffer, long BufferLen )
    {
		//RECT rc;
		//GetClientRect(ghApp, &rc);
		int x, y;
		//char szText[256];

		//sprintf(szText, "Get video data = 0x%x\n", pBuffer[0]);
		//OutputDebugString(szText);
#if 1
		if (pBuffer[0] < 4) {
			if (g_ViewChannel[pBuffer[0]] == false)
				return 0;
			g_dwChannelFrameCount[pBuffer[0]] += 1.0;
			g_fTotalByteReceived += (double) BufferLen;
			g_IgnoreCount[pBuffer[0]] = 0;
		}
		else if (pBuffer[0] < 8) {
			if (g_ViewChannel[pBuffer[0] - 4] == false)
				return 0;
			if (g_IgnoreCount[pBuffer[0] - 4] < 2)
				g_IgnoreCount[pBuffer[0] - 4]++;
			else
				return 0;
		}
		else
			return 0;
		//switch (g_nCount)
		switch (pBuffer[0])
		{
			case 0:
			case 4:
				x = 0;
				y = 0;
				break;

			case 1:
			case 5:
				x = g_FrameWidth;
				y = 0;
				break;

			case 2:
			case 6:
				x = 0;
				y = g_FrameHeight;
				break;

			case 3:
			case 7:
				x = g_FrameWidth;
				y = g_FrameHeight;
				break;
		}
#endif
		HDC hDC2 = GetDC(ghApp);

		DrawDibBegin(hDrawDib, hDC2, 0, 0, &BmpInfHdr, 0, 0, 0x0200);
		//DrawDibDraw(hDrawDib, hDC2, rc.left, rc.top, rc.right, rc.bottom, &BmpInfHdr, pBuffer, 0, 0,
		DrawDibDraw(hDrawDib, hDC2, x, y, g_FrameWidth, g_FrameHeight, &BmpInfHdr, pBuffer, 0, 0,
					g_FrameWidth, g_FrameHeight, 0x0200);

		ReleaseDC(ghApp, hDC2);
		//if (++g_nCount > 3)
		//	g_nCount = 0;
        return 0;
    }

};

static CFakeCallback pCallback;

void UpdateStatus(BOOL fAllStats)
{
    char tach[128];
	long lTime = timeGetTime() - g_lCapStartTime;
	double fTotal = (g_dwChannelFrameCount[0] + g_dwChannelFrameCount[1] + g_dwChannelFrameCount[2] + g_dwChannelFrameCount[3]);
	double fTime = (double) lTime / 1000.0 ;

    if (!fAllStats) {
        sprintf(tach, "Total %.0f frames in %.2f seconds", fTotal, fTime);
    }
	else {
		sprintf(tach, "Total %.0f frames in %.2f seconds: %.2f fps, %.2f MBps",
			fTotal, fTime, fTotal / fTime, g_fTotalByteReceived / fTime / 1048576.0);
	}
	statusUpdateStatus(ghwndStatus, tach);
    return;
}

void SetCaptureSize()
{
	if(g_pVSC[g_nCurrentDevice] == NULL)
		return;

	HRESULT hr;
	AM_MEDIA_TYPE *pmt;

	hr = g_pVSC[g_nCurrentDevice]->GetFormat(&pmt);
	if(SUCCEEDED(hr) )
	{
		if (pmt->formattype == FORMAT_VideoInfo)
		{
			VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *) pmt->pbFormat;
			pmt->subtype = MEDIASUBTYPE_RGB555;
			HEADER(pmt->pbFormat)->biWidth = g_FrameWidth;
			HEADER(pmt->pbFormat)->biHeight = g_FrameHeight;
			hr = g_pVSC[g_nCurrentDevice]->SetFormat(pmt);
		}
		DeleteMediaType(pmt);
	}
}

BOOL SetVideoFormat(long lFormat)
{
	HRESULT hr;
	long	lAvailableTVFormats = 0;

	if(g_pVdoDec[g_nCurrentDevice] == NULL)
		return FALSE;

	hr = g_pVdoDec[g_nCurrentDevice]->get_AvailableTVFormats(&lAvailableTVFormats);
	if( FAILED( hr ) )
		return FALSE;

	if( (lFormat & lAvailableTVFormats) == 0)
		return FALSE;

	hr = g_pVdoDec[g_nCurrentDevice]->put_TVFormat(lFormat);

	if(hr == S_OK)
		return TRUE;
	else
		return FALSE;
}

HRESULT ConnectGrabberPin(IBaseFilter *pCapFilter)
{
    HRESULT hr;

	if(g_pBF == NULL || pCapFilter == NULL)
		return S_FALSE;

	IPin * pCapOut = GetOutPin( pCapFilter, 0 );
	IPin * pGrabIn = GetInPin( g_pBF, 0 );
    hr = g_pGraph->Connect( pCapOut, pGrabIn );
	if( FAILED( hr ) )
        return hr;
	IPin * pGrabOut = GetOutPin( g_pBF, 0 );
	IPin * pNullRenderIn = GetInPin( g_pNullRender, 0 );
	hr = g_pGraph->Connect( pGrabOut, pNullRenderIn );
	return hr;
}

int CheckChannelMenu(HMENU hSubMenu, int nChangeDevice, bool bAutoDetect)
{
	ULONG ret = 0;
	HRESULT hr;

	KSPROPERTY_CUSTOMBT848_AUTO_DETECT_S tc1;
	ZeroMemory(&tc1,sizeof(tc1));
	hr = g_pKPS[g_nCurrentDevice]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_DETECT,
		INSTANCE_DATA_OF_PROPERTY_PTR(&tc1),
		INSTANCE_DATA_OF_PROPERTY_SIZE(tc1),
		&tc1,
		sizeof(tc1),
		&ret);
	if (FAILED(hr)) {
		g_SetAutoDetect = true;
		CheckMenuItem(hSubMenu, IDM_AUTO_DETECT, MF_CHECKED);
	}
	else {
		g_SetAutoDetect = tc1.bAutoDetect;
		CheckMenuItem(hSubMenu, IDM_AUTO_DETECT, tc1.bAutoDetect ? MF_CHECKED : MF_UNCHECKED);
	}

	if (bAutoDetect && g_SetAutoDetect) {
		KSPROPERTY_CUSTOMBT848_AUTO_DETECT_S tc1;
		ZeroMemory(&tc1,sizeof(tc1));
		tc1.bAutoDetect = true;
		g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_DETECT,
			INSTANCE_DATA_OF_PROPERTY_PTR(&tc1),
			INSTANCE_DATA_OF_PROPERTY_SIZE(tc1),
			&tc1,
			sizeof(tc1));
	}

	KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW_S tc;

	ZeroMemory(&tc,sizeof(tc));
	hr = g_pKPS[g_nCurrentDevice]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW,
		INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
		INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
		&tc,
		sizeof(tc),
		&ret);

	if (FAILED(hr)) {
		CheckMenuItem(hSubMenu, IDM_CHANNEL0, MF_CHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL1, MF_CHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL2, MF_CHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL3, MF_CHECKED);
		g_ViewChannel[0] = true;
		g_ViewChannel[1] = true;
		g_ViewChannel[2] = true;
		g_ViewChannel[3] = true;
	}
	else {
		CheckMenuItem(hSubMenu, IDM_CHANNEL0, ((tc.dwValue & 1) != 0) ? MF_CHECKED : MF_UNCHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL1, ((tc.dwValue & 2) != 0) ? MF_CHECKED : MF_UNCHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL2, ((tc.dwValue & 4) != 0) ? MF_CHECKED : MF_UNCHECKED);
		CheckMenuItem(hSubMenu, IDM_CHANNEL3, ((tc.dwValue & 8) != 0) ? MF_CHECKED : MF_UNCHECKED);
		if (nChangeDevice) {
			g_ViewChannel[0] = ((tc.dwValue & 1) != 0);
			g_ViewChannel[1] = ((tc.dwValue & 2) != 0);
			g_ViewChannel[2] = ((tc.dwValue & 4) != 0);
			g_ViewChannel[3] = ((tc.dwValue & 8) != 0);
		}
	}

	return (tc.dwValue != 0);
}

HRESULT CaptureVideo(int initialized)
{
    HRESULT hr;

	hr = GetInterfaces();
	if (FAILED(hr))
		return hr;

	if (!initialized) {
		hr = FindCaptureDevice();
		if (hr != S_OK)
			return hr;
	}

	HMENU hMenuSub = GetSubMenu(GetMenu(ghApp), 0);
	GetMenuString(hMenuSub, MENU_DEVICE0 + g_nCurrentDevice, g_szDeviceText, sizeof(g_szDeviceText), MF_BYCOMMAND);
	SetWindowText(ghApp, g_szDeviceText);

	hr = g_pGraph->AddFilter(g_pSrcFilter[g_nCurrentDevice], L"Video Capture");
	if (FAILED(hr))
		return hr;

	int must_run = CheckChannelMenu(GetSubMenu(GetMenu(ghApp), 2), 1, true);

#ifdef VF_NTSC
	SetVideoFormat((long)AnalogVideo_NTSC_M);
#else
	SetVideoFormat((long)AnalogVideo_PAL_B);
#endif

	SetCaptureSize();
	ConnectGrabberPin(g_pSrcFilter[g_nCurrentDevice]);
	//GetGrabberInfo();
	//ConnectXBar();

	g_dwChannelFrameCount[0] = 0.0;
	g_dwChannelFrameCount[1] = 0.0;
	g_dwChannelFrameCount[2] = 0.0;
	g_dwChannelFrameCount[3] = 0.0;
	g_IgnoreCount[0] = 0;
	g_IgnoreCount[1] = 0;
	g_IgnoreCount[2] = 0;
	g_IgnoreCount[3] = 0;
	g_fTotalByteReceived = 0.0;

	if (must_run) {
		hr = g_pMC->Run();
		if (FAILED(hr))
			return hr;
		
		KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
		ZeroMemory(&tc,sizeof(tc));
		tc.bAutoSwitch = true;
		hr = g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
			INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
			INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
			&tc,
			sizeof(tc));
		if (FAILED(hr)) {
			hr = g_pMC->Stop();
			return hr;
		}
		g_SetAutoSwitch = true;
		g_psCurrent = Running;
		g_lCapStartTime = timeGetTime();
		SetTimer(ghApp, ID_UPDATETIMER, 33, NULL);
	}
	return S_OK;
}

int CheckDeviceMenu(HMENU hMenuSub, int check_current_device)
{
	int i, ok = 0, count = 0;

	for (i = 0; i < g_nDeviceCount; i++) {
		KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
		ULONG ret = 0;
		HRESULT hr;

		ZeroMemory(&tc,sizeof(tc));
		hr = g_pKPS[i]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
			INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
			INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
			&tc,
			sizeof(tc),
			&ret);
		if (check_current_device) {
			if (FAILED(hr) || tc.bAutoSwitch) {
				CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_UNCHECKED);
				EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_GRAYED);
			}
			else {
				count++;
				EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_ENABLED);
				if (!ok) {
					CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_CHECKED);
					g_nCurrentDevice = i;
					ok = 1;
				}
				else
					CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_UNCHECKED);
			}
		}
		else {
			if (FAILED(hr)) {
				CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_UNCHECKED);
				EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_GRAYED);
			}
			else if (tc.bAutoSwitch) {
				if (i == g_nCurrentDevice) {
					EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_ENABLED);
					CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_CHECKED);
				}
				else {
					CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_UNCHECKED);
					EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_GRAYED);
				}
			}
			else {
				count++;
				EnableMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_ENABLED);
				CheckMenuItem(hMenuSub, MENU_DEVICE0 + i, MF_UNCHECKED);
			}
		}
	}
	if (check_current_device && count == 0)
		return 0;
	else
		return 1;
}

HRESULT FindCaptureDevice()
{
    USES_CONVERSION;

    HRESULT hr;
    CComPtr <ICreateDevEnum> pDevEnum;

    hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
        IID_ICreateDevEnum, (void ** ) &pDevEnum);
    if (FAILED(hr))
    {
        return hr;
    }

    CComPtr <IEnumMoniker> pClassEnum;

    hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
    if (FAILED(hr))
    {
        return hr;
    }

    if (pClassEnum == NULL)
    {
        MessageBox(ghApp,TEXT("No IVC-100 was detected!\r\n\r\n")
                   TEXT("This demo program requires at least one IVC-100\r\n")
                   TEXT("to be installed and working properly. The program will now close."),
                   TEXT("Warning"), MB_OK | MB_ICONWARNING);
        return E_FAIL;
    }

	HMENU hMenuSub = GetSubMenu(GetMenu(ghApp), 0);
	// Clean the sub menu
	int iMenuItems = GetMenuItemCount(hMenuSub), i;
	if (iMenuItems > 0)
	{
		for (i = 0; i < iMenuItems; i++)
		{
			RemoveMenu(hMenuSub, 0, MF_BYPOSITION);
		}

	}
	pClassEnum->Reset();

    while(1)
    {
		CComPtr <IMoniker> pMoniker;
		ULONG cFetched;
		if (S_OK != pClassEnum->Next (1, &pMoniker, &cFetched))
			break;
		CComPtr <IPropertyBag> pBag;
		hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
		if(SUCCEEDED(hr))
		{
			CComVariant var;
			var.vt = VT_BSTR;
			hr = pBag->Read(L"FriendlyName", &var, NULL);
			if (hr == S_OK) {
				char	szText[64];
				wsprintf(szText, "%s", W2T(var.bstrVal));
				if (strstr(szText, "IVC-100 Video")) {
					hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&g_pSrcFilter[g_nDeviceCount]);
					if (SUCCEEDED(hr))
					{
						hr = g_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, g_pSrcFilter[g_nDeviceCount],
									IID_IAMAnalogVideoDecoder, (void **)&g_pVdoDec[g_nDeviceCount]);
						if (SUCCEEDED(hr)) {
							hr = g_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, g_pSrcFilter[g_nDeviceCount],
										IID_IAMStreamConfig, (void **)&g_pVSC[g_nDeviceCount]);
							if (SUCCEEDED(hr)) {
								hr = g_pSrcFilter[g_nDeviceCount]->QueryInterface(IID_IKsPropertySet, (void **) &g_pKPS[g_nDeviceCount]);
								if (SUCCEEDED(hr)) {
									AppendMenu(hMenuSub, MF_STRING, MENU_DEVICE0 + g_nDeviceCount, szText);
									g_nDeviceCount++;
								}
								else {
									g_pVSC[g_nDeviceCount]->Release();
									g_pVSC[g_nDeviceCount] = NULL;
									g_pVdoDec[g_nDeviceCount]->Release();
									g_pVdoDec[g_nDeviceCount] = NULL;
									g_pSrcFilter[g_nDeviceCount]->Release();
									g_pSrcFilter[g_nDeviceCount] = NULL;
								}
							}
							else {
								g_pVdoDec[g_nDeviceCount]->Release();
								g_pVdoDec[g_nDeviceCount] = NULL;
								g_pSrcFilter[g_nDeviceCount]->Release();
								g_pSrcFilter[g_nDeviceCount] = NULL;
							}
						}
						else {
							g_pSrcFilter[g_nDeviceCount]->Release();
							g_pSrcFilter[g_nDeviceCount] = NULL;
						}
					}
				}
			}
		}
		//break;
    }
	if (g_nDeviceCount == 0) {
		MessageBox(ghApp,TEXT("No IVC-100 was detected!\r\n\r\n")
            TEXT("This demo program requires at least one IVC-100\r\n")
            TEXT("to be installed and working properly. The program will now close."),
            TEXT("Warning"), MB_OK | MB_ICONWARNING);
		return S_FALSE;
	}
	if (CheckDeviceMenu(hMenuSub, 1) == 0) {
		MessageBox(ghApp,TEXT("All available devices were busy!\r\n\r\n")
			TEXT("The demo program will now close."),
            TEXT("Warning"), MB_OK | MB_ICONWARNING);
		return S_FALSE;
	}
	else {
	}
	return S_OK;
}

HRESULT GetInterfaces()
{
    HRESULT hr;

    hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
        IID_IGraphBuilder, (void **) &g_pGraph);
    if (FAILED(hr))
        return hr;

    hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
        IID_ICaptureGraphBuilder2, (void **) &g_pCapture);
    if (FAILED(hr))
        return hr;
	hr = g_pCapture->SetFiltergraph(g_pGraph);
    if (FAILED(hr))
        return hr;

	hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC,
	    IID_IBaseFilter, (LPVOID *)&g_pNullRender);
    if (FAILED(hr))
        return hr;

	hr = g_pGraph->AddFilter(g_pNullRender, L"Null Renderer");
    if (FAILED(hr))
        return hr;

    hr = g_pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &g_pMC);
    if (FAILED(hr))
        return hr;

    hr = g_pGraph->QueryInterface(IID_IMediaEvent, (LPVOID *) &g_pME);
    if (FAILED(hr))
        return hr;

	hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC,
	    IID_IBaseFilter, (LPVOID *)&g_pBF);
    if (FAILED(hr))
        return hr;
	hr = g_pBF->QueryInterface(IID_ISampleGrabber, (void **)&g_pGrab);
    if (FAILED(hr))
        return hr;

	hr = g_pGraph->AddFilter(g_pBF, L"SampleGrabber");
    if (FAILED(hr))
        return hr;

	AM_MEDIA_TYPE   mt;
	ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
	mt.majortype = MEDIATYPE_Video;
	mt.subtype = MEDIASUBTYPE_RGB555;
	//mt.subtype = MEDIASUBTYPE_RGB24;
	//mt.subtype = MEDIASUBTYPE_RGB565;
	//mt.subtype = MEDIASUBTYPE_YV12;
	mt.formattype = FORMAT_VideoInfo;
	hr = g_pGrab->SetMediaType(&mt);
    if (FAILED(hr))
        return hr;

	if(g_pGrab)
		hr = g_pGrab->SetBufferSamples( FALSE );

	if(g_pGrab)
		hr = g_pGrab->SetOneShot( FALSE );

    CComQIPtr< ISampleGrabberCB, &IID_ISampleGrabberCB > pCB( &pCallback );
	if(g_pGrab)
		hr = g_pGrab->SetCallback( pCB, 1 );

    hr = g_pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);

    return hr;
}

void CloseInterfaces(int remove_device)
{
    if (g_pMC && g_psCurrent == Running) {
        g_pMC->Stop();
		KillTimer(ghApp, ID_UPDATETIMER);
	}

    g_psCurrent = Stopped;

    if (g_pME)
        g_pME->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, 0);

	if (g_SetAutoSwitch) {
		KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
		ZeroMemory(&tc,sizeof(tc));
		g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
			INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
			INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
			&tc,
			sizeof(tc));
		g_SetAutoSwitch = false;
	}

	if (remove_device) {
		for (int i = 0; i < g_nDeviceCount; i++)
			SAFE_RELEASE(g_pKPS[i]);
		for (i = 0; i < g_nDeviceCount; i++)
			SAFE_RELEASE(g_pSrcFilter[i]);
		for (i = 0; i < g_nDeviceCount; i++)
			SAFE_RELEASE(g_pVSC[i]);
		for (i = 0; i < g_nDeviceCount; i++)
			SAFE_RELEASE(g_pVdoDec[i]);
	}
    SAFE_RELEASE(g_pMC);
    SAFE_RELEASE(g_pME);
    SAFE_RELEASE(g_pNullRender);
    SAFE_RELEASE(g_pGrab);
    //SAFE_RELEASE(g_pXbar);
    SAFE_RELEASE(g_pBF);
    SAFE_RELEASE(g_pGraph);
    SAFE_RELEASE(g_pCapture);
}

void ChangePreviewState(int nShow)
{
	if (nShow)
		g_bLimitSize = true;
	else
		g_bLimitSize = false;
	return;
}

HRESULT HandleGraphEvent(void)
{
    LONG evCode, evParam1, evParam2;
    HRESULT hr=S_OK;

    while(SUCCEEDED(g_pME->GetEvent(&evCode, &evParam1, &evParam2, 0)))
    {
        hr = g_pME->FreeEventParams(evCode, evParam1, evParam2);
    }

    return hr;
}

HRESULT ChooseDevice(int device)
{
	if (g_nCurrentDevice == (device - MENU_DEVICE0))
		return S_OK;
	CloseInterfaces(0);
	g_nCurrentDevice = (device - MENU_DEVICE0);
//	UpdateWindow(ghApp);
	PostMessage(ghApp, WM_PAINT, 0, 0);
//	InvalidateRect(ghApp, NULL, TRUE);
//	g_nCount = 0;
	return CaptureVideo(1);
}

HRESULT ChooseSize(int size)
{
	if (g_nCurrentSize == (size - IDM_SIZE0))
		return S_OK;
	CloseInterfaces(0);
	g_nCurrentSize = (size - IDM_SIZE0);
	if (g_nCurrentSize == 1) {
		g_FrameWidth = CAP_W1;
		g_FrameHeight = CAP_H1;
	}
	else {
		g_FrameWidth = CAP_W;
		g_FrameHeight = CAP_H;
	}
//	g_nCount = 0;
	BmpInfHdr.biHeight = g_FrameHeight;
	BmpInfHdr.biWidth = g_FrameWidth;
	BmpInfHdr.biSizeImage = g_FrameHeight * g_FrameWidth * 3;
	RECT rect;
	int cxBorder, cyBorder, cy;
	cxBorder = GetSystemMetrics(SM_CXBORDER);
	cyBorder = GetSystemMetrics(SM_CYBORDER);
	cy = statusGetHeight() + cyBorder;
	SetRect(&rect, 0, 0, g_FrameWidth * 2, g_FrameHeight * 2 + cy);
	AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN, TRUE, 0);
	g_bLimitSize = false;
	SetWindowPos(ghApp, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
	GetClientRect(ghApp, &rect);
	MoveWindow(ghwndStatus, -cxBorder, rect.bottom - cy,
		rect.right + (2 * cxBorder), cy + cyBorder, TRUE);
	g_bLimitSize = true;

	return CaptureVideo(1);
}

BOOL APIENTRY GPIOPageProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BOOL bOutput[4];
	static int nCount;

	switch (message) {
		case WM_INITDIALOG: {
			KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW_S tc;
			ULONG ret = 0;
			char szText[128];

			ZeroMemory(&tc,sizeof(tc));
			g_pKPS[g_nCurrentDevice]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
				INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
				INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
				&tc,
				sizeof(tc),
				&ret);
			SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT1), ((tc.dwValue & 0x00000001) ? "On" : "Off"));
			SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT2), ((tc.dwValue & 0x00000002) ? "On" : "Off"));
			SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT3), ((tc.dwValue & 0x00000004) ? "On" : "Off"));
			SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT4), ((tc.dwValue & 0x00000008) ? "On" : "Off"));
			if (tc.dwValue & 0x00000010)
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_1_ON, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			else
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_1_OFF, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			if (tc.dwValue & 0x00000020)
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_2_ON, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			else
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_2_OFF, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			if (tc.dwValue & 0x00000040)
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_3_ON, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			else
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_3_OFF, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			if (tc.dwValue & 0x00000080)
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_4_ON, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			else
				SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_4_OFF, BM_SETCHECK, (WPARAM) BST_CHECKED, 0);
			bOutput[0] = (BOOL) (tc.dwValue & 0x00000010);
			bOutput[1] = (BOOL) (tc.dwValue & 0x00000020);
			bOutput[2] = (BOOL) (tc.dwValue & 0x00000040);
			bOutput[3] = (BOOL) (tc.dwValue & 0x00000080);
			nCount = 0;
			sprintf(szText, "GPIO Settings for %s", g_szDeviceText);
			SetWindowText(hDlg, szText);
			}
			return FALSE;

		case WM_TIMER: {
			KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW_S tc;

			ZeroMemory(&tc,sizeof(tc));
			if (nCount < 10) {
				tc.dwTarget = 0x000000f0;
				tc.dwValue = 0;
				tc.dwValue |= (g_dwTestOutput[nCount][0] << 0);
				tc.dwValue |= (g_dwTestOutput[nCount][1] << 1);
				tc.dwValue |= (g_dwTestOutput[nCount][2] << 2);
				tc.dwValue |= (g_dwTestOutput[nCount][3] << 3);
				//SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_1, BM_SETCHECK, (WPARAM) (tc.dwValue[4] ? BST_CHECKED : BST_UNCHECKED), 0);
				//SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_2, BM_SETCHECK, (WPARAM) (tc.dwValue[5] ? BST_CHECKED : BST_UNCHECKED), 0);
				//SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_3, BM_SETCHECK, (WPARAM) (tc.dwValue[6] ? BST_CHECKED : BST_UNCHECKED), 0);
				//SendDlgItemMessage(hDlg, IDC_GPIO_OUTPUT_4, BM_SETCHECK, (WPARAM) (tc.dwValue[7] ? BST_CHECKED : BST_UNCHECKED), 0);
				g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
					INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
					INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
					&tc,
					sizeof(tc));
				nCount++;
			}
			else {
				KillTimer(hDlg, 1);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_CLOSE), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_ALL), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_1), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_2), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_3), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_4), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_DEMO), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_1_ON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_2_ON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_3_ON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_4_ON), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_1_OFF), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_2_OFF), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_3_OFF), TRUE);
				EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_4_OFF), TRUE);
				tc.dwTarget = 0x000000f0;
				tc.dwValue = 0;
				tc.dwValue |= (bOutput[0] ? 0x1 : 0);
				tc.dwValue |= (bOutput[1] ? 0x2 : 0);
				tc.dwValue |= (bOutput[2] ? 0x4 : 0);
				tc.dwValue |= (bOutput[3] ? 0x8 : 0);
				g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
					INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
					INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
					&tc,
					sizeof(tc));
				nCount = 0;
			}
			}
			return TRUE;

		case WM_COMMAND: {
			int id = GET_WM_COMMAND_ID(wParam, lParam);
			KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW_S tc;

			switch (id) {
				case IDC_GPIO_OUTPUT_DEMO:
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_CLOSE), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_ALL), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_1), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_2), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_3), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_INPUT_4), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_DEMO), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_1_ON), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_2_ON), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_3_ON), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_4_ON), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_1_OFF), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_2_OFF), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_3_OFF), FALSE);
					EnableWindow(GetDlgItem(hDlg, IDC_GPIO_OUTPUT_4_OFF), FALSE);
					ZeroMemory(&tc,sizeof(tc));
					tc.dwTarget = 0x000000f0;
					tc.dwValue = 0;
					tc.dwValue |= (g_dwTestOutput[nCount][0] << 0);
					tc.dwValue |= (g_dwTestOutput[nCount][1] << 1);
					tc.dwValue |= (g_dwTestOutput[nCount][2] << 2);
					tc.dwValue |= (g_dwTestOutput[nCount][3] << 3);
					g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc));
					SetTimer(hDlg, 1, 500, NULL);
					nCount++;
					return TRUE;

				case IDC_GPIO_CLOSE:
					EndDialog(hDlg, 1);
					return TRUE;

				case IDC_GPIO_INPUT_ALL:
				case IDC_GPIO_INPUT_1:
				case IDC_GPIO_INPUT_2:
				case IDC_GPIO_INPUT_3:
				case IDC_GPIO_INPUT_4: {
					ULONG ret = 0;

					ZeroMemory(&tc,sizeof(tc));
					g_pKPS[g_nCurrentDevice]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc),
						&ret);
					if (IDC_GPIO_INPUT_ALL == id) {
						SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT1), ((tc.dwValue & 0x00000001) ? "On" : "Off"));
						SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT2), ((tc.dwValue & 0x00000002) ? "On" : "Off"));
						SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT3), ((tc.dwValue & 0x00000004) ? "On" : "Off"));
						SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT4), ((tc.dwValue & 0x00000008) ? "On" : "Off"));
					}
					else
						SetWindowText(GetDlgItem(hDlg, IDC_GPIO_INPUT_TEXT1 + id - IDC_GPIO_INPUT_1), ((tc.dwValue & (1 << (id - IDC_GPIO_INPUT_1))) ? "On" : "Off"));
					}
					return TRUE;

				case IDC_GPIO_OUTPUT_1_ON:
				case IDC_GPIO_OUTPUT_2_ON:
				case IDC_GPIO_OUTPUT_3_ON:
				case IDC_GPIO_OUTPUT_4_ON:
					ZeroMemory(&tc,sizeof(tc));
					bOutput[id - IDC_GPIO_OUTPUT_1_ON] = TRUE;
					tc.dwTarget = (1 << (id - IDC_GPIO_OUTPUT_1_ON + 4));
					tc.dwValue |= (1 << (id - IDC_GPIO_OUTPUT_1_ON));
					g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc));
					return TRUE;

				case IDC_GPIO_OUTPUT_1_OFF:
				case IDC_GPIO_OUTPUT_2_OFF:
				case IDC_GPIO_OUTPUT_3_OFF:
				case IDC_GPIO_OUTPUT_4_OFF:
					ZeroMemory(&tc,sizeof(tc));
					bOutput[id - IDC_GPIO_OUTPUT_1_OFF] = FALSE;
					tc.dwTarget = (1 << (id - IDC_GPIO_OUTPUT_1_OFF + 4));
					g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_MY_GPIO_NEW,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc));
					return TRUE;

				default:
					break;
			}
			}
			break;

		default:
			break;
	}
	return FALSE;
}

BOOL hexstrtobyte(char *temp, BYTE *value)
{
	int i = strlen(temp) - 1;
	//char szText[128];

	if (temp[i] >= 0x61 && temp[i] <= 0x66) {
		*value += (temp[i] - 0x57);
	}
	else if (temp[i] >= 0x41 && temp[i] <= 0x46) {
		*value += (temp[i] - 0x37);
	}
	else if (temp[i] >= 0x30 && temp[i] <= 0x39) {
		*value += (temp[i] - 0x30);
	}
	else
		return FALSE;
	//sprintf(szText, "%x %x", temp[i], *value);
	//OutputDebugString(szText);
	for (--i; i >= 0; i--) {
		if (temp[i] >= 97 && temp[i] <= 102) {
			//sprintf(szText, "%x %x", temp[i] - 87, 1 << (4 * (strlen(temp) - 1 - i)));
			//OutputDebugString(szText);
			*value += (temp[i] - 87) * (1 << (4 * (strlen(temp) - 1 - i)));
		}
		else if (temp[i] >= 65 && temp[i] <= 70) {
			//sprintf(szText, "%x %x", temp[i] - 55, 1 << (4 * (strlen(temp) - 1 - i)));
			//OutputDebugString(szText);
			*value += (temp[i] - 55) * (1 << (4 * (strlen(temp) - 1 - i)));
		}
		else if (temp[i] >= 48 && temp[i] <= 57) {
			//sprintf(szText, "%x %x", temp[i] - 48, 1 << (4 * (strlen(temp) - 1 - i)));
			//OutputDebugString(szText);
			*value += (temp[i] - 48) * (1 << (4 * (strlen(temp) - 1 - i)));
		}
		else
			return FALSE;
		//sprintf(szText, "%x %x", temp[i], *value);
		//OutputDebugString(szText);
	}
	return TRUE;
}

BOOL APIENTRY VPD2PageProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	char szText[128];
	KSPROPERTY_CUSTOMBT848_VPD_S tc;
	ULONG ret = 0;

	switch (message) {
		case WM_INITDIALOG: {
			sprintf(szText, "VPD2 Settings for %s", g_szDeviceText);
			SetWindowText(hDlg, szText);
			}
			return FALSE;

		case WM_COMMAND: {
			int id = GET_WM_COMMAND_ID(wParam, lParam);


			switch (id) {
				case IDC_VPD2_WRITE:
					ZeroMemory(&tc,sizeof(tc));
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT1), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[0])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT2), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[1])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT3), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[2])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT4), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[3])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT5), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[4])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT6), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[5])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT7), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[6])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT8), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[7])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					tc.bTarget = 0xff;
					if (S_OK == g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_VPD2,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc)))
						MessageBox(hDlg, "Ok!", "Write VPD2", MB_OK);
					else
						MessageBox(hDlg, "Failed!", "Write VPD2", MB_OK);
					return TRUE;

				case IDC_VPD2_CLOSE:
					EndDialog(hDlg, 1);
					return TRUE;

				case IDC_VPD2_READ: {
					ZeroMemory(&tc,sizeof(tc));
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT9), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[0])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT10), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[1])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT11), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[2])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT12), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[3])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT13), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[4])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT14), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[5])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT15), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[6])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					GetWindowText(GetDlgItem(hDlg, IDC_VPD2_EDIT16), szText, sizeof(szText));
					if (strlen(szText) > 2 || !hexstrtobyte(szText, &tc.bVPD[7])) {
						MessageBox(hDlg, "Value must be between 0 and FF!", "Error", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					tc.bTarget = 0xff;
					g_pKPS[g_nCurrentDevice]->Get(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_VPD2,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc),
						&ret);
					if (tc.bTarget == 0)
						MessageBox(hDlg, "Ok!", "Verify VPD2", MB_OK);
					else if (tc.bTarget == 0xfe)
						MessageBox(hDlg, "Failed!", "Verify VPD2", MB_OK);
					else
						MessageBox(hDlg, "Not supported!", "Verify VPD2", MB_OK);
					}
					return TRUE;

				default:
					break;
			}
			}
			break;

		default:
			break;
	}
	return FALSE;
}

static void PASCAL AppCommand (HWND hwnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
    int id = GET_WM_COMMAND_ID(wParam, lParam);
    switch (id)
    {
		case MENU_DEVICE0:
		case MENU_DEVICE1:
		case MENU_DEVICE2:
		case MENU_DEVICE3:
		case MENU_DEVICE4:
		case MENU_DEVICE5:
		case MENU_DEVICE6:
		case MENU_DEVICE7:
		case MENU_DEVICE8:
		case MENU_DEVICE9:
		case MENU_DEVICE10:
		case MENU_DEVICE11:
		case MENU_DEVICE12:
		case MENU_DEVICE13:
		case MENU_DEVICE14:
		case MENU_DEVICE15:
			if (FAILED(ChooseDevice(id)))
				PostMessage(hwnd, WM_CLOSE, 0, 0);
			break;
		case IDM_SIZE0:
		case IDM_SIZE1:
			if (FAILED(ChooseSize(id)))
				PostMessage(hwnd, WM_CLOSE, 0, 0);
			break;

		case IDM_GPIO:
			DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_GPIO), hwnd, (DLGPROC) GPIOPageProc);
			break;

		case IDM_VPD2:
			DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_VPD2), hwnd, (DLGPROC) VPD2PageProc);
			break;

		case IDM_SELECT_ALL: {
				HMENU hSubMenu = GetSubMenu(GetMenu(ghApp), 2);
				KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW_S tc;
				int i;

				ZeroMemory(&tc,sizeof(tc));
				for (i = 0; i < 4; i++) {
					g_ViewChannel[i] = true;
					tc.dwValue |= (1 << i);
					tc.dwTarget |= (1 << i);
				}
				g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW,
					INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
					INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
					&tc,
					sizeof(tc));
				if (g_psCurrent != Running) {
					g_dwChannelFrameCount[0] = 0.0;
					g_dwChannelFrameCount[1] = 0.0;
					g_dwChannelFrameCount[2] = 0.0;
					g_dwChannelFrameCount[3] = 0.0;
					g_IgnoreCount[0] = 0;
					g_IgnoreCount[1] = 0;
					g_IgnoreCount[2] = 0;
					g_IgnoreCount[3] = 0;
					g_fTotalByteReceived = 0.0;
					if (g_SetAutoDetect) {
						KSPROPERTY_CUSTOMBT848_AUTO_DETECT_S tc1;
						ZeroMemory(&tc1,sizeof(tc1));
						tc1.bAutoDetect = true;
						g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_DETECT,
							INSTANCE_DATA_OF_PROPERTY_PTR(&tc1),
							INSTANCE_DATA_OF_PROPERTY_SIZE(tc1),
							&tc1,
							sizeof(tc1));
					}
					g_pMC->Run();
					KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
					ZeroMemory(&tc,sizeof(tc));
					tc.bAutoSwitch = true;
					g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc));
					g_SetAutoSwitch = true;
					g_psCurrent = Running;
					g_lCapStartTime = timeGetTime();
					SetTimer(ghApp, ID_UPDATETIMER, 33, NULL);
				}
				PostMessage(hwnd, WM_PAINT, 0, 0);
			}
			break;

		case IDM_STOP_ALL: {
				HMENU hSubMenu = GetSubMenu(GetMenu(ghApp), 2);
				KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW_S tc;
				int i;

				ZeroMemory(&tc,sizeof(tc));
				for (i = 0; i < 4; i++) {
					g_ViewChannel[i] = false;
					tc.dwTarget |= (1 << i);
				}
				g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW,
					INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
					INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
					&tc,
					sizeof(tc));
				if (g_psCurrent == Running) {
					g_pMC->Stop();
					KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
					ZeroMemory(&tc,sizeof(tc));
					g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
						INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
						INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
						&tc,
						sizeof(tc));
					g_SetAutoSwitch = false;
					g_psCurrent = Stopped;
					KillTimer(ghApp, ID_UPDATETIMER);
					UpdateStatus(true);
				}
				PostMessage(hwnd, WM_PAINT, 0, 0);
			}
			break;

		case IDM_AUTO_DETECT: {
			HMENU hSubMenu = GetSubMenu(GetMenu(ghApp), 2);
			KSPROPERTY_CUSTOMBT848_AUTO_DETECT_S tc;
			ZeroMemory(&tc,sizeof(tc));
			tc.bAutoDetect = ((GetMenuState(hSubMenu, IDM_AUTO_DETECT, MF_BYCOMMAND) & MF_CHECKED) != 0);
			g_SetAutoDetect = tc.bAutoDetect = !tc.bAutoDetect;
			g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_DETECT,
				INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
				INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
				&tc,
				sizeof(tc));
			}
			break;

		case IDM_CHANNEL0:
		case IDM_CHANNEL1:
		case IDM_CHANNEL2:
		case IDM_CHANNEL3: {
				HMENU hSubMenu = GetSubMenu(GetMenu(ghApp), 2);
				KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW_S tc;
				BOOL bAtLeaseOneRunning;
				int i;

				ZeroMemory(&tc,sizeof(tc));
				for (i = 0; i < 4; i++) {
					if (i != (id - IDM_CHANNEL0))
						tc.dwValue |= ((GetMenuState(hSubMenu, (IDM_CHANNEL0 + i), MF_BYCOMMAND) & MF_CHECKED) == 0) ? 0 : (1 << i);
					else {
						if (g_ViewChannel[i] = ((GetMenuState(hSubMenu, (IDM_CHANNEL0 + i), MF_BYCOMMAND) & MF_CHECKED) == 0)) {
							g_IgnoreCount[i] = 0;
							tc.dwValue |= (1 << i);
						}
						tc.dwTarget = (1 << i);
					}
				}
				bAtLeaseOneRunning = (tc.dwValue != 0);
				g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_SET_CHANNEL_NEW,
					INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
					INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
					&tc,
					sizeof(tc));
				if (g_psCurrent == Running) {
					if (!bAtLeaseOneRunning) {
						g_pMC->Stop();
						KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
						ZeroMemory(&tc,sizeof(tc));
						g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
							INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
							INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
							&tc,
							sizeof(tc));
						g_SetAutoSwitch = false;
						g_psCurrent = Stopped;
						KillTimer(ghApp, ID_UPDATETIMER);
						UpdateStatus(true);
					}
				}
				else {
					if (bAtLeaseOneRunning) {
						g_dwChannelFrameCount[0] = 0.0;
						g_dwChannelFrameCount[1] = 0.0;
						g_dwChannelFrameCount[2] = 0.0;
						g_dwChannelFrameCount[3] = 0.0;
						g_fTotalByteReceived = 0.0;
#if 0
						g_IgnoreCount[0] = 0;
						g_IgnoreCount[1] = 0;
						g_IgnoreCount[2] = 0;
						g_IgnoreCount[3] = 0;
						if (g_SetAutoDetect) {
							KSPROPERTY_CUSTOMBT848_AUTO_DETECT_S tc1;
							ZeroMemory(&tc1,sizeof(tc1));
							tc1.bAutoDetect = true;
							g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_DETECT,
								INSTANCE_DATA_OF_PROPERTY_PTR(&tc1),
								INSTANCE_DATA_OF_PROPERTY_SIZE(tc1),
								&tc1,
								sizeof(tc1));
						}
#endif
						g_pMC->Run();
						KSPROPERTY_CUSTOMBT848_AUTO_SWITCH_S tc;
						ZeroMemory(&tc,sizeof(tc));
						tc.bAutoSwitch = true;
						g_pKPS[g_nCurrentDevice]->Set(PROPSETID_CUSTOMBT848, KSPROPERTY_CUSTOMBT848_AUTO_SWITCH,
							INSTANCE_DATA_OF_PROPERTY_PTR(&tc),
							INSTANCE_DATA_OF_PROPERTY_SIZE(tc),
							&tc,
							sizeof(tc));
						g_SetAutoSwitch = true;
						g_psCurrent = Running;
						g_lCapStartTime = timeGetTime();
						SetTimer(ghApp, ID_UPDATETIMER, 33, NULL);
					}
				}
				PostMessage(hwnd, WM_PAINT, 0, 0);
			}
			break;

		default:
			break;
	}
}

LRESULT CALLBACK WndMainProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
		case WM_TIMER:
			if (wParam == ID_UPDATETIMER) {
				UpdateStatus(FALSE);
			}
			break;

        case WM_GRAPHNOTIFY:
            HandleGraphEvent();
            break;
		case WM_PAINT: {
				PAINTSTRUCT ps;
				HDC hDC = BeginPaint(hwnd, &ps);

				InvalidateRect(hwnd, NULL, TRUE);
				EndPaint(hwnd, &ps);
			}
			break;

		case WM_COMMAND:
			AppCommand (hwnd, message, wParam, lParam);
			break;

		case WM_INITMENUPOPUP:
            if (GetSubMenu(GetMenu(ghApp), 0) == (HMENU)wParam)
                CheckDeviceMenu((HMENU)wParam, 0);
			else if (GetSubMenu(GetMenu(ghApp), 1) == (HMENU)wParam) {
				CheckMenuItem((HMENU)wParam, IDM_SIZE0, (g_nCurrentSize == 0) ? MF_CHECKED : MF_UNCHECKED);
				CheckMenuItem((HMENU)wParam, IDM_SIZE1, (g_nCurrentSize == 1) ? MF_CHECKED : MF_UNCHECKED);
			}
			else if (GetSubMenu(GetMenu(ghApp), 2) == (HMENU)wParam)
				CheckChannelMenu((HMENU)wParam, 0, false);
            break;

        //case WM_WINDOWPOSCHANGED:
		case WM_SIZE:
            ChangePreviewState(! (IsIconic(hwnd)));
            break;
#if 1
		case WM_GETMINMAXINFO:
			if (g_bLimitSize) {
				MINMAXINFO *lppt;
				RECT rect;

				lppt = (MINMAXINFO *) lParam;
				GetWindowRect(hwnd, &rect);
				lppt->ptMaxTrackSize.x = lppt->ptMinTrackSize.x = (rect.right - rect.left);
				lppt->ptMaxTrackSize.y = lppt->ptMinTrackSize.y = (rect.bottom - rect.top);
			}
			break;
#endif
        case WM_CLOSE:
            ShowWindow(ghApp, SW_HIDE);
            CloseInterfaces(1);
            break;

        case WM_CREATE:
			//BmpInfHdr.biBitCount = 24;
			BmpInfHdr.biBitCount = 16;
			BmpInfHdr.biClrImportant = 0;
			BmpInfHdr.biClrUsed = 0;
			BmpInfHdr.biCompression = BI_RGB;
			//BmpInfHdr.biCompression = BI_BITFIELDS;
			BmpInfHdr.biHeight = g_FrameHeight;
			BmpInfHdr.biWidth = g_FrameWidth;
			BmpInfHdr.biPlanes = 1;
			BmpInfHdr.biSize = sizeof(BITMAPINFOHEADER);
			//BmpInfHdr.biSizeImage = g_FrameWidth * g_FrameHeight * 3;
			BmpInfHdr.biSizeImage = g_FrameWidth * g_FrameHeight * 2;
			BmpInfHdr.biXPelsPerMeter = 0;
			BmpInfHdr.biYPelsPerMeter = 0;

			hDrawDib = DrawDibOpen();
			break;

        case WM_DESTROY:
			DrawDibClose(hDrawDib);
			DeleteObject(g_hBrush);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc (hwnd , message, wParam, lParam);
}


int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hInstP, LPTSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    WNDCLASS wc;

    if(FAILED(CoInitialize(NULL)))
    {
        exit(1);
    }

	g_hBrush = CreateSolidBrush(RGB(0, 0, 198));
    ZeroMemory(&wc, sizeof wc);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    wc.lpfnWndProc   = WndMainProc;
    wc.hInstance     = hInstance;
	wc.lpszMenuName   = MAKEINTATOM(IDR_MENU1);
    wc.lpszClassName = CLASSNAME;
	wc.hbrBackground = g_hBrush;
    //wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_VIDPREVIEW));
    if(!RegisterClass(&wc))
    {
        CoUninitialize();
        exit(1);
    }

    ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME,
                    (WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN) & (~WS_MAXIMIZEBOX),
                    CW_USEDEFAULT, CW_USEDEFAULT,
					CW_USEDEFAULT, CW_USEDEFAULT,
					//(CAP_W * 2 + 8), (CAP_H * 2 + 48),
                    0, 0, hInstance, 0);

    if(ghApp)
    {
        HRESULT hr;
		g_hInstance = hInstance;

		statusInit(hInstance, hInstP);
		ghwndStatus = CreateWindowEx(0,
			szStatusClass,
			NULL,
			WS_CHILD|WS_BORDER|WS_VISIBLE|WS_CLIPSIBLINGS,
			0, 0,
			0, 0,
			ghApp,
			NULL,
			hInstance,
			NULL);
		if (ghwndStatus == NULL) {
			CoUninitialize();
			DestroyWindow(ghApp);
			exit(1);
		}

        hr = CaptureVideo(0);
        if (hr != S_OK)
        {
            CloseInterfaces(1);
            DestroyWindow(ghApp);
        }
        else
        {
			RECT rect;
			int cxBorder, cyBorder, cy;

			cxBorder = GetSystemMetrics(SM_CXBORDER);
			cyBorder = GetSystemMetrics(SM_CYBORDER);
			cy = statusGetHeight() + cyBorder;
			SetRect(&rect, 0, 0, g_FrameWidth * 2, g_FrameHeight * 2 + cy);
			AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN, TRUE, 0);
			SetWindowPos(ghApp, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
			GetClientRect(ghApp, &rect);
			MoveWindow(ghwndStatus, -cxBorder, rect.bottom - cy,
				rect.right + (2 * cxBorder), cy + cyBorder, TRUE);
			g_bLimitSize = true;
            ShowWindow(ghApp, nCmdShow);
        }

        while(GetMessage(&msg,NULL,0,0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    CoUninitialize();

    return msg.wParam;
}
