// Video.cpp: implementation of the CVideo class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FrameRate.h"
#include "Video.h"
#include "fwxVideo.h" 
#include "fwx1394API.h"
#include "fwxDCam.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#ifdef _PROFILE
	ULONG nFramesProcessed;
#endif

BYTE *pFrame[50];
BYTE *pData;
#define IMAGESIZE 153600 //320x240
//614400 //640x480
#define TOTAL 50


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CVideo::CVideo()
{
    pDCam = NULL;
	pVideoFrame = NULL;
	
	nFrameSize[0] = FRAMESIZE_MODE0;
	nFrameSize[1] = FRAMESIZE_MODE1;
	nFrameSize[2] = FRAMESIZE_MODE2;
	nFrameSize[3] = FRAMESIZE_MODE3;
	nFrameSize[4] = FRAMESIZE_MODE4;
	nFrameSize[5] = FRAMESIZE_MODE5;
	nVideoMode = 1;
	
	pFrameData = NULL;
	hDeviceRemovedEvent = NULL;
	bInitialized = FALSE;
	bPaused = FALSE;
	bReallocateIsoResources = FALSE;
	pVideoThread = NULL;

	hStopVideoThreadEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	for (UINT i = 0; i < ISO_VIDEO_FRAMES; i++)
		hFrameCompleteEvent[i] = NULL;

}

CVideo::~CVideo()
{

}

BOOL CVideo::RunCamera()
{
    //initialize video frame and camera
    FILE *fp;
	char name[] = "stream.yuv";

	bInitialized = FALSE;
	
	//open the camera and initialize
	GUID.GUIDHi = 0x08004602;
	GUID.GUIDLo = 0x000302BA;

	fwxInitialize();

	if ((pDCam = (CfwxDCam *) DCam.Open(GUID.GUIDHi, GUID.GUIDLo)))
	{
		hDeviceRemovedEvent = DCam.GetDeviceRemovedEvent();

		//setup the default type of video
		if (DCam.SetCameraPower(TRUE) && DCam.SetVFormat(0) &&
			DCam.SetVMode(nVideoMode) && DCam.SetVFrameRate(4) &&
			DCam.SetIsoChannelSpeed(0, 0))
		{
			//allocate the video frame buffer for mode 0
			pVideoFrame = new BYTE[nFrameSize[nVideoMode]];

			//allocate the Iso resources
			if (pVideoFrame && DCam.AllocateIsoResources(ISO_VIDEO_FRAMES, &pFrameData))
			{
				//get the handles for the frame complete named events
				for (UINT i = 0; i < ISO_VIDEO_FRAMES; i++)
					hFrameCompleteEvent[i] = DCam.GetFrameCompleteEvent(i);
			}
			else
			{
                DCam.Close();
		        printf("Error trying to allocate Iso resources.");
			}
		}
		else
		{
            DCam.Close();
			printf("Error trying to initilaize camera settings.");
		}
	}
	else
	{
        DCam.Close();
		printf("Open camera device error.");
	}

    for (int k = 0; k < 50; k++)
    {
        pFrame[k] = new BYTE[IMAGESIZE];
    }

    //ChangeVMode(3);
    //start taking images
    OnStart();
    //ChangeVMode(3);
    
    printf("the camera is taking images.");

    //close the camera and stop video
    nShutDown = 1;
    Sleep(2000);

	//stop the threads
	SetEvent(hStopVideoThreadEvent);
	
	//make sure the video was stopped
	OnStop();
    printf("the camera has stopped.");
    fp = fopen(name, "wb");

   for (int l = 0; l < TOTAL; l++)
    {
	    fwrite(pFrame[l], IMAGESIZE, 1, fp);
        delete []pFrame[l];
        printf("%d-", l);
    }
   fclose(fp);

    getchar();

	//make sure the iso resources are free
	DCam.FreeIsoResources();

    return TRUE;

}

BOOL CVideo::StartVideo()
{
	UINT nFrames = ISO_VIDEO_FRAMES;
	//videomode is set to 3 (i.e. 640 x 480)
    if (nVideoMode >= 2)
		nFrames = 1;

        if (bReallocateIsoResources)
	{
		//the video mode or framerate changed
		//we have to reallocate the correct amount of iso resources
		if (!StopVideo())
			return FALSE;

		if (!DCam.FreeIsoResources())
		{
			printf("Error trying to free Iso resources.");
			return FALSE;
		}
		else
		{
			for (UINT i = 0; i <ISO_VIDEO_FRAMES; i++)
				hFrameCompleteEvent[i] = NULL;
		}

		if (!DCam.AllocateIsoResources(nFrames, &pFrameData))
		{
			printf("error trying to allocate Iso resources.");
			return FALSE;
		}
		else
		{
			for (UINT i = 0; i < nFrames; i++)
				hFrameCompleteEvent[i] = DCam.GetFrameCompleteEvent(0);
		}

		bReallocateIsoResources = FALSE;
	}

	if (!DCam.SetIsoTransmit(TRUE))
	{
		printf("Error trying to start the camera transmission.");
		return FALSE;
	}

    Sleep(2000);

	//start the video thread
	if (pVideoThread == NULL)
	{
        printf("start the video thread.");
		ResetEvent(hStopVideoThreadEvent);
		VideoThreadArg.pFrameData = pFrameData;
		VideoThreadArg.pVideoFrame = pVideoFrame;
		VideoThreadArg.nFrameSize = nFrameSize[nVideoMode];
		VideoThreadArg.nFrames = nFrames;
		VideoThreadArg.nVideoMode = nVideoMode;
		VideoThreadArg.hStopVideoEvent = hStopVideoThreadEvent;
		for (int i = 0; i < ISO_VIDEO_FRAMES; i++)
			VideoThreadArg.hFrameCompleteEvent[i] = hFrameCompleteEvent[i];

#ifdef _PROFILE
		nFramesProcessed = 0;
		StartTick = ::GetTickCount();
#endif
		nShutDown = 0;
        ProcessVideoThread(&VideoThreadArg);
        StopTick = ::GetTickCount();

	}
	return TRUE;
}


