/*
* jellyTracker.cpp
*
* A Jon Steck Corporation Code
*
* "Please work"
*      -Jon
*  
* --No downgraded video--
* 
* 
*/

#include <opencv/cv.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

//hide the local functions in an anon namespace/same as static function
namespace {
    void help(char** av) {
        cout << "The program captures frames from a video file, image sequence (01.jpg, 02.jpg ... 10.jpg) or camera connected to your computer." << endl
             << "Usage:\n" << av[0] << " <video file, image sequence or device number>" << endl
             << "q,Q,esc -- quit" << endl
             << "space   -- save frame" << endl << endl
             << "\tTo capture from a camera pass the device number. To find the device number, try ls /dev/video*" << endl
             << "\texample: " << av[0] << " 0" << endl
             << "\tYou may also pass a video file instead of a device number" << endl
             << "\texample: " << av[0] << " TheKardashiansGoToTheJerseyShore.avi" << endl
             << "\tYou can also pass the path to an image sequence and OpenCV will treat the sequence just like a video." << endl
             << "\texample: " << av[0] << " TheBachorette7.jpg" << endl;
    }

    int process(VideoCapture& capture) {
        clock_t start = time(0);
	int n = 0;
        char filename[200];
        string window_name = "video | q or esc to quit";
        cout << "press space to save a picture. q or esc to quit" << endl;
        namedWindow(window_name, WINDOW_NORMAL); //resizable window;
        Mat frame;
	int mem[10000][5];
	int memStart = 0;
	int memEnd = 0;
	int frameNum = 1;
	int id = 0;
	int jCount = 0;

        for (;;) {
            capture >> frame;
            if (frame.empty())
                break;
            
	    //Mat dGrade;
            //resize(frame,dGrade,Size(),.1,.1,INTER_LINEAR);
	    Rect r(550,150,1100,600);
	    //Rect r2(
	    Mat small = frame(r);
	    Mat grey;
	    cvtColor(small,grey,CV_RGB2GRAY);
	    Mat comp, temp;
	    temp = Mat::ones(grey.rows,grey.cols,0);
            temp = temp*255;
	    comp = temp - grey;
            Mat topHat, struct1;
            struct1 =  getStructuringElement(2,Size(120,120),Point(-1,-1));
	    morphologyEx(comp,topHat,MORPH_TOPHAT,struct1);
            Mat thresh;
            threshold(topHat,thresh,10,255,THRESH_BINARY);
            Mat opened, struct2;
	    struct2 = getStructuringElement(2,Size(40,40),Point(-1,-1));
	    morphologyEx(thresh,opened,MORPH_OPEN,struct2);
	    Rect r3(50,50,1000,500);
	    Mat smaller = opened(r3);

            vector<vector<Point> > contours;
            vector<Vec4i> hierarchy;
	    //vector<Point2f> center(contours.size());
            findContours(smaller,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
	    /// Approximate contours to polygons + get bounding rects and circles
  	    vector<vector<Point> > contours_poly( contours.size() );
            vector<Rect> boundRect( contours.size() );
	    Mat drawing = Mat::zeros( opened.size(), CV_8UC3 );

	    for( int i = 0; i < contours.size(); i++ )
     		{ 
		mem[memEnd][0] = frameNum;
		mem[memEnd][1] = id;
		id ++;
		//cout << mem[memEnd][0] << endl;
		approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
       		boundRect[i] = boundingRect( Mat(contours_poly[i]) );
		mem[memEnd][2] = int(boundRect[i].tl().x);
		mem[memEnd][3] = int(boundRect[i].tl().y);
		//cout << mem[memEnd][2] << endl;
		mem[memEnd][4] = 0;
		for( int k = memStart; k < memEnd; k++)
			{
			if(mem[memEnd][0]-mem[k][0] > 10)
				{
				memStart++;
				}
			//cout << memStart << endl;
			if(abs(mem[k][2]-mem[memEnd][2]) < 75 && abs(mem[k][3]-mem[memEnd][3]) < 75)
				{
				mem[memEnd][1] = mem[k][1];
				mem[memEnd][4] = mem[k][4]+1;
				}
			}
		//cout << mem[memEnd][1] << endl;
		//string String = static_cast<ostringstream*>( &(ostringstream() << mem[memEnd][1]) )->str();
		//putText(small,String,boundRect[i].tl(),FONT_HERSHEY_SIMPLEX,0.1,Scalar(0,0,255),1,8,false);
		if(mem[memEnd][4] == 10)
			{
			jCount++;
			}
		if(mem[memEnd][4]>9)
			{
			rectangle( drawing, boundRect[i].tl()+Point(50,50), boundRect[i].br()+Point(50,50), Scalar(0,0,255), 1, 8, 0 );
			}
		else
			{
			rectangle( drawing, boundRect[i].tl()+Point(50,50), boundRect[i].br()+Point(50,50), Scalar(0,0,100), 1, 8, 0 );
			}
		memEnd++;
     		}


  	    /// Draw polygonal contour + bonding rects + circles
  	    //Mat drawing = Mat::zeros( opened.size(), CV_8UC3 );
  	    //for( int i = 0; i< contours.size(); i++ )
     		//{
       		//if(mem[
		//rectangle( drawing, boundRect[i].tl()+Point(5,5), boundRect[i].br()+Point(5,5), Scalar(0,0,100), 1, 8, 0 );
     	    	//}

            Mat combined;
            combined = small + drawing;

	    string String = static_cast<ostringstream*>( &(ostringstream() << jCount) )->str();
            putText(combined,String,Point(1,7),FONT_HERSHEY_SIMPLEX,0.3,Scalar(0,0,255),1,8,false);

	    imshow(window_name, combined);
            char key = (char)waitKey(3); //delay N millis, usually long enough to display and capture input

            switch (key) {
            case 'q':
            case 'Q':
            case 27: //escape key
                return 0;
            case ' ': //Save an image
                sprintf(filename,"filename%.3d.jpg",n++);
                imwrite(filename,frame);
                cout << "Saved " << filename << endl;
                break;
            default:
                break;
            }
	    frameNum++;
        }
	clock_t end = time(0);
    	double time = difftime(end,start);
    	cout << time << endl;
        return 0;
    }
}

int main(int ac, char** av) {

    //clock_t start = time(0);
    if (ac != 2) {
        help(av);
        return 1;
    }
    std::string arg = av[1];
    VideoCapture capture(arg); //try to open string, this will attempt to open it as a video file or image sequence
    if (!capture.isOpened()) //if this fails, try to open as a video camera, through the use of an integer param
        capture.open(atoi(arg.c_str()));
    if (!capture.isOpened()) {
        cerr << "Failed to open the video device, video file or image sequence!\n" << endl;
        help(av);
        return 1;
    }
    //clock_t end = time(0);
    //double time = difftime(end,start)*1000;
    //cout << time << endl;
    return process(capture);
}
