// OpenCV headers.
#include "opencv/cv.h"
#include "opencv/cvwimage.h"
#include "opencv/cxcore.h"
#include "opencv/highgui.h"
// Note that the 3 following headers do not build in C mode.
#include "opencv/cvaux.h"
#include "opencv/cxmisc.h"
#include "opencv/ml.h"

// Useful third-party functions.
#include "Utils.h"

// It is important to get a good quality for the captured videos, but the quality of 
// the images does not matter so much for the movement detection, all that we want is 
// to know whether there were changes between the current and previous images.
// Therefore, we count significant changes (a change >64 for a value in [0;255]) 
// of the red, green or blue components of 1 pixel among 4 and when the count reaches
// a threshold depending on the width and height of the images, we return that there
// was a movement.
int MovementDetection(IplImage* previmage, IplImage* image)
{
	int r = 0, g = 0, b = 0;
	int i = 0, j = 0, index = 0;
	unsigned int count = 0; 
	unsigned char* prevdata = reinterpret_cast<unsigned char*>(previmage->imageData);
	unsigned char* data = reinterpret_cast<unsigned char*>(image->imageData);

	for (i = 0; i < image->height; i += 4)
	{
		for (j = 0; j < image->width; j += 4)
		{
			index = 3*(j+image->width*i);
			b = data[0+index]-prevdata[0+index];
			g = data[1+index]-prevdata[1+index];
			r = data[2+index]-prevdata[2+index];
			if ((abs(r) > 64)||(abs(g) > 64)||(abs(b) > 64)) count++; 
		}
	}	

	if (count > (unsigned int)image->width*(unsigned int)image->height/(1024*4*4)) 
		return 1; 
	else 
		return 0;
}

int main(int argc, char* argv[])
{
	int c = 0; // Will contain the ASCII code of the key pressed.
	int bCapture = 0; // Boolean indicating if we are currently capturing video.
	int bMovement = 0; // Boolean indicating if a movement has just been detected.
	char videofilename[256]; // Path to the video file.
	CvVideoWriter* videofile = NULL; // Pointer representing the video file.
	CvCapture* webcam0 = NULL; // Pointer representing the webcam.
	IplImage* image = NULL; // Pointer representing the current image from the webcam.
	IplImage* previmage = NULL; // Pointer representing the previous image from the webcam.
	CHRONO chrono; // Chronometer used to stop capture after a defined timeout
	// when a movement has been detected. If a movement is detected again before the timeout,
	// the chronometer will be reset. 

	// If this program is called from the command prompt with an argument, it will use it 
	// to choose the webcam to open if there are several webcams connected to the 
	// computer. If no argument is specified, it will open the first found.
	// e.g. if you double-click on the program it opens the first found, if you type
	// WebcamMonitoring 1
	// on the command prompt, it opens the first,
	// WebcamMonitoring 2
	// it opens the second...
	if (argc == 2) webcam0 = cvCreateCameraCapture(atoi(argv[1]));
	else webcam0 = cvCreateCameraCapture(0);

	// Set the desired resolution for the webcam.
	cvSetCaptureProperty(webcam0, CV_CAP_PROP_FRAME_WIDTH, 800);
	cvSetCaptureProperty(webcam0, CV_CAP_PROP_FRAME_HEIGHT, 600);

	// Take a first image from the webcam in image, then copy it in previmage.
	// Note that an image returned by cvQueryFrame() should not be previously
	// allocated nor deallocated, and an image returned by cvCloneImage() should
	// not be previously allocated but should be deallocated at the end using 
	// cvReleaseImage().
	image = cvQueryFrame(webcam0);
	previmage = cvCloneImage(image);

	// Create a window that will show the webcam images.
	cvNamedWindow("Webcam");

	for (;;)
	{
		image = cvQueryFrame(webcam0);
		cvShowImage("Webcam", image);

		// Wait 100 ms to check if the ESC key (ASCII code 27) has been pressed.
		// In this case stops the loop.
		// Note that the cvShowImage() might not work properly in a loop if cvWaitKey()
		// is not called with a parameter !=0 and that the window must have the focus
		// when you press a key to be detected by cvWaitKey().
		c = cvWaitKey(100);
		if ((char)c == 27) break;

		// Compare the previous image with the current image from the webcam.
		// If significant changes are detected, we will consider that something is
		// moving and set bMovement to 1, otherwise to 0.
		bMovement = MovementDetection(previmage, image);

		// If a movement is detected and we were not already capturing, we create
		// a new video file with the dateand time in its name. We indicate in the call
		// of cvCreateVideoWriter() that the codec to use is the one with the FOURCC
		// (FOUR-Character Code) WMV2 (which is most of the time available on 
		// Windows), the frame-rate at which the video should be played is 10 img/s,
		// the resolution, and that it is a color video.
		// Note that a typical failure is a codec problem, change it if the video
		// cannot be created (see e.g. 
		// http://msdn.microsoft.com/en-us/library/bb970509(v=VS.85).aspx
		// for a list of possible FOURCC codes) or change the framerate in case it
		// is not supported by the codec (e.g. 30 or 60 are typical values for some
		// of them). Another problem is that the frame-rate at which the video should
		// be played is difficult to set to correspond with the captured frame-rate.
		if (!bCapture && bMovement) 
		{
			sprintf(videofilename, "vid_%s.wmv", strtime_fns());
			videofile = cvCreateVideoWriter(videofilename, 
				CV_FOURCC('W','M','V','2'), 
				10, 
				cvSize((int)cvGetCaptureProperty(webcam0, CV_CAP_PROP_FRAME_WIDTH),
				(int)cvGetCaptureProperty(webcam0, CV_CAP_PROP_FRAME_HEIGHT)), 
				1);
			bCapture = 1;
			printf("Capture started\n");
		}
		// If a movement has been detected, the timer is reset.
		if (bMovement) {StartChrono(&chrono);printf("1\n");} else printf("0\n");
		if (bCapture) 
		{
			// Add the current webcam image in the video file.
			cvWriteFrame(videofile, image);
			// If the timer reaches 30 s, we stop the capture.
			// It means that there was no movement for 30 s, otherwise the timer
			// whould have been reset.
			if ((GetTimeElapsedChronoQuick(&chrono) > 30)) 
			{
				StopChronoQuick(&chrono);
				cvReleaseVideoWriter(&videofile);
				bCapture = 0;
				printf("Capture stopped\n");
			}
		}

		cvCopy(image, previmage, NULL);
	}

	if (bCapture) 
	{
		StopChronoQuick(&chrono);
		cvReleaseVideoWriter(&videofile);
	}

	cvDestroyWindow("Webcam");

	cvReleaseImage(&previmage);

	cvReleaseCapture(&webcam0);

	return EXIT_SUCCESS;
}

// References:
// Google wiki OpenCV
// Google MSDN library, e.g. http://msdn.microsoft.com/en-us/library/ms644904(v=VS.85).aspx
// Google wiki ASCII code
// Google wiki codec four character code, e.g. http://msdn.microsoft.com/en-us/library/bb970509(v=VS.85).aspx
