#include "FrameGrabber.h"

DWORD WINAPI _FGThread(LPVOID lpParameter)
{
	LPFGSTRUCT			lpFgs	= (LPFGSTRUCT)lpParameter;		
	LPRWLOCK			lpRwl	= NULL;
	LPCRITICAL_SECTION	lpCs	= NULL;
	VIHANDLE			hVi		= VICreate();
	MSG					msg;
	UINT_PTR			uiTimerID;

	if(lpFgs == NULL)	
		ExitThread(0);
	
	lpFgs->bThrdIsRunning = TRUE;

	lpFgs->wvfFrame.uFrameSize		= 0;
	lpFgs->wvfFrame.lpFrame			= NULL;
	lpFgs->wvfFrame.uBufSize		= 0;
	lpFgs->wvfFrame.iCompression	= WVF_COMPRESSION_NONE;
	lpFgs->bIsSetup					= FALSE; 
	
	InitializeRWLock(&(lpFgs->rwl));	
		
	lpRwl = &(lpFgs->rwl);

	SetEvent(lpFgs->hJobsDone);
	
	
	while(GetMessage(&msg, NULL, 0, 0))
	{
		switch(msg.message)
		{
		case FG_SETUP:
			{
				LPFGSETUP lpFgstp = (LPFGSETUP)msg.lParam;
				INT i;
				lpFgs->lResult = FALSE;
				
				if(lpFgs->bIsSetup)
				{
					VIStopDevice(lpFgs->iDeviceID, hVi);
					WVFFree(&(lpFgs->wvfFrame));					
				}
				i = VIListDevices(hVi);

				if(VISetupDevice(lpFgstp->iDeviceID, lpFgstp->iWidth, lpFgstp->iHeight, hVi))
				{
					lpFgs->nFrameCnt = 0;					
					lpFgs->iDeviceID				= lpFgstp->iDeviceID;
					lpFgs->wvfFrame.uFrameSize		= VIGetSize(lpFgstp->iDeviceID, hVi);
					lpFgs->wvfFrame.lpFrame			= (LPBYTE)HeapAlloc(g_hProcHeap, 0, lpFgs->wvfFrame.uFrameSize);
					lpFgs->wvfFrame.uBufSize		= lpFgs->wvfFrame.uFrameSize;
					lpFgs->wvfFrame.iWidth			= VIGetWidth(lpFgstp->iDeviceID, hVi);
					lpFgs->wvfFrame.iCompression	= WVF_COMPRESSION_NONE;
					lpFgs->wvfFrame.iHeight			= VIGetHeight(lpFgstp->iDeviceID, hVi);
					lpFgs->bIsSetup					= TRUE;			
					lpFgs->lResult					= TRUE;
					uiTimerID = SetTimer(NULL, 0, (1000/30), NULL);
					SendMessage(NULL, WM_TIMER, 0, 0);
				}
				else
				{
					lpFgs->bIsSetup = FALSE;
					lpFgs->lResult	= FALSE;					
				}
				
				SetEvent(lpFgs->hJobsDone);
			}
			break;

		case FG_STOP:
			{
				if(lpFgs->bIsSetup)
				{
					lpFgs->bIsSetup = FALSE;
					KillTimer(NULL, uiTimerID);
					VIStopDevice(lpFgs->iDeviceID, hVi);
					WVFFree(&(lpFgs->wvfFrame));					
					lpFgs->lResult	= TRUE;					
				}					
				else
					lpFgs->lResult = FALSE;				

				SetEvent(lpFgs->hJobsDone);
			}
			break;

		case FG_ISRUNNING:
			lpFgs->lResult = lpFgs->bIsSetup;
			SetEvent(lpFgs->hJobsDone);
			break;

		case FG_LISTDEV:
			lpFgs->lResult = VIListDevices(hVi);
			SetEvent(lpFgs->hJobsDone);
			break;

		case FG_GETNAME:
			VIGetDeviceName(msg.wParam, (LPWSTR)(((LPBUFFER)msg.lParam)->lpBuffer), 
				((LPBUFFER)msg.lParam)->cSize, hVi);
			SetEvent(lpFgs->hJobsDone);
			break;

		case FG_SHOWSETTINGS:
			VIDefSetupDevice(msg.wParam, hVi);
			VIShowSettings(msg.wParam, hVi);
			VIStopDevice(msg.wParam, hVi);
			SetEvent(lpFgs->hJobsDone);
			break;

		case WM_TIMER:
			if(lpFgs->bIsSetup)
			{				
				WriterEnter(lpRwl);
				VIGrabFrame(lpFgs->iDeviceID, lpFgs->wvfFrame.lpFrame, hVi);
				lpFgs->nFrameCnt ++;
				WriterLeave(lpRwl);
			}			
			break;			
		}		
	}

	if(lpFgs->bIsSetup)
	{
		KillTimer(NULL, FG_TIMERID);
		VIStopDevice(lpFgs->iDeviceID, hVi);
		WVFFree(&(lpFgs->wvfFrame));
		lpFgs->bIsSetup = FALSE;
	}	
	
	DeleteRWLock(lpRwl);	
	VIClose(hVi);
	ExitThread(0);
}

