#ifdef _CH_
#pragma package <opencv>
#endif

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

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>

#include "training.h"
#include "feature.h"
#include "BestWC.h"

BestWC::BestWC() {
	desiredDetect=0;
	desiredfalsepos=0;
	currentdetect=0;
	currentfalsepos=1;
	Tmax=30;
	numpos=0;
	numneg=0;
	combinedError=1;
	bestClassifiersAlpha=0;
}

BestWC::BestWC(float desDetect, float desfalsepos, int pos, int neg) {	//desired detection rate + falsepos rate
	desiredDetect=desDetect;
	desiredfalsepos=desfalsepos;
	currentdetect=0;		//current falsenegative rate
	currentfalsepos=1;		//current falsepositive rate
	Tmax=20;				
	numpos=pos;				//p number of positive training examples
	numneg=neg;				//n number of negative training examples
	combinedError=1;		//combined error of strong classifier
	bestClassifiersAlpha=0;	//current alpha of combined strong classifier

	alphaHX=new float[pos+neg];
	for (int i=0; i<(pos+neg); i++) {
		alphaHX[i]=0;
	}
}

BestWC::~BestWC() {
	desiredDetect=0;
	desiredfalsepos=0;

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

	delete [] alphaHX;
}

void BestWC::findBestWC(std::vector<training*>& trList, int indexFirstWC) {
	bool alreadyFound(int* alfound, int ind, int numindices);

	training *e=(training*) trList[indexFirstWC];
	float ealpha=e->alpha;
	float currentCE=e->ce;	//current cumulative error of strong classifier
	std::vector<trainingRecord*> bestrecords=e->getUnsortedTrainRecs();
	int *indices=new int[Tmax];
	int indicesCounter=0;

	//initialize the alpha x h(x) array for faster calculations later
	for (int k=0; k<(numpos+numneg); k++) {
		trainingRecord *trb=(trainingRecord*) bestrecords[k];
		float bval=(float)trb->getVal();
		
		short decision=0;	//1 or -1 decision of 
		if (e->s==1) {		//if sign ==1
			if (bval<=e->theta) {
				decision = 1;
			} else {
				decision =-1;
			}
		} else {	//s==-1
			if (bval<=e->theta) {
				decision = -1;
			} else {
				decision = 1;
			}
		}

		alphaHX[k]=(float)decision*ealpha;
	}
	
	training *er=new training(e);
	bestClassifiers.push_back(er);
	indices[indicesCounter]=indexFirstWC;
	indicesCounter++;

	currentdetect=(float)(numpos-e->missed)/(float)numpos;
	currentfalsepos=(float)e->falsepos/(float)numneg;

	short T=1;
	int curfalsepos=e->falsepos;	//best falsepos in loop
	int curmissed=e->missed;

	while ((currentdetect<=desiredDetect)||(currentfalsepos>=desiredfalsepos)) {
		int falsepos=0;
		int missed=0;
		float loopCE=currentCE;	//temp Current error of proposed WC 
		int bestindex=-1;	//index of proposed next WC addition

		for (int i=0; i<trList.size(); i++) {	//for each WC
			training *t=(training*) trList[i];	//get the WC
			float Ttheta=t->theta;
			std::vector<trainingRecord*> unsorted=t->getUnsortedTrainRecs();	//get trainingvectors
			float alpha=t->alpha;	//weight of current WC
			float alphaVal=0;

			//for every positive example
			for (int p=0; p<numpos; p++) {
				trainingRecord *trr=(trainingRecord*) unsorted[p];
				int val=trr->getVal();
				
				short decision=0;	//1 or -1 decision of 
				if (t->s==1) {		//if sign ==1
					if ((float)val<=Ttheta) {
						decision = 1;
					} else {
						decision =-1;
					}
				} else {	//s==-1
					if ((float)val<=Ttheta) {
						decision = -1;
					} else {
						decision = 1;
					}
				}
				
				alphaVal=alpha*(float)decision + alphaHX[p];
				
				if (alphaVal<0) {
					missed++;
				}
			}

			for (int n=numpos; n<numpos+numneg; n++) {
				trainingRecord *trr=(trainingRecord*) unsorted[n];
				int val=trr->getVal();

				short decision=0;	//1 or -1 decision of 
				if (t->s==1) {		//if sign ==1
					if ((float)val<=Ttheta) {
						decision = 1;
					} else {
						decision =-1;
					}
				} else {	//s==-1
					if ((float)val<=Ttheta) {
						decision = -1;
					} else {
						decision = 1;
					}
				}

				alphaVal=alpha*(float)decision + alphaHX[n];

				if (alphaVal>=0) {
					falsepos++;
				}
			}
			//use falsepos and missed to find error and weight.
			float ce=((float)missed+(float)falsepos)/(float)(numpos+numneg);
			
			if ((ce<loopCE)&&(!alreadyFound(indices, i, indicesCounter))) {
				bestindex=i;
				loopCE=ce;
				curfalsepos=falsepos;
				curmissed=missed;
			}
			missed=0;
			falsepos=0;
		}

		//index of next WC to be added to best classifier is found
		if (bestindex>=0) {	//if best classifier exists
			training *t=(training*) trList[bestindex];
			std::vector<trainingRecord*> bestaddition=t->getUnsortedTrainRecs();
			float alphaAdd=t->alpha;
			float Ttheta=t->theta;
			
			//update the alpha*h(x) array.
			for (k=0; k<(numpos+numneg); k++) {
				trainingRecord *trb=(trainingRecord*) bestaddition[k];
				float bval=(float)trb->getVal();
				
				short decision=0;	//1 or -1 decision of 
				if (t->s==1) {		//if sign ==1
					if (bval<=Ttheta) {
						decision = 1;
					} else {
						decision =-1;
					}
				} else {	//s==-1
					if (bval<=Ttheta) {
						decision = -1;
					} else {
						decision = 1;
					}
				}
				
				alphaHX[k]+=(float)decision*alphaAdd;
			}	
			training *er=new training(t);
			bestClassifiers.push_back(er);
			currentdetect=(float)(numpos-curmissed)/(float)numpos;
			currentfalsepos=(float)(curfalsepos)/(float)numneg;

			printf("currentdetect = %f currentfalsepos = %f at stage %i\n", currentdetect, currentfalsepos, T);
			
			indices[indicesCounter]=bestindex;	//add the best index to the array so that it is not chosen again by the machine
			indicesCounter++;

		} else {
			printf("no additional wc can increase accuracy\n");
			T=Tmax;
		}
		T++;
		if (T>=Tmax) {
			printf("Tmax reached\n");
			break;
		}
	}
	printf("final detection rate is %f\n", currentdetect);
	printf("final falsepos rate is  %f\n", currentfalsepos);
	delete [] indices;
}

