#ifdef _CH_
#pragma package <opencv>
#endif

#ifndef _EiC
#include "cv.h"
#include "highgui.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include "ImageDatabase.h"
#include "feature.h"
#include "training.h"
#include "BestWC.h"
#include "edgeFeature.h"
#include "redFeature.h"
#include <time.h>
#include <ctime>
#include <sys/timeb.h>
#include <vector>

//#define max(a,b) ((a) > (b) ? (a) : (b))
#define incFactor 1.10	//factor by which search window grows

//**************************** THE FUNCTIONS *********************************
// The main program entry point
// Return 0 on success, 1 on failure/error
int main(int argc, char **argv)
{
	IplImage *pImageToMatch1 = NULL;
	CImageDatabase imageDatabase;

	// Load the database
	system("dir /b *.jpg > skinimages2.txt");	//make list of jpegs in the folder
	system("dir /b/s feat*.jpg > features.txt");	//make list of all viola features	
	system("dir /b/s dfeat*.txt > featdesc.txt");	//make list of all feature descriptions (rectangles) for viola features
	system("dir /b/s efeat*.txt > edgefeatdesc.txt");	//make list of all edge features
	system("dir /b/s rfeat*.txt > redfeatdesc.txt");	//make list of all red features

	printf("Loading database: skinimages2.txt ...\n");

	if(!imageDatabase.load1("skinimages2.txt"))
	{
		printf("Cannot load database2.txt database file\nProgram exited\n");
		return 1;
	}

	//load features database
	std::vector<feature*> featurelist;	//list of basic features (shapes)
	if(!imageDatabase.loadFeatures("features.txt", "featdesc.txt", "edgefeatdesc.txt", "redfeatdesc.txt", featurelist))	//1 for load negatives
	{
		printf("Cannot load features.txt database file\nProgram exited\n");
		return 1;
	}
	int di=0;//used for vector deallocation at the end

	std::vector<training*> traininglist;

	//load the series of predetermined WC
	imageDatabase.loadSeries("series.txt", traininglist);

	printf("Loading first image:\n");

	pImageToMatch1=imageDatabase.getImage1(0);
	
	if(pImageToMatch1 == NULL)
	{
		printf("Cannot load 0001.jpg image file\nProgram exited\n");
		return 1;
	}
	
	int count=0;
	int subcount=0;

	cvNamedWindow("target", 1);
	cvShowImage( "target", pImageToMatch1 );
	printf("choose an option\n");
	printf("to see help menu 	h\n");
	
	bool needcarsmall=false;
	
	int numSubImages=imageDatabase.getNumberOfSubs();
	int numEntries=imageDatabase.getNumberOfEntries();
	printf("number of entries is %i\n", numEntries);
	int i;
	IplImage *integrals;
	IplImage *reds;
	IplImage *EObins[eohbins];
	IplImage *cin=cvCloneImage(pImageToMatch1);	//integral image of all eoh bins
	//IplImage *cin;	//integral image of all eoh bins
	IplImage *milos;
	std::vector<rect*> hondas;
	float scale;
	int windowWidth=100;
	int windowHeight=50;
	int subwindowcounter=0;
	int levels=0;
	bool isFuzzy=false;
	bool constAlpha=false;
	int mil=0;
	//int timestamp1=0;
	//int timestamp2=0;
	//time_t start,end;	//start and end time of 'a'
	//double dif;			//differece of start and end times
	float sec=0;;
	float millisec=0;
	int rep=0;
	
	struct _timeb timebuffer;
	char *timeline;
	
	_ftime( &timebuffer );
	timeline = ctime( & ( timebuffer.time ) );
	
	printf( "The time is %.19s.%hu %s", timeline, timebuffer.millitm, &timeline[20] );
	
	for(;;) {
		int c;
		c = cvWaitKey( 0 );
		switch( c ) {
			case 's':
				count++;
				if (count==numEntries)
					count=0;
				cvShowImage("target", imageDatabase.getImage1(count));
				pImageToMatch1=imageDatabase.getImage1(count);
				printf("showing = %s\n", imageDatabase.getImageName(count));
				break;
			case 'd':
				count--;
				if (count<0)
					count=numEntries-1;
				cvShowImage("target", imageDatabase.getImage1(count));
				pImageToMatch1=imageDatabase.getImage1(count);
				printf("showing = %s\n", imageDatabase.getImageName(count));
				break;
			case 'h':
				printf("\nto exit				e\n");
				printf("to find Honda's 		a\n");
				printf("to scan database forward 	s\n");
				printf("to scan database backward 	d\n");
				printf("to see help menu 		h\n");
				printf("choose an option\n");
				break;

			case 'c':
				
				for (mil=0; mil<traininglist.size(); mil++) {
					training *e= (training*) traininglist[mil];
					IplImage *c = cvCloneImage(pImageToMatch1);
					CvPoint p1 = cvPoint(e->featoffsetX,e->featoffsetY);
					CvPoint p2 = cvPoint(e->featoffsetX+e->featScaleX*2,e->featoffsetY+e->featScaleY*2);
					if (e->featureNumber==0) {
						cvRectangle( c, p1, p2, CV_RGB(255,0,0), 1 );
						printf("horizontal rigth\n");
					}else if (e->featureNumber==1) {
						cvRectangle( c, p1, p2, CV_RGB(0,255,0), 1 );
						printf("bin 2\n");
					}else if (e->featureNumber==2) {
						cvRectangle( c, p1, p2, CV_RGB(0,0,255), 1 );
						printf("vertical\n");
					}else if (e->featureNumber==3) {
						cvRectangle( c, p1, p2, CV_RGB(255,255,0), 1 );
						printf("bin 4\n");
					}else if (e->featureNumber==4) {
						cvRectangle( c, p1, p2, CV_RGB(255,0,255), 1 );
						printf("horizontal left\n");
					}else if (e->featureNumber==5) {
						cvRectangle( c, p1, p2, CV_RGB(0,255,255), 1 );
						printf("horizontal left and right\n");
					}else {
						cvRectangle( c, p1, p2, CV_RGB(255,255,255), 1 );
						printf("reds\n");
					}
					cvNamedWindow("best", 1);
					cvShowImage( "best", c);
					cvWaitKey(0);
					cvReleaseImage(&c);
					//printf("it is featno %i, with offset x %i, and offsety %i, scalex %i, scaley %i\n", e->featureNumber, e->featoffsetX, e->featoffsetY, e->featScaleX, e->featScaleY);
				}
				cvDestroyWindow("best");
				
				break;
			case 'a':
				printf("finding hondas....\n");
				struct _timeb timebuffer1;
				struct _timeb timebuffer2;
				
				_ftime( &timebuffer1 );
				
				cin=cvCloneImage(pImageToMatch1);
				IplImage *testImage;	
				CvSize subsize = cvGetSize(pImageToMatch1);
				testImage=cvCreateImage(subsize, IPL_DEPTH_8U, 1);
				cvCvtColor( pImageToMatch1, testImage, CV_BGR2GRAY);
				integrals=imageDatabase.CVIntegral(testImage);
				cvReleaseImage(&testImage);
				reds=imageDatabase.createRedIntegral(pImageToMatch1);
				imageDatabase.createEOHIntegral(pImageToMatch1, EObins, cin);
				
				milos=cvCloneImage(pImageToMatch1);
				
				scale=1.0;
				while (((scale*100)<=pImageToMatch1->width)||((scale*50)<=pImageToMatch1->height)) {
					levels++;
					if (scale!=1.0) {
						windowWidth=windowWidth*incFactor;
						windowHeight=windowHeight*incFactor;
					}
					for (i=0; i<=pImageToMatch1->width-windowWidth; i+=2) {			//width
						for (int j=0; j<=pImageToMatch1->height-windowHeight; j+=2) {	//height
							subwindowcounter++;
							float decision=0;		//final decision of each subwindow
							for (int s=0; s<traininglist.size(); s++) {		//each WC
								
								training *t= (training*) traininglist[s];	//current WC
								feature *f= (feature*) featurelist[t->featureNumber];	//corresponding feature
								
								//feature *f=new feature(f1);
								f->setfeature(i+t->featoffsetX*scale, j+t->featoffsetY*scale, t->featScaleX*scale, t->featScaleY*scale);
								float val=(float)f->calcValue(integrals, EObins, reds, cin);
								
								float factor=1.0;	
								//if (t->featureNumber==6)		//if red feature
									factor=scale*scale;
								
								float dec=0;	//1 or -1 decision of 
								if (t->s*val<=t->s*t->theta*factor) {
									if (abs(t->pm-t->theta)<=0.00001)
										dec=1;
									else {
										if (!isFuzzy)
											dec=1;
										else
											dec = min(1,abs((val-t->theta)/(t->pm-t->theta)));
									}
								} else {
									if (abs(t->nm-t->theta)<=0.00001)
										dec =-1;
									else {
										if (!isFuzzy)
											dec=-1;
										else
											dec = max(-1,-abs((val-t->theta)/(t->nm-t->theta)));
									}
								}
								//update decision
								if (constAlpha)
									decision+=dec;
								else
									decision+=dec*t->alpha;
							}
							
							//printf("decision= %f\n", decision);

							//if decision >0, then honda found, remember it
							if (decision>0) {							
								CvPoint &p1 = cvPoint(i,j);
								CvPoint &p2 = cvPoint(i+windowWidth-1,j+windowHeight-1);
								
								//Create a rect
								struct rect *rec = new struct rect(p1,p2, decision);
								
								//check if is ok to put new rec in vector 'hondas'
								if (hondas.empty()) {
									hondas.push_back(rec);
								} else {
									bool add=true;	//determines whether or not to add *rec to 'hondas' or to replace with an esisting element
									for (int r=0; r<hondas.size(); r++) {
										rect *tra=(rect*) hondas[r];
										if (rec->isTooClose(tra)) {
											if (rec->decision>tra->decision) {
												tra->p1=rec->p1;			//replace the existing element with the better one
												tra->p2=rec->p2;
												tra->decision=rec->decision;
												add=false;	//do not add, we just replaced it
												r=hondas.size();	//stop loop
												delete rec;
											} else {
												delete rec;
												add=false;
												r=hondas.size();
											}
										}
									}
									if (add) {
										hondas.push_back(rec);	
									}
								}
								
								//cvRectangle( milos, p1, p2, CV_RGB(255,0,0), 1 );
								//cvShowImage("target", imageDatabase.getImage1(count));
							}
						}
					}
					scale=scale*incFactor;
				}

				//out of a group of overlapping positive windows, select and display only one
				for (rep=0; rep<hondas.size(); rep++) {
					rect *tra=(rect*) hondas[rep];
					for (int trav=rep+1; trav<hondas.size(); trav++) {
						rect *test=(rect*) hondas[trav];
						if (tra->isTooClose(test)) {
							if (test->decision>tra->decision) {
								tra->p1=test->p1;			//replace the existing element with the better one
								tra->p2=test->p2;
								tra->decision=test->decision;
							} else {
								test->p1=tra->p1;			//replace the existing element with the better one
								test->p2=tra->p2;
								test->decision=tra->decision;
							}
						}
					}
				}

				if (!hondas.empty()) {
					for (int r=0; r<hondas.size(); r++) {
						rect *tra=(rect*) hondas[r];
						cvRectangle( milos, tra->p1, tra->p2, CV_RGB(255,0,0), 1 );
						//printf("drawing rectangle p1 %i %i p2 %i %i \n", tra->p1.x, tra->p1.y, tra->p2.x, tra->p2.y); 
						//cvShowImage("target", milos);
						//cvWaitKey(0);						
					}
				}

				

				_ftime( &timebuffer2 );
				sec=timebuffer2.time-timebuffer1.time;
				millisec=timebuffer2.millitm-timebuffer1.millitm;
				sec+=millisec/1000;

				scale=1.0;
				windowWidth=100;
				windowHeight=50;
				cvShowImage("target", milos);
				
				printf ("runtime %f seconds \n", sec );
				printf("subwindows tested %i levels %i\n", subwindowcounter, levels);
				subwindowcounter=0;
				levels=0;
				cvReleaseImage(&reds);
				for (i=0; i<6; i++) 
					cvReleaseImage(&EObins[i]);
				
				cvReleaseImage(&cin);
				cvReleaseImage(&milos);
				cvReleaseImage(&integrals);
				hondas.clear();	//deallocate hondas

				break;
			case 'e':
				//imageDatabase.exit();
				goto exit_main;
		}
	}

	//next:
	//printf("before FINDBESTMATCH1\n");
	// Now do the image comparison
	//imageDatabase.findBestMatch1(pImageToMatch1, imageDatabase.getImageName(count), count);
	//printf("after FINDBESTMATCH1\n");
	cvReleaseImage(&pImageToMatch1);
	cvDestroyWindow("target");

	//cvDestroyWindow("targetSub");
	printf("Exiting:...\n");
	
	exit_main:
	for (di=0; di<featurelist.size(); ++di){			
		feature *e = (feature*) featurelist[di];
		delete e;
	}
	return 0;
}

#ifdef _EiC
main(1,"main.cpp");
#endif