/*
This application showcases how the window component of cvui can be
enhanced, i.e. movable anb minimizable, and used to control the
application of the Canny edge algorithm to a loaded image.

This application uses the EnhancedWindow class available in the
"ui-enhanced-window-component" example.

Authors:
	Fernando Bevilacqua <dovyski@gmail.com>

Contributions:
	Amaury Bréhéret - https://github.com/abreheret
	ShengYu - https://github.com/shengyu7697

Code licensed under the MIT license.
*/

#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/tracking.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <opencv2/core/ocl.hpp>

#define CVUI_IMPLEMENTATION
#include "include/cvui.h"

// Include the class that provides an enhanced cvui window component
#include "include/EnhancedWindow.h"

#define WINDOW_NAME	"CVUI Ehnanced UI Canny Edge"

int main(int argc, const char *argv[])
{
	
	cv::VideoCapture vid("/home/nvidia/data/151201_SC1ATK63_Batho_5-ProResHQ\ logo.mov");
	
	// Check if camera opened successfully
	if(!vid.isOpened()){
		std::cout << "Error opening video stream or file" << std::endl;
		return -1;
	}
	
	cv::Mat container(cv::Size(960, 540), CV_8UC3, cv::Scalar(0)); 
	cv::Mat scratch = container.clone();
	cv::Mat frame = container.clone();
	cv::Mat fgMaskMOG2 = container.clone();
	int low_threshold = 50, high_threshold = 150;
	int fps = vid.get(CV_CAP_PROP_FPS);
	int maxFPS = 2*fps;
	int position = 0;
	bool use_canny = false;
	bool detect_rois = false;
	bool pauseVideo = false;
	bool subtractBackground = false;
	int framesInFile = vid.get(CV_CAP_PROP_FRAME_COUNT);
	
	cv::Ptr<cv::BackgroundSubtractor> pMOG2;
	pMOG2 = cv::createBackgroundSubtractorMOG2(30,16,false);
	
	    // List of tracker types in OpenCV 3.4.1
    std::string trackerTypes[8] = {"BOOSTING", "MIL", "KCF", "TLD","MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"};
    // vector <string> trackerTypes(types, std::end(types));
 
    // Create a tracker
    std::string trackerType = trackerTypes[2];
 
    cv::Ptr<cv::Tracker> tracker;
 
    #if (CV_MINOR_VERSION < 3)
    {
        tracker = Tracker::create(trackerType);
    }
    #else
    {
        if (trackerType == "BOOSTING")
            tracker = cv::TrackerBoosting::create();
        if (trackerType == "MIL")
            tracker = cv::TrackerMIL::create();
        if (trackerType == "KCF")
            tracker = cv::TrackerKCF::create();
        if (trackerType == "TLD")
            tracker = cv::TrackerTLD::create();
        if (trackerType == "MEDIANFLOW")
            tracker = cv::TrackerMedianFlow::create();
        if (trackerType == "GOTURN")
            tracker = cv::TrackerGOTURN::create();
        //if (trackerType == "MOSSE")
        //    tracker = cv::TrackerMOSSE::create();
        //if (trackerType == "CSRT")
        //    tracker = cv::TrackerCSRT::create();
    }
	#endif


	// Create a settings window using the EnhancedWindow class.
	EnhancedWindow settings(10, 50, 270, 340, "Settings");

	// Init cvui and tell it to create a OpenCV window, i.e. cv::namedWindow(WINDOW_NAME).
	cvui::init(WINDOW_NAME);
	
	int timer = 1000;
	
	// Variables for ROI processing
	std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::Mat sel7 = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(5,5),cv::Point(-1,-1));
    cv::Mat sel3;
    
	cv::Rect rectangle(0, 0, 0, 0);
	cv::Rect2d bbox(0, 0, 0, 0);
	
	bool trackerInitialized = false;
	bool updateTracker = false;
	bool ok = false;
	
	while (true) {
		// Should we apply Canny edge?
		
		// Get the next frame and scale it
		cv::Mat vidFrame;
		cv::Mat resizedVidFrame;
		
		if (timer > 1000/fps && !pauseVideo) {
		
			
			// If the frame is empty, break immediately
			if (position > framesInFile)
			{
				printf("!!! cvQueryFrame failed: no frame\n");
				vid.set(CV_CAP_PROP_POS_FRAMES,0);
				position = 0;
				continue;
			}  
			
			// Capture frame-by-frame
			vid >> vidFrame;
			timer = 0;
  
			if (vidFrame.empty()) {
				std::cout << "No frame read." << std::endl;
				continue;
			}
		
			position++;
			
			//std::cout << "Frame Size: " << vidFrame.cols << "," << vidFrame.rows << std::endl;
			//std::cout << "Fruits Size: " << container.cols << "," << container.rows << std::endl;
			
			cv::resize(vidFrame,resizedVidFrame,cv::Size(container.cols,container.rows),0,0,CV_INTER_LINEAR);
			
			container = resizedVidFrame.clone();
			
			// add frame to BG estimator
			pMOG2->apply(container, fgMaskMOG2);
			pMOG2->getBackgroundImage(fgMaskMOG2);
			
					// subtract background before canny!
			if (subtractBackground) {
				absdiff(container,fgMaskMOG2,scratch);
				container = scratch.clone();
			}
			
			if (trackerInitialized) {
			
				double timer = (double)cv::getTickCount();
				
				ok = tracker->update(frame, bbox);
		 
				// Calculate Frames per second (FPS)
				float fps = cv::getTickFrequency() / ((double)cv::getTickCount() - timer);
				 
			}
		}
		



		
		if (use_canny) {
			// Yes, we should apply it.
			cv::cvtColor(container, frame, CV_BGR2GRAY);
			cv::Canny(frame, frame, low_threshold, high_threshold, 3);
			cv::cvtColor(frame, frame, CV_GRAY2BGR);
		} else {
			// No, so just copy the original image to the displaying frame.
			container.copyTo(frame);
		}
		
		if (detect_rois) {
			// Yes, we should apply it.
			cv::cvtColor(container, frame, CV_BGR2GRAY);
			cv::Canny(frame, frame, low_threshold, high_threshold, 3);
			// Find contours in the binary image
			dilate(frame,frame,sel7,cv::Point(-1,-1));
			erode(frame,frame,sel7,cv::Point(-1,-1));
			cv::findContours( frame, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
			
			// Draw the contours for objects of certain size
			for (unsigned int i = 0;i < contours.size();i++) {

				double area = cv::contourArea(contours[i]);
				
				if (area > 250) {
				
					cv::Rect bb = cv::boundingRect(contours[i]);
					cv::rectangle( container, bb.tl(), bb.br(), cv::Scalar(255,0,0), 2, 8, 0 );
					
				}
			}
			frame = container.clone();
			
		}
		



		

		// Render the settings window and its content, if it is not minimized.
        settings.begin(frame);
			if (!settings.isMinimized()) {
				cvui::checkbox("Use Background Subtration", &subtractBackground);
				cvui::checkbox("Use Canny Edge", &use_canny);
				cvui::checkbox("Detect ROIs",&detect_rois);
				cvui::trackbar(settings.width() - 20, &low_threshold, 5, 150);
				cvui::trackbar(settings.width() - 20, &high_threshold, 80, 300);
				cvui::trackbar(settings.width() - 20, &fps, 2, maxFPS);
				if (cvui::trackbar(settings.width() - 20, &position, 0,framesInFile)) {
					vid.set(CV_CAP_PROP_POS_FRAMES,position); 
				}
				cvui::checkbox("Pause Video", &pauseVideo);
				cvui::space(20); // add 20px of empty space
				if (cvui::button(settings.width() - 20, 30, "&Quit")) {
					break;
				}
			}
        settings.end();
		
				// Did any mouse button go down?
		if (cvui::mouse(cvui::DOWN)) {
			// Position the rectangle at the mouse pointer.
			rectangle.x = cvui::mouse().x;
			rectangle.y = cvui::mouse().y;
			trackerInitialized = false;
		}

		// Is any mouse button down (pressed)?
		if (cvui::mouse(cvui::IS_DOWN)) {
			// Adjust rectangle dimensions according to mouse pointer
			rectangle.width = cvui::mouse().x - rectangle.x;
			rectangle.height = cvui::mouse().y - rectangle.y;

			// Show the rectangle coordinates and size
			cvui::printf(frame, rectangle.x + 5, rectangle.y + 5, 0.3, 0xff0000, "(%d,%d)", rectangle.x, rectangle.y);
			cvui::printf(frame, cvui::mouse().x + 5, cvui::mouse().y + 5, 0.3, 0xff0000, "w:%d, h:%d", rectangle.width, rectangle.height);
			trackerInitialized = false;
		}

		// Did any mouse button go up?
		if (cvui::mouse(cvui::UP)) {
			// Hide the rectangle and init the tracker
			if (rectangle.area() > 100) {
				bbox = rectangle;
				tracker->clear();
				if (trackerType == "BOOSTING")
					tracker = cv::TrackerBoosting::create();
				if (trackerType == "MIL")
					tracker = cv::TrackerMIL::create();
				if (trackerType == "KCF")
					tracker = cv::TrackerKCF::create();
				if (trackerType == "TLD")
					tracker = cv::TrackerTLD::create();
				if (trackerType == "MEDIANFLOW")
					tracker = cv::TrackerMedianFlow::create();
				if (trackerType == "GOTURN")
					tracker = cv::TrackerGOTURN::create();
				tracker->init(frame, bbox);
				rectangle.x = 0;
				rectangle.y = 0;
				rectangle.width = 0;
				rectangle.height = 0;
				trackerInitialized = true;
				std::cout << "Tracking init" << std::endl;
			}
		}
		
		// Render the rectangle
		if (!trackerInitialized) {
			cvui::rect(frame, rectangle.x, rectangle.y, rectangle.width, rectangle.height, 0xff0000, 0xd00000ff);
		}
		else {
			if (ok)
			{
				// Tracking success : Draw the tracked object
				cv::rectangle(frame, bbox, cv::Scalar( 255, 0, 0 ), 2, 1 );
				//cv::putText(frame, "Tracking okay", cv::Point(300,400), cv::FONT_HERSHEY_SIMPLEX, 0.2, cv::Scalar(0,0,255),2);
				std::cout << fps << std::endl;
			}
			else
			{
				// Tracking failure detected.
				cv::putText(frame, "Tracking failure detected", cv::Point(300,400), cv::FONT_HERSHEY_SIMPLEX, 0.2, cv::Scalar(0,0,255),2);
				//std::cout << "tracking failed :(" << std::endl;
			}
		}

		
		// Update all cvui internal stuff, e.g. handle mouse clicks, and show
		// everything on the screen.
		cvui::imshow(WINDOW_NAME, frame);

		// Check if ESC was pressed
		if (cv::waitKey(1) == 27) {
			break;
		}
		timer+=10;
	}

	return 0;
}