bool alreadyFound(int* alfound, int ind, int numindices) {

	for (int i=0; i<numindices; i++) {
		if (ind==alfound[i])
			return true;
	}

	return false;
}

std::vector<training*>& BestWC::getBestWCs() {
	return bestClassifiers;
}

void BestWC::printWCs() {
	for (int mil=0; mil<bestClassifiers.size(); mil++) {
		training * e= (training*) bestClassifiers[mil];
		//printf("featno = %i, ", t->featureNumber);
		//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);
		printf("it is featno %i, with offset x %i, and offsety %i, scalex %i, scaley %i, CE = %f, theta = %f, sign = %i\n", e->featureNumber, e->featoffsetX, e->featoffsetY, e->featScaleX, e->featScaleY, e->ce, e->theta, (int)e->s);
	}

	printf("here is the alpha*h(x) array\n");

	for (int i=0; i<(numpos+numneg); i++) {
		printf("%f\n", alphaHX[i]);
	}
}

void BestWC::showWCs(IplImage *demo) {
	for (int mil=0; mil<bestClassifiers.size(); mil++) {
		training * e= (training*) bestClassifiers[mil];
		IplImage *c = cvCloneImage(demo);
		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 );
		else if (e->featureNumber==1)
			cvRectangle( c, p1, p2, CV_RGB(0,255,0), 1 );
		else if (e->featureNumber==2)
			cvRectangle( c, p1, p2, CV_RGB(0,0,255), 1 );
		else if (e->featureNumber==3)
			cvRectangle( c, p1, p2, CV_RGB(255,255,0), 1 );
		else 
			cvRectangle( c, p1, p2, CV_RGB(255,255,255), 1 );
		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");
}

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