#include "opencv/cv.h" 
#include "opencv/cxcore.h" 
#include "opencv/highgui.h"
#include <X11/Xlib.h>

// for testing purposes
#include <iostream>
//using namespace std;

#define REGIONMAX 1000
#define SCREEN_W 1024
#define SCREEN_H 768

#define CAM_XMIN 0.05
#define CAM_XMAX 0.95
#define CAM_YMIN 0.05
#define CAM_YMAX 0.95


// **** Some defs


#include "procRGB.h"

// Some parameters

const int tsThreshold = 4;
const int dragThreshold = 100000;
const int rightThreshold = 30;

// f$@*# it just make these global
IplImage *frame, *frameMod;

RgbImage rgbFrame;
RgbImage rgbFrameMod;



// **** Program
#include "proc.h"
#include "procMath.h"


Region reg1[REGIONMAX], reg2[REGIONMAX];
Region *myRegions, *myRegionsLast;

int currentTouchType;
enum { LEFT, RIGHT, THIRD };
int currentTouchState;
enum { UP, DOWN };
int maxRegion, lastMaxRegion;
int closestRegion, lastClosestRegion, closestRegionDist, lastClosestRegionDist;
int screenX, screenY;
int currentButton;



Display *display;
Window root_window;
XEvent event;


void process(int xMin, int xMax, int yMin, int yMax, CvSize size, int res, int & regionCount, int regionMax);

char checkPixel(Region *currentRegion, int i, int j);
#include "procMouse.h"



// The "distance" function
// This can of course be done rectilinearly or as the crow flies\



int main(int argc, char** argv) {
	myRegions = reg1;
	myRegionsLast = reg2;
	currentTouchType = LEFT;
	currentTouchState = UP;
	maxRegion = lastMaxRegion = 0;

	/*
	Display *dpy;
	Window root_window;

	dpy = XOpenDisplay(0);
	root_window = XRootWindow(dpy, 0);
	XSelectInput(dpy, root_window, KeyReleaseMask);
	XWarpPointer(dpy, None, root_window, 0, 0, 0, 0, 100, 100);
	XFlush(dpy);
	
	XEvent event;
	*/
	
	// **************************************************************************
	// ** Initialize the events for controlling the cursor                     **
	// **************************************************************************
	
	display = XOpenDisplay(NULL);
	root_window = XRootWindow(display, 0);
	
	if(display == NULL) {
		fprintf(stderr, "Errore nell'apertura del Display !!!\n");
		exit(EXIT_FAILURE);
	}
	
	memset(&event, 0x00, sizeof(event));
	
	
	
	
	
	
	
	
	
	
	
	cvNamedWindow("Hello", CV_WINDOW_AUTOSIZE); // open a GUI window
	cvMoveWindow("Hello", 100, 100);
	CvCapture* capture = cvCreateCameraCapture(0); // automatically detect a camera from USB port
	//CvCapture* capture = cvCreateFileCapture("./ftir/a.mpg"); // alternately, use a video input for testing
	
	frame = cvQueryFrame(capture);
	frameMod = cvCreateImage(cvGetSize(frame), 8, 3);
	CvScalar white = cvScalar(255.0, 255.0, 255.0, 255.0); // color
	
	rgbFrame = RgbImage(frame);
	rgbFrameMod = RgbImage(frameMod);

	if (frame) {
		while(1) {
			frame = cvQueryFrame(capture);
			if (!frame) break;
			CvSize mySize;
			mySize = cvGetSize(frame);
			
			int myRegionCount = 0;
			process(mySize.width*CAM_XMIN, mySize.width*CAM_XMAX, mySize.height*CAM_YMIN, mySize.height*CAM_YMAX, mySize, 1, myRegionCount, REGIONMAX);
			//cout << myRegionCount << endl;
			lastMaxRegion = maxRegion;
			maxRegion = -1;
			//int maxRegionArea = 0;
			for (int i = 0; i < myRegionCount; i++) {
				if (myRegions[i].enabled & myRegions[i].myArea > 10) {
					cvCircle(frameMod, cvPoint(myRegions[i].myX, myRegions[i].myY), 2, cvScalar(255.0, 255.0, 255.0, 255.0), 1, 8, 0);
					myRegionCount++;
					//cout << "(" << myRegions[i].myX << ", " << myRegions[i].myY << ") " << myRegions[i].myArea << "; " << flush;
					if (myRegionCount) {
						if (myRegions[maxRegion].myArea < myRegions[i].myArea) {
							maxRegion = i;
						}
					} else {
						maxRegion = i;
					}
						
				}
			}
			if (-1 != maxRegion) {
				// now, check for another region
				closestRegion = -1;
				for (int i = 0; i < myRegionCount; i++) {
					if (i != maxRegion) {
						if (-1 == closestRegion) {
							closestRegion = i;
							closestRegionDist = distance(myRegions[i], myRegions[maxRegion]);
						} else if (distance(myRegions[i], myRegions[maxRegion]) < closestRegionDist) {
							closestRegion = i;
							closestRegionDist = distance(myRegions[i], myRegions[maxRegion]);
						}
						
					}
				}
				
				if (closestRegionDist <= 30) {
					// it's theoretically a right click...
					
					double myX = (myRegions[maxRegion].myX + myRegions[closestRegion].myX) / 2;
					double myY = (myRegions[maxRegion].myY + myRegions[closestRegion].myY) / 2;
					camToScreen(myX, myY, screenX, screenY);
					//screenX = SCREEN_W - double(myX - mySize.width*CAM_XMIN) / double(mySize.width*(CAM_XMAX - CAM_XMIN)) * SCREEN_W;
					//screenY = double(myY - mySize.height*CAM_YMIN) / double(mySize.height*(CAM_YMAX - CAM_YMIN)) * SCREEN_H;
					currentButton = 1;
					
					
				} else {				
					//screenX = SCREEN_W - double(myRegions[maxRegion].myX - mySize.width*CAM_XMIN) / double(mySize.width*(CAM_XMAX - CAM_XMIN)) * SCREEN_W;
					//screenY = double(myRegions[maxRegion].myY - mySize.height*CAM_YMIN) / double(mySize.height*(CAM_YMAX - CAM_YMIN)) * SCREEN_H;
					camToScreen(myRegions[maxRegion].myX, myRegions[maxRegion].myY, screenX, screenY);
					currentButton = 1;
				}
				if (-1 != lastMaxRegion) {
					if (
						distance(myRegionsLast[lastMaxRegion], myRegions[maxRegion]) < dragThreshold
					) {
					/*
					if (
						(	myRegionsLast[lastMaxRegion].myX - myRegions[maxRegion].myX < dragThreshold
						||	myRegionsLast[lastMaxRegion].myX - myRegions[maxRegion].myX > -dragThreshold )
						&&
						(	myRegionsLast[lastMaxRegion].myY - myRegions[maxRegion].myY < dragThreshold
						||	myRegionsLast[lastMaxRegion].myY - myRegions[maxRegion].myY > -dragThreshold )
					) {
					*/
						// keep the mouse down...
						mouseMove(screenX, screenY);
						
						
					} else {
						// mouse up, then back down again
						mouseUp(currentButton);
						mouseMove(screenX, screenY);
						mouseDown(currentButton);
					}
					

				}	else {
					mouseMove(screenX, screenY);
					mouseDown(currentButton);
				}
			} else {
				mouseUp(currentButton);
			}
			
			//if (
			
			
			////cout << endl;
			
			// move myRegions into myLastRegions by swapping the addresses
			
			Region *swap = myRegions;
			myRegionsLast = myRegions;
			myRegions = swap;
			
			cvShowImage("Hello", frameMod);
		
			
			char c = cvWaitKey(10);
			if (27 == c) break;
		}
	}
	cvSaveImage("./last.jpg", frameMod);
}


