// Tracking.cpp
// Chris Pocock 2014

#include "Tracking.h"
//#include <ctime>

Tracking::Tracking(String videoName, std::ofstream* output, bool processing) {

	// Variables from constructor
	video = videoName;
	outputstream = output;
	showProcessing = processing;

	// Default class variables
	cnt = 2;
	resetTimer = 50;

	// Trackbar upper limits
	max_resetTimer = 1000;
}

Tracking::~Tracking() {
}

int Tracking::track(){

	double startTime = clock();

	// Attempt to open video
	capture.open(video);
	if(!capture.isOpened()){
		std::cout << "Failed to load video" << std::endl;
		return -1;
	}

	// Read in 3 video frames
	capture.read(prev_frame);
	result = prev_frame;
	capture.read(current_frame);
	capture.read(next_frame);

	// Convert each frame to grayscale
	cvtColor(current_frame, current_frame, CV_RGB2GRAY);
	cvtColor(prev_frame, prev_frame, CV_RGB2GRAY);
	cvtColor(next_frame, next_frame, CV_RGB2GRAY);

	// Instantiate masker, dilator, eroder, thresholder and detector
	Masking *masker = new Masking (current_frame);
	Dilation *dilator = new Dilation();
	Erosion *eroder = new Erosion();
	Thresholding *thresholder = new Thresholding();
	MotionDetector *detector  = new MotionDetector(current_frame.cols, current_frame.rows);

	// If user wants to view processing steps
	if (showProcessing){
		setupUi();
		dilator->setupUi();
		eroder->setupUi();
		masker->setupUi();
		thresholder->setupUi();
		detector->setupUi();
	}

	// While there are frames left to process
	while(capture.get(CV_CAP_PROP_POS_FRAMES)<capture.get(CV_CAP_PROP_FRAME_COUNT)-1){

		 // Store n-1, n, n+1
		prev_frame = current_frame;
		current_frame = next_frame;
		capture.read(next_frame);
		result = next_frame;

		cvtColor(next_frame, next_frame, CV_RGB2GRAY); // Greyscale n+1 (other 2 already been converted)

		// Calculate two sets of differences and the union of the differences
		absdiff(prev_frame, next_frame, d1); // diff(n-1, n+1)
		absdiff(next_frame, current_frame, d2); // diff (n+1, n)
		bitwise_and(d1, d2, differenceImage);// U(d1,d2)

		differenceImage = thresholder->threshold(differenceImage);

		// Mask the difference image to avoid searching areas not near the lecturer
		maskedImage = masker->mask(detector->getSafeL(), detector->getSafeR(),
				detector->getSafeT(), detector->getSafeB(), differenceImage);

		// Dilate the image to favour grouped pixels (which indicate movement)
		dilatedImage = dilator->dilate(maskedImage);

		// Erode the image to get rid of noise
		erodedImage = eroder->erode(dilatedImage);

		// Threshold image to ignore minor movements
		thresholdedImage = thresholder->threshold(erodedImage);

		// Detect boundaries of lecturer within the thresholded image and write co-ordinates to output file
		detector->searchForMovement(thresholdedImage,result, outputstream, cnt);

		// If user wants to view processing steps
		if (showProcessing){
			rectangle(result, masker->getRect(), CV_RGB(255, 255,0), 2);
			//dilator->update();
			//eroder->update();
			//imshow ("Dilation", dilatedImage);
			//imshow ("Erosion", erodedImage);
			//imshow("Thresholding",thresholdedImage);
			//imshow("Masking",maskedImage);
			imshow("Tracking",result);

			switch(waitKey(1)){

			case 27: // Esc key
				return -2;;
			}
		}

		cnt++;

		// If it's been too long since the last detect, reset so it searches entire frame for movement
		if (detector->getNoDetectCnt() > resetTimer)
			detector->reset();

	} // end of while loop

	// Release videocapture and ofstream objects
	capture.release();
	outputstream->close();

	// Report processing stats to console
	double endTime = clock();
	double duration = (endTime - startTime ) / (double) CLOCKS_PER_SEC;
	std::cout<< "Tracking complete. \n Frames Processed: " << cnt << "\nPositive frames: "
			<< detector->getDetectCnt() << "\nTime taken: " << duration << std:: endl;

	return 0;
}

// Create trackbar for live parameter tweaking
void Tracking::setupUi(){
	createTrackbar( "Reset Timer (frames)", "Tracking",
			&resetTimer,max_resetTimer);
}
