#ifdef _CH_
#pragma package <opencv>
#endif

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

#include <stdio.h>
//#include <iostream.h>
#include <stdlib.h>
#include "ImageDatabase.h"
#include "feature.h"
#include "training.h"
#include "BestWC.h"
#include "edgeFeature.h"
#include "redFeature.h"

#include <vector>

#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))

//**************************** 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 pos*.jpg > positives.txt");	//make list of all positives
	system("dir /b/s neg*.jpg > negatives.txt");	//make list of all negatives
	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 positive database
	printf("Loading database: positives.txt ...\n");
	if(!imageDatabase.loadTraining("positives.txt", 0))	//0 for load positives
	{
		printf("Cannot load positives.txt database file\nProgram exited\n");
		return 1;
	}

	//load negatives database
	if(!imageDatabase.loadTraining("negatives.txt", 1))	//1 for load negatives
	{
		printf("Cannot load negatives.txt database file\nProgram exited\n");
		return 1;
	}

	int q=imageDatabase.getPosAndNeg();	//q is the number of pos and neg exxamples in database

	//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

	//create integral images from positives and negatives. 
	imageDatabase.createIntegrals();	//OK
		printf("regular integrals created\n");
	imageDatabase.createEOHIntegrals();
		printf("EOH integrals created\n");
	imageDatabase.createRedIntegrals();
		printf("red integrals created\n");
	std::vector<IplImage*> positiveIntegrals1 = imageDatabase.getPositiveIntegrals();
	std::vector<IplImage*> negativeIntegrals1 = imageDatabase.getNegativeIntegrals();

	int n=negativeIntegrals1.size();
	int p=positiveIntegrals1.size();
	float minpn=((float)min(n,p))/(float)q-0.01*((float)min(n,p))/(float)q;

	printf("starting training loop\n");
	//IplImage **milos;
	//milos=imageDatabase.EOHbins[0];

	//IplImage *hha=milos[0];

	//start training procedure
	std::vector<training*> traininglist;
	float minCombinedError=100.0;		//used in findBestWC
	int minCombinedErrorIndex=0;	//index of WC with combined lowest error.
	int runningtotalWC=0;
	int numberWCtried=0;

	//evaluate all features for all positions for all pos and neg images
	//store results in traininglist vector for further processing
	for (int numfeatures = 0; numfeatures < featurelist.size(); numfeatures++) {//for each feature
		printf("starting feature %i\n", numfeatures);
		feature *e = (feature*) featurelist[numfeatures];
		for (int widths=2; widths<(100/e->width)-2; widths+=2) {	//for each width (scaleX) of feature 2..45 (experimental values)
			printf("cu width = %i \n", widths);
			for (int heights=2; heights < (50/e->height)-2; heights+=2) {	//for each height (scaleY) of feature 2..20 (experimental..)
				for (int px=0; px<98-widths*e->width; px+=2) {	//for each x position in train image
					for (int py=0; py<48-heights*e->height; py+=2) {	//for each y position in train image
						if((px==14)&&(py==16)&&(widths==36)&&(heights==2)&&(numfeatures==4))
							printf("");

						e->setfeature(px, py, widths, heights);
						training *tr=new training();
						tr->settraining(numfeatures, px, py, widths, heights);
						//evaluate for all pos and neg
						for (int mil=0; mil<positiveIntegrals1.size(); mil++) {
							IplImage *pp = (IplImage*) positiveIntegrals1[mil];
							//IplImage *reds=(IplImage*) imageDatabase.redIntegrals[mil];
							int val=e->calcValue(pp, imageDatabase.EOHbins[mil], (IplImage*) imageDatabase.redIntegrals[mil]);
							tr->addRec(1,val);
						}
						
						for (mil=0; mil<negativeIntegrals1.size(); mil++) {
							IplImage *pp = (IplImage*) negativeIntegrals1[mil];
							//IplImage *reds=(IplImage*) imageDatabase.redIntegrals[positiveIntegrals1.size()+mil];
							int val=e->calcValue(pp, imageDatabase.EOHbins[positiveIntegrals1.size()+mil], (IplImage*) imageDatabase.redIntegrals[positiveIntegrals1.size()+mil]);
							tr->addRec(-1,val);
						}
						
						tr->sortRecs();
						std::vector<trainingRecord*> trainers = tr->getTrainRecs();
						/*if((px==14)&&(py==16)&&(widths==36)&&(heights==2)&&(numfeatures==4)) {
						for (int idi=0; idi<trainers.size(); ++idi){	//OK
							trainingRecord *e = (trainingRecord*) trainers[idi];
							e->printRecord();
						}
						}*/
 						tr->findWeakClassifier();
						
						numberWCtried++;

						if (tr->ce<=minpn) {		//good move: reduces number of WC respect to minpn
							if (tr->ce<minCombinedError) {
								if(tr->ce==0){
									printf("zero found at index %i!\n", minCombinedErrorIndex);
									printf("it is featno %i, with offset x %i, and offsety %i, scalex %i, scaley %i, CE = %f, theta = %f, sign = %i\n\n", tr->featureNumber, tr->featoffsetX, tr->featoffsetY, tr->featScaleX, tr->featScaleY, tr->ce, tr->theta, (int)tr->s);
								}
								
								minCombinedError=tr->ce;
								minCombinedErrorIndex=traininglist.size();
							}
							tr->freeVectors();	//frees memory stored for each feature
							traininglist.push_back(tr);
							runningtotalWC++;
							if (runningtotalWC%1000==0)
								printf("%i WCs\n", runningtotalWC);
						} else {
							delete tr;
						}
					}
				}
			}
		}
	}

	printf("done training features. We have %i WC in the traininglist vector\n", traininglist.size());
	printf("total number of WC tried is %i\n", numberWCtried);

	training *e = (training*) traininglist[minCombinedErrorIndex];
	printf("first classifier is found!\n");
	printf("it is featno %i, with offset x %i, and offsety %i, scalex %i, scaley %i, CE = %f, theta = %f, sign = %i\n\n", e->featureNumber, e->featoffsetX, e->featoffsetY, e->featScaleX, e->featScaleY, e->ce, e->theta, (int)e->s);

	CvPoint p1 = cvPoint(e->featoffsetX,e->featoffsetY);
	CvPoint p2 = cvPoint(e->featoffsetX+e->featScaleX*2,e->featoffsetY+e->featScaleY*2);

	//find best weak classifiers
	printf("starting bestWC find process\n");
	BestWC *best=new BestWC((float)0.999, (float)0.05, positiveIntegrals1.size(), negativeIntegrals1.size());
	best->findBestWC(traininglist, minCombinedErrorIndex, featurelist, imageDatabase.positiveIntegrals, 
		imageDatabase.negativeIntegrals, imageDatabase.redIntegrals, imageDatabase.EOHbins);	//check
	best->printWCs();
	pImageToMatch1= imageDatabase.getImage1(0);
	best->showWCs(pImageToMatch1);

	for (di=0; di<featurelist.size(); ++di){			
		feature *e = (feature*) featurelist[di];
		delete e;
	}

	for (di=0; di<traininglist.size(); ++di){			
		training *e = (training*) traininglist[di];
		delete e;
	}

	printf("Loading first image:\n");
	cvRectangle( pImageToMatch1, p1, p2, CV_RGB(255,0,0), 1 );
	
	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);

	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 best match 		n\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 'e':
				//imageDatabase.exit();
				goto exit_main;
		}
	}

	cvReleaseImage(&pImageToMatch1);
	cvDestroyWindow("target");
	printf("Exiting:...\n");
	
	exit_main:
	return 0;
}

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