#include "templateMatching.h"

bool tempMatch::TemplateMatching::setTemplate(const char* path)
{
	return (_loadImage(path,_temp)) ? true : false;
}


bool tempMatch::TemplateMatching::findTemplateInImage(const char* path)
{
	_loadImage(path,_image);

	cv::Canny(_image, _edges, _cannyThreshold1, _cannyThreshold2);

	resize(_temp, _temp, cv::Size(_temp.cols / _reduceTemp, _temp.rows / _reduceTemp));

	_findTemplate();

	return (_err == tempMatch::err::ok) ? true : false;
}

bool tempMatch::TemplateMatching::findTemplateInImage(cv::Mat image)
{
	if (image.type() != 0) return false;

	_image = image;

	cv::Canny(image, _edges, _cannyThreshold1, _cannyThreshold2);

	resize(_temp, _temp, cv::Size(_temp.cols / _reduceTemp, _temp.rows / _reduceTemp));

	_findTemplate();

	return (_err == tempMatch::err::ok) ? true : false;
}

void tempMatch::TemplateMatching::resizeTemp(float number)
{
	if (number > 0) _reduceTemp = number;
}

bool tempMatch::TemplateMatching::setThreshold(float thr)
{
	if (thr >= 0 || thr <= 1)
	{
		_threshold = thr;
		return true;
	}
	else
	{
		_err = tempMatch::err::badRange;
		return false;
	}
}

void tempMatch::TemplateMatching::setResizeTemplate(float number)
{
	if (number > 0)
		_reduceTemp = number;
}

bool tempMatch::TemplateMatching::setSpeed(int percent)
{
	if (percent >= 0 || percent <= 100)
	{
		_speedReduce = percent;
		return true;
	}
	else
	{
		_err = tempMatch::err::badRange;
		return false;
	}
}

void tempMatch::TemplateMatching::setCannyThreshold(double threshold1, double threshold2)
{
	_cannyThreshold1 = threshold1;
	_cannyThreshold2 = threshold2;
}

void tempMatch::TemplateMatching::getRectangle(int& left, int& top, int& right, int& bottom)
{
	left = _findRectLeft;
	top = _findRectTop;
	right = _findRectRight;
	bottom = _findRectBottom;
}

// private functions

void  tempMatch::TemplateMatching::_transformLinear2D(int& x, int& y, int& xReal, int& yReal, float inlox, float inupx, float outlox, float outupx, float inloy, float inupy, float outloy, float outupy)
{
	// for x
	float spanx = ((outupx - outlox) / (inupx - inlox));
	xReal = (x - inlox) * spanx + outlox;
	//for y
	float spany = ((outupy - outloy) / (inupy - inloy));
	yReal = (y - inloy) * spany + outloy;
}

bool tempMatch::TemplateMatching::_loadImage(const char* path, cv::Mat& image)
{
	if (path)
	{
		image = cv::imread(path, CV_8U);

		if (image.empty())
		{
			_err = tempMatch::err::cannotOpen;
			return false;
		}
		return true;
	}
	else
	{
		_err = tempMatch::err::wrongPath;
		return false;
	}
}

int tempMatch::TemplateMatching::_findTemplate()
{
	cv::Mat result;

	int width = 0;
	int height = _edges.rows;
	int edgeWidth = 0;
	int edgeHeight = 0;
	bool loop = true;

	_findRectLeft = 0;
	_findRectTop = 0;
	_findRectRight = 0;
	_findRectBottom = 0;

	double minval = 0;
	double maxvalUpdate = 0;
	double maxval = 0;
	cv::Point tempRight;
	cv::Point tempBottom;
	cv::Point minloc;
	cv::Point maxlocUpdate;
	cv::Point maxloc;

	while (true)
	{
		//Calculate 95% of the image width and height
		width = _edges.cols - ((_edges.cols / 100) * _speedReduce);
		height = _edges.rows - ((_edges.rows / 100) * _speedReduce);

		if (width == _edges.cols || height == _edges.rows)
			break;

		if (_temp.cols > _edges.cols || _temp.rows > _edges.rows) 
			break;

		cv::matchTemplate(_edges, _temp, result, cv::TM_CCOEFF_NORMED);
		minMaxLoc(result, &minval, &maxvalUpdate, &minloc, &maxlocUpdate);

		// Save the new max value, resize the template 
		if (maxvalUpdate > maxval && maxvalUpdate > _threshold)
		{
			maxval = maxvalUpdate;

			_transformLinear2D(maxlocUpdate.x, maxlocUpdate.y, maxloc.x, maxloc.y, 0, _edges.cols, 0, _image.cols, 0, _edges.rows, 0, _image.rows);

			tempRight.x = maxlocUpdate.x;
			tempRight.y = maxlocUpdate.y;
			edgeWidth = _edges.cols;
			edgeHeight = _edges.rows;
		}

		// Pyramid down - size of the image is smaller
		resize(_edges, _edges, cv::Size(width, height));
	}

	cv::cvtColor(_image, _image, cv::COLOR_GRAY2RGB);

	if (maxval > 0)
	{
		tempRight.x = tempRight.x + _temp.cols;
		tempBottom.x = tempRight.x;
		tempBottom.y = tempRight.y + _temp.rows;

		_transformLinear2D(tempRight.x, tempRight.y, tempRight.x, tempRight.y, 0, edgeWidth, 0, _image.cols, 0, edgeHeight, 0, _image.rows);
		_transformLinear2D(tempBottom.x, tempBottom.y, tempBottom.x, tempBottom.y, 0, edgeWidth, 0, _image.cols, 0, edgeHeight, 0, _image.rows);

		//rectangle(_image, cv::Rect(maxloc.x, maxloc.y, tempRight.x - maxloc.x, tempBottom.y - maxloc.y), cv::Scalar(0, 255, 0));
		_findRectLeft = maxloc.x;
		_findRectTop = maxloc.y;
		_findRectRight = tempRight.x - maxloc.x;
		_findRectBottom = tempBottom.y - maxloc.y;
	}


	return 0;
}