#include <opencv2/opencv.hpp>
#include <string>

const std::string WINDOW_NAME = "A Scanner Darkly";

const std::string CANNY_LOWER_THRESHOLD_TRACKBAR_NAME = "Canny: Lower Threshold";

const float CARD_ASPECT_RATIO = 88.0f / 63.0f;
const std::string ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME = "Aspect Ratio Tolerance";
int g_aspect_ratio_tolerance = 1;
const int MAX_ASPECT_RATIO_TOLERANCE = 100;

int g_Canny_lower_threshold = 110;
const int CANNY_UPPER_THRESHOLD = 255;

int main(int argc, char** argv ) {
	cv::VideoCapture cap;
	cap.open(0);

	if (!cap.isOpened()) {
		std::cerr << "Couldn't open capture" << std::endl;
		return -1;
	}

	std::cout << "Card aspect ratio: " << CARD_ASPECT_RATIO << std::endl;

	cv::namedWindow(WINDOW_NAME);

	cv::createTrackbar(CANNY_LOWER_THRESHOLD_TRACKBAR_NAME, WINDOW_NAME, &g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD, NULL);
	cv::createTrackbar(ASPECT_RATIO_TOLERANCE_TRACKBAR_NAME, WINDOW_NAME, &g_aspect_ratio_tolerance, MAX_ASPECT_RATIO_TOLERANCE, NULL);

	cv::Mat frame, grayscaleFrame, blurFrame, cannyFrame;
	while (true) {
		cap >> frame;

		cv::cvtColor(frame, grayscaleFrame, cv::COLOR_BGR2GRAY);
		cv::GaussianBlur(grayscaleFrame, blurFrame, cv::Size(5, 5), 2, 2);
		cv::Canny(blurFrame, cannyFrame, g_Canny_lower_threshold, CANNY_UPPER_THRESHOLD);

		std::vector<std::vector<cv::Point> > contours;
		std::vector<cv::Vec4i> hierarchy;
		cv::findContours(cannyFrame, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

		std::vector<std::vector<cv::Point>> hull(contours.size());
		for(size_t i = 0; i < contours.size(); i++) {
			cv::convexHull(contours[i], hull[i]);
		}

	  std::vector<cv::RotatedRect> minRect(contours.size());
		for(size_t i = 0; i < contours.size(); i++) {
		  minRect[i] = cv::minAreaRect(contours[i]);
		}
		
		//cv::Scalar contourColor = cv::Scalar(255, 0, 0);
		cv::Scalar hullColor = cv::Scalar(0, 255, 0);
		cv::Scalar rectangleColor = cv::Scalar(0, 0, 255);

		for (size_t i = 0; i< contours.size(); i++) {
			//cv::drawContours(frame, contours, (int)i, contourColor, 2, cv::LINE_8, hierarchy, 0);
			//cv::drawContours(frame, hull, (int)i, hullColor, 2, cv::LINE_8);
			
			// TODO: A purely aspect-ratio-based detection method returns too many false positives
			// inside of a card, even after screwing around with various algorithm parameters.
			// We have two potential options to fix this:
			// - Only take the largest detected RotatedRect with the correct aspect ratio
			// - Perform the camera calibration necessary to take accurate real-world measurements
			float aspectRatio = minRect[i].size.height / minRect[i].size.width;
			float aspectRatioTolerance = g_aspect_ratio_tolerance / 100.0f;
			if (aspectRatio < (CARD_ASPECT_RATIO - aspectRatioTolerance) || aspectRatio > (CARD_ASPECT_RATIO + aspectRatioTolerance)) {
				continue;
			}
			
			cv::Point2f rect_points[4];
			minRect[i].points(rect_points);
			for (int j = 0; j < 4; j++) {
				cv::line(frame, rect_points[j], rect_points[(j + 1) % 4], rectangleColor);
			}
			cv::putText(frame, std::to_string(aspectRatio), minRect[i].center, cv::FONT_HERSHEY_COMPLEX, 1, hullColor);
		}

		cv::imshow(WINDOW_NAME, frame);

		char c = (char)cv::waitKey(33);
		if (c == 27) {
			break;
		} else if (c == 's') {
			cv::imwrite("output.png", frame);
		}
	}

	cv::destroyWindow(WINDOW_NAME);
}