BOOL CVideo::StopVideo()
{
    // Stop the video thread
	if (pVideoThread)
	{
		SetEvent(hStopVideoThreadEvent);
#ifdef _PROFILE
		StopTick = ::GetTickCount();
#endif
		Sleep(20);
		pVideoThread = NULL;
	}

	// Stop the camera's transmission
	if( pVideoFrame == NULL )
		return FALSE;
	if (!DCam.SetIsoTransmit(FALSE))
	{
		printf("Error trying to stop the camera transmission.");
		return FALSE;
	}

	return TRUE;
}


void CVideo::OnStop()
{
    if (StopVideo())
		bPaused = FALSE;

#ifdef _PROFILE
	float fSeconds = (float) (StopTick - StartTick) / 1000;
	CString szMsg;

	szMsg.Format("Driver frames processed:%d\nDriver frame rate:%dfps\nApp frames processed:%d\nApp frame rate:%d fps", 
		DCam.GetDriverFramesReceived(),
		(int) (DCam.GetDriverFramesReceived() / fSeconds + 0.5f), nFramesProcessed, 
		(int) (nFramesProcessed / fSeconds + 0.5f));
	printf("%s", szMsg);

	nFramesProcessed = 0;
#endif
}

UINT CVideo::ProcessVideoThread(LPVOID pParam)
{
    UINT i;
    int j = 0;
	SVideoThreadArg *pArg = (SVideoThreadArg *) pParam;

	if ((pArg->pFrameData == NULL) || (pArg->pVideoFrame == NULL) || 
		(pArg->nFrameSize == 0) || (pArg->nFrames == 0))
		AfxEndThread(1);


	while(j < 50)
	{
		// Loop through the number of frames
		for(i=0; i < pArg->nFrames && j < 50; i++)
		{
			// Check to see if the thread should stop
			if (WaitForSingleObject(pArg->hStopVideoEvent, 0) == WAIT_OBJECT_0)
				AfxEndThread(0);

			pData = ((BYTE *)pArg->pFrameData) + (i * (pArg->nFrameSize));

			// If we have an event handle and there are multiple frames, then
			// wait until the frame is ready to be read.  If we timeout, then
			// just go to next frame
			if( i > 10 ) 
				AfxEndThread(0);

			if (( pArg->hFrameCompleteEvent[i] != NULL) && (pArg->nFrames > 1))
				if (WaitForSingleObject(pArg->hFrameCompleteEvent[i], 10000) != WAIT_OBJECT_0)
					continue;

			// Process the frame
			//YUV file
            if (pArg->nVideoMode == 1)
			    memcpy(pFrame[j++], pData, pArg->nFrameSize);
			
			if (!ResetEvent( pArg->hFrameCompleteEvent[i] ))  
			{ 
				AfxEndThread(0);
			}


#ifdef _PROFILE
			nFramesProcessed++;
#endif
		}
	}
	return 0;
}

void CVideo::OnStart()
{
    if (bPaused)
	{
		if (pVideoThread)
		{
			pVideoThread->ResumeThread();
			bPaused = FALSE;
		}
	}
	else
	{
		if (StartVideo())
			bPaused = FALSE;
	}
}
	
BOOL CVideo::ChangeVMode(int nMode)
{
    // See if the camera is currently transmitting
	int xmit = DCam.GetIsoTransmit();
	if (xmit < 0)
	{
		printf("Error reading camera while changing the video mode");
		return FALSE;	// An error occurred
	}

	if (xmit > 0)	// If the camera is transmitting
	{
		// Stop the video transmission
		if (!StopVideo())
		{
			return FALSE;
		}
	}

	// Change the camera video mode
	if (!DCam.SetVMode(nMode))
	{
		printf("Could not change the video mode");
		return FALSE;
	}
	else
	{
		bReallocateIsoResources = TRUE;
		nVideoMode = nMode;
	}

	// Delete the current video frame buffer
	delete []pVideoFrame;
	// Allocate a new buffer to hold the new frame size
	pVideoFrame = new BYTE[nFrameSize[nVideoMode]];

	return TRUE;
}

