#include "intelligentroi.h"

#include <opencv2/imgproc.hpp>

#include "utils.h"
#include "log.h"

bool InteligentRoi::compPointPrio(const std::pair<cv::Point2i, int>& a, const std::pair<cv::Point2i, int>& b, const cv::Point2i& center)
{
	if(a.second != b.second)
		return a.second > b.second;

	double distA = pointDist(a.first, center);
	double distB = pointDist(b.first, center);

	return distA < distB;
}

void InteligentRoi::slideRectToPoint(cv::Rect& rect, const cv::Point2i& point)
{
	if(!pointInRect(point, rect))
	{
		if(point.x < rect.x)
			rect.x = point.x;
		else if(point.x > rect.x+rect.width)
			rect.x = point.x-rect.width;
		if(point.y < rect.y)
			rect.y = point.y;
		else if(point.y > rect.y+rect.height)
			rect.y = point.y-rect.height;
	}
}

cv::Rect InteligentRoi::maxRect(bool& incompleate, const cv::Size2i& imageSize, std::vector<std::pair<cv::Point2i, int>> mustInclude)
{
	incompleate = false;
	int diameter = std::min(imageSize.height, imageSize.width);
	cv::Point2i point(imageSize.width/2, imageSize.height/2);
	cv::Rect candiate(point.x-diameter/2, point.y-diameter/2, diameter, diameter);

	std::sort(mustInclude.begin(), mustInclude.end(),
		[&point](const std::pair<cv::Point2i, int>& a, const std::pair<cv::Point2i, int>& b){return compPointPrio(a, b, point);});

	while(true)
	{
		cv::Rect includeRect = rectFromPoints(mustInclude);
		if(includeRect.width-2 > diameter || includeRect.height-2 > diameter)
		{
			incompleate = true;
			slideRectToPoint(candiate, mustInclude.back().first);
			mustInclude.pop_back();
			Log(Log::DEBUG)<<"cant fill";
			for(const std::pair<cv::Point2i, int>& mipoint : mustInclude)
				Log(Log::DEBUG)<<mipoint.first<<' '<<pointDist(mipoint.first, point)<<' '<<mipoint.second;
		}
		else
		{
			break;
		}
	}

	for(const std::pair<cv::Point2i, int>& includePoint : mustInclude)
		slideRectToPoint(candiate, includePoint.first);

	if(candiate.x < 0)
		candiate.x = 0;
	if(candiate.y < 0)
		candiate.y = 0;
	if(candiate.x+candiate.width > imageSize.width)
		candiate.width = imageSize.width-candiate.x;
	if(candiate.y+candiate.height > imageSize.height)
		candiate.height = imageSize.height-candiate.y;

	return candiate;
}

InteligentRoi::InteligentRoi(const Yolo& yolo)
{
	personId = yolo.getClassForStr("person");
}

bool InteligentRoi::getCropRectangle(cv::Rect& out, const std::vector<Yolo::Detection>& detections, const cv::Size2i& imageSize)
{
	std::vector<std::pair<cv::Point2i, int>> corners;
	for(size_t i = 0; i < detections.size(); ++i)
	{
		int priority = detections[i].priority;
		if(detections[i].class_id == personId)
		{
			corners.push_back({detections[i].box.tl()+cv::Point2i(detections[i].box.width/2, 0), priority+2});
			corners.push_back({detections[i].box.tl(), priority+1});
			corners.push_back({detections[i].box.br(), priority});
			corners.push_back({detections[i].box.tl()+cv::Point2i(detections[i].box.width, 0), priority+1});
			corners.push_back({detections[i].box.br()+cv::Point2i(0-detections[i].box.width, 0), priority});
		}
		else
		{
			corners.push_back({detections[i].box.tl(), priority});
			corners.push_back({detections[i].box.br(), priority});
			corners.push_back({detections[i].box.tl()+cv::Point2i(detections[i].box.width, 0), priority});
			corners.push_back({detections[i].box.br()+cv::Point2i(0-detections[i].box.width, 0), priority});
		}
	}

	bool incompleate;
	out = maxRect(incompleate, imageSize, corners);
	return incompleate;
}
