#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include "ClippedFrame.h"
#include <time.h>       /* time_t, struct tm, difftime, time, mktime */

using namespace cv;
using namespace std;

double dWidth, dHeight;
double clippedWidth = 800, clippedHeight = 600;
double initialX = 800;
double initialY = 390; // Clipped frame stays at this height for now.
double fps = 25;
ClippedFrame cf(initialX, initialY, clippedWidth, clippedHeight, fps);

std::string roiFromTrackerFilename = "tracking_output.txt";
std::string roiPointsFilename = "initialROIDataOpenCV.yaml";
std::string panPointsFilename = "panDataPointsOut.yaml";

const char * inputFileURL;
const char * outputFileURL;

std::string intToString(int x) {
	std::ostringstream convert;
	convert << x;
	return convert.str();
}

void getROIPoints(std::map <int, int> &roiLocationMap){
	FileStorage fs(roiPointsFilename, FileStorage::READ);
	FileNode points = fs["panPoints"];
	FileNodeIterator it = points.begin(), it_end = points.end();
	int idx = 0;
	// iterate through a sequence using FileNodeIterator
	for( ; it != it_end; ++it, idx++ )
	{
		int frame=(int)(*it)["frame"];
		int location=(int)(*it)["location"];

		roiLocationMap[frame]=location;
	}
}

void getPanningPoints(std::map <int, float> &panningTimeMap, std::map <int, int> &panningLocationMap){
	FileStorage fs(panPointsFilename, FileStorage::READ);

	FileNode features = fs["panPoints"];
	FileNodeIterator it = features.begin(), it_end = features.end();
	int idx = 0;

	// iterate through a sequence using FileNodeIterator
	for( ; it != it_end; ++it, idx++ )
	{
		int frameStart = (int)(*it)["frame_start"];
		float seconds = (float)(*it)["seconds"];
		int location = (int)(*it)["location"];
		std::cout << frameStart << std::endl;
		panningTimeMap[frameStart] = seconds;
		panningLocationMap[frameStart] = location;
	}
}

int writeClippedVideo(std::string stitchedVideoURL, std::string outputVideoURL, std::string roiFromTrackerFilename)
{
	// run python script to generate panning points
	std::string cmd = "python src/panning/json_to_pan_file.py \""+roiFromTrackerFilename+"\"";
	system(cmd.c_str());

	VideoCapture cap(stitchedVideoURL); // open the video stream which was recorded to disk
	//VideoCapture cap(0); // open the video camera (webcam). If there are many, try other numbers.

	if (!cap.isOpened())  // if not success, exit program
	{ cout << "ERROR: Cannot open the video stream" << endl; return -1; }

	std::map <int, int> roiLocationMap;
	getROIPoints(roiLocationMap);

	std::map <int, float> panningTimeMap;
	std::map <int, int> panningLocationMap;
	getPanningPoints(panningTimeMap, panningLocationMap);

	dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH); //get the width of frames of the video
	dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
	if (cap.get(CV_CAP_PROP_FPS) < 100) //TODO: investigate why this was occuring (frame rates of 10000 and so on)
			fps = cap.get(CV_CAP_PROP_FPS);
	int fourcc = CV_FOURCC('M', 'P', '4', 'S');//cap.get(CV_CAP_PROP_FOURCC); // We could not get ffmpeg and x264 to work, so we use mpeg

	cf.setFps(fps); // important to set the fps on the clipped frame once we know it.

	Size frameSize(clippedWidth, clippedHeight);
	VideoWriter oVideoWriter(outputVideoURL, fourcc, fps, frameSize, true);

	if ( !oVideoWriter.isOpened() ) //if instantiating the VideoWriter failed, exit the program
	{ cout << "ERROR: Failed to open the video writing stream." << endl; return -1; }

	Mat frame;
    int frameNumber = 0;
    std::cout << "Writing the pan to the video and producing output..." << std::endl;
    while (true) // processing loop
	{
		frameNumber++;

//		if (frameNumber%(int)fps==0) { cout << (int)(frameNumber/fps+0.01) << " sec" << endl; } //print out frame every second

		bool isNextFrame = cap.read(frame);
		if (!isNextFrame) {
			cout << "Error getting next frame. Probably no more frames. Processing will stop now." << endl;
			break;
		}

//		if (roiLocationMap.count(frameNumber)==1) {
//			int xval = initialX;
//			int yval = initialY;
			// cv::circle(frame, cv::Point(xval, yval), 2, CV_RGB(50, 0, 255), 8, CV_AA, 0);
//		}

		if (panningTimeMap.count(frameNumber)==1) {
			// cout << "Panning at frame: " << frameNumber << endl;
			float time = panningTimeMap[frameNumber];
			int xval = panningLocationMap[frameNumber];
			int yval = initialY; // average height from panning algorithm
			cf.snapToWithinBounds(xval, yval, dWidth, dHeight); // ensure xval,yval are within bounds
			cf.moveTo(xval, yval, time);
		}

		// update the clipped frames coordinates and draw rectangle to canvas
		cf.update();
//		cf.drawTo(frame);

		Mat clippedFrame = cf.getAsMat(frame);
		oVideoWriter.write(clippedFrame); //write the clipped frame into the file
	}
	return 0;
}