void process(int xMin, int xMax, int yMin, int yMax, CvSize size, int res, int & regionCount, int regionMax) {
	currentAccount++;
	regionCount = 0;
	checkCount = 0;
	cmCount = 0;
	cxMin = xMin;
	cxMax = xMax;
	cyMin = yMin;
	cyMax = yMax;
	myRegions[0].init();
	for (int i = yMin; i < yMax & i < size.height; i += res) {
		
		for (int j = lX; j <= rX & j < size.width; j += res) {
			if (i > (j-lX)/double(rX-lX)*(tlY-trY)+trY
				& i < (j-lX)/double(rX-lX)*(brY-blY)+blY ) {
				
				if (accounted[i][j] != currentAccount) {
					char found = checkPixel(&myRegions[regionCount], i, j);
					if (found) {
						myRegions[regionCount+1].init();
						regionCount++;
					}
				
				}
			
			}
			
		}
		
	}
	//cout << checkCount << ", " << cmCount << endl;
}

char checkPixel(Region *currentRegion, int i, int j) {
	checkCount++;
	// dead spot
	if (329 < j & j < 352 & 
		219 < i & i < 240 ) {
		return 0;
	}
	
	// outside screen area
	if (i <= (j-lX)/double(rX-lX)*(tlY-trY)+trY
	| i >= (j-lX)/double(rX-lX)*(brY-blY)+blY
	| j < lX
	| j > rX ) {
		return 0;
	}
	// inside
	if (
		i > cyMin & i < cyMax & j > cxMin & j < cxMax & accounted[i][j] != currentAccount
		) {
		accounted[i][j] = currentAccount;	
		if (rgbFrame[i][j].b > 152) { // counts as a "touch" for now
			// for visual purposes
			rgbFrameMod[i][j].r = 255;
			rgbFrameMod[i][j].g = 0;
			rgbFrameMod[i][j].b = 0;
			currentRegion->addNewPixel(j, i);
			
			checkPixel(currentRegion, i+1, j);
			checkPixel(currentRegion, i, j+1);
			checkPixel(currentRegion, i, j-1);

			return 1;
			
			
		} else {
			rgbFrameMod[i][j].r = rgbFrame[i][j].r;
			rgbFrameMod[i][j].g = rgbFrame[i][j].g;
			rgbFrameMod[i][j].b = rgbFrame[i][j].b;
		}
		
	}
	return 0;
	
}


