// MotionDetector.cpp
// Chris Pocock 2014

#include "MotionDetector.h"

MotionDetector::MotionDetector(int width, int height) {

	// Variables from constructor
	w = width;
	h = height;

	// Default class variables
	min_movement = 20;
	max_movement = 100;
	minArea = 5;
	maxArea = 10;

	xpos = 0;
	ypos = 0;

	topMost = height;
	leftMost = width;
	botMost = 0;
	rightMost = 0;

	safeTopMost = height;
	safeLeftMost = width;
	safeBotMost = 0;
	safeRightMost = 0;

	detect_count = 0;
	nodetect_count = 0;

	// Trackbar  upper limits
	max_min_movement = 75;
	max_max_movement = 500;
	max_minArea = 100;
	max_maxArea = 100;

}

MotionDetector::~MotionDetector() {
}

// Search for lecturer movement in a binary image and write estimated position to output file
void MotionDetector::searchForMovement(Mat &thresholdImage, Mat &result, std::ofstream *output, int cnt){

	Mat temp;
	thresholdImage.copyTo(temp);
	float frameArea = thresholdImage.rows*thresholdImage.cols;

	vector< vector<Point> > contours;
	vector<Vec4i> hierarchy;

	// Find contours in input binary image
	//findContours(temp,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE ); // All contours
	findContours(temp,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE );// External contours

	std::cout << contours.size() << std::endl;

	// If there aren't too few or too many contours (which indicate movement)
	if(contours.size() > min_movement && contours.size() < max_movement){


		topMost = thresholdImage.rows;
		leftMost = thresholdImage.cols;
		botMost = 0;
		rightMost = 0;

		// Calculate the minimum and maximum x and y values of the contours
		for (unsigned int i = 0; i < contours.size(); i++){
			for (unsigned int j = 0; j < contours.at(i).size() ; j++){

				if (contours.at(i).at(j).x < leftMost)
					leftMost = contours.at(i).at(j).x;

				if (contours.at(i).at(j).x > rightMost)
					rightMost = contours.at(i).at(j).x;

				if (contours.at(i).at(j).y < topMost)
					topMost = contours.at(i).at(j).y;

				if (contours.at(i).at(j).y > botMost)
					botMost = contours.at(i).at(j).y;
			}
		}
	}

	// Create a rectangle using these 4 values, rectangle represents estimated lecturer position
	//Rect myRect(Point2f(leftMost,topMost),Point2f(rightMost,thresholdImage.rows*0.8));
	Rect myRect(Point2f(leftMost,topMost),Point2f(rightMost,botMost));

	// If the area of the estimated lecturer rectangle is not too small or too big
	if (contours.size() > min_movement && contours.size() < max_movement
			&& myRect.area() > frameArea*((float)minArea/100) && myRect.area() < frameArea*((float)maxArea/100)){

		nodetect_count = 0; // reset no_detect counter

		// Save these values for the masking component to use
		safeTopMost = topMost;
		safeLeftMost = leftMost;
		safeBotMost = botMost;
		safeRightMost = rightMost;

		oldRect = myRect; // Save the acceptable rectangle

		// Store x and y co-ordinates of estimated lecturer position
		xpos = myRect.x + myRect.width/2;
		ypos = myRect.y + myRect.height/3;

		// Draw on result image for viewing
		rectangle(result, myRect, CV_RGB(255, 0,0), 2);
		circle(result, Point2f(xpos,ypos), 3, Scalar(0,255,0), -1, 8);

		*output << cnt << " " << xpos << " " << ypos << std::endl; // write co-ordinates to output file
		detect_count++; // increment detect counter
	}

	// Else, the rectangle is too big or too small
	else{

		// Get x & y co-ordinates from the old acceptable rectangle
		xpos = oldRect.x+oldRect.width/2;
		ypos = oldRect.y+oldRect.height/3;

		// Draw on result image for viewing
		rectangle(result, oldRect, CV_RGB(255, 0,0), 2);
		circle(result, Point2f(xpos,ypos), 3, Scalar(0,255,0), -1, 8);

		// write "- -" to file instead of co-ordinates, indicates to panning component that no significant movement was found
		*output << cnt << " - -" << std::endl;
		nodetect_count++; // increment no_detect counter
	}
}

// Methods to return detect and no_detect counters
int MotionDetector::getDetectCnt(){
	return detect_count;
}
int MotionDetector::getNoDetectCnt(){
	return nodetect_count;
}

// Reset variables used by masking component
void MotionDetector::reset(){

	safeTopMost = h;
	safeLeftMost = w;
	safeBotMost = 0;
	safeRightMost = 0;
}

// Methods to return "safe" values that passed area checking
int MotionDetector::getSafeL(){
	return safeLeftMost;
}
int MotionDetector::getSafeR(){
	return safeRightMost;
}
int MotionDetector::getSafeT(){
	return safeTopMost;
}
int MotionDetector::getSafeB(){
	return safeBotMost;
}

// Create windows and trackbars for observation & live parameter tweaking
void MotionDetector::setupUi(){

	namedWindow( "Tracking", CV_WINDOW_AUTOSIZE );
	moveWindow("Tracking", 960, 540);

	createTrackbar( "Minimum Movement #", "Tracking",
			&min_movement,max_min_movement);

	createTrackbar( "Maximum Movement #", "Tracking",
			&max_movement,max_max_movement);

	createTrackbar( "Minimum Movement Area", "Tracking",
			&minArea,max_minArea);

	createTrackbar( "Maximum Movement Area", "Tracking",
			&maxArea,max_maxArea);
}