LPFGSTRUCT CreateFG()
{
	ULONG			uFrameSize		= 0;
	LPFGSTRUCT	lpFgs			= (LPFGSTRUCT)HeapAlloc(g_hProcHeap, 0, sizeof(FGSTRUCT));	
	lpFgs->hJobsDone	= CreateEvent(NULL, TRUE, FALSE, NULL);
	

	if(lpFgs->hJobsDone == NULL)
	{
		HeapFree(g_hProcHeap, 0, lpFgs);
		return NULL;
	}
	

	lpFgs->hThread = CreateThread(NULL, 0, _FGThread, (LPVOID)lpFgs, 0, &(lpFgs->dwThreadID));
	if(lpFgs->hThread == NULL)
	{
		CloseHandle(lpFgs->hJobsDone);
		HeapFree(g_hProcHeap, 0, lpFgs);		
		return NULL;
	}
	InitializeCriticalSection(&lpFgs->csMsgLoop);

	WaitForSingleObject(lpFgs->hJobsDone, INFINITE);
	if(!(lpFgs->bThrdIsRunning))
	{
		CloseHandle(lpFgs->hThread);
		CloseHandle(lpFgs->hJobsDone);
		HeapFree(g_hProcHeap, 0, lpFgs);
		return NULL;
	}
	return lpFgs;
}

BOOL FGStart(LPFGSTRUCT lpFgs, INT iDeviceID, INT iW, INT iH)
{
	FGSETUP fgstp;

	fgstp.iDeviceID = iDeviceID;
	fgstp.iWidth	= iW;
	fgstp.iHeight	= iH;	
	
	return (BOOL) SendFGMessage(lpFgs, FG_SETUP, 0, (LPARAM)&fgstp);
}


VOID DeleteFG(LPFGSTRUCT lpFgs)
{

	if(lpFgs->bThrdIsRunning)
	{		
		lpFgs->bThrdIsRunning = FALSE;		
		PostThreadMessage(lpFgs->dwThreadID, WM_QUIT, 0, 0);
		WaitForSingleObject(lpFgs->hThread, INFINITE);
		CloseHandle(lpFgs->hThread);
	}
	CloseHandle(lpFgs->hJobsDone);	
	DeleteCriticalSection(&lpFgs->csMsgLoop);

	HeapFree(g_hProcHeap, 0, lpFgs);
}


LRESULT SendFGMessage(LPFGSTRUCT lpFgs, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult;

	EnterCriticalSection(&lpFgs->csMsgLoop);
	ResetEvent(lpFgs->hJobsDone);
	PostThreadMessage(lpFgs->dwThreadID, uMsg, wParam, lParam);
	WaitForSingleObject(lpFgs->hJobsDone, INFINITE);
	lResult = lpFgs->lResult;
	LeaveCriticalSection(&lpFgs->csMsgLoop);

	return lResult;
}

VOID FGGetDeviceName(LPFGSTRUCT lpFgs, INT iDeviceID, LPWSTR lpszBuffer, INT cBufferSize)
{
	BUFFER	buff;

	buff.lpBuffer	= lpszBuffer;
	buff.cSize		= cBufferSize;
	SendFGMessage(lpFgs, FG_GETNAME, (WPARAM)iDeviceID, (WPARAM)&buff);
}