
#include <stdlib.h> 
#include <stdio.h> 
#include <cmath>//for use of round, PI, atan
#include <iostream>
#include <fstream>
#include "vision_utilities.h"

using namespace std;

typedef struct _myObject
{
	int label;
	int rowPosition;
	int colPosition;
	double minMomentInertia;
	double angle;
	double roundness;
	int area;// I add it	
} MyObject;

const int MAXSIZE = 511;
double round(double x);

int p4(char *fname_input, char *dataBaseName, char *fname_output)
{
	Image *im_input = new Image;//input image
	Image *im_output = new Image;//output image
	readImage(im_input, fname_input);
	int NRows = getNRows(im_input);
	int NCols = getNCols(im_input);
	setSize(im_output,NRows,NCols);

	int maxLabel = 0;
	for (int i = 0; i<NRows; i++)
	{
		for (int j = 0; j<NCols; j++)
		{			
			int pixel = getPixel(im_input,i,j);//gray-level of the input image at (i,j)(x=j,y=i)			
			if (pixel > maxLabel)
			{
				maxLabel = pixel;
			}
		}
	}
	//We get the max label in the input image. It is also the number of objects in it.
	printf("The max label in the input image is: %d\n", maxLabel);
	setColors(im_output,maxLabel+5);

	/*=====PART ONE=====*/
	/*label, rowPosition, colPosition, area*/

	int *sumX = new int[maxLabel];for (int i = 0; i < maxLabel; i++) sumX[i] = 0;
	int *sumY = new int[maxLabel];for (int i = 0; i < maxLabel; i++) sumY[i] = 0;
	int *area = new int[maxLabel];for (int i = 0; i < maxLabel; i++) area[i] = 0;

	//MyObject *objList = new MyObject[maxLabel];
	MyObject objList[maxLabel];
	
	for (int i = 0; i<NRows; i++)
	{
		for (int j = 0; j<NCols; j++)
		{
			int pixel = getPixel(im_input,i,j);//gray-level of the input image at (i,j)(x=j,y=i)
			setPixel(im_output,i,j,pixel);
			if (pixel!=0)
			{
				sumX[pixel-1] += j;
				sumY[pixel-1] += i;
				area[pixel-1] ++;
			}
		}
	}

	for (int i = 0; i < maxLabel; i++)	
	{
		objList[i].label = i+1;
		objList[i].rowPosition = round(double(sumX[i])/area[i]);//x bar
		objList[i].colPosition = round(double(sumY[i])/area[i]);//y bar
		objList[i].area = area[i];
	}

	/*=====PART TWO=====*/
	/*minMomentInertia, angle, roundness*/

	long *sumA = new long[maxLabel];//sumA[i] is the "a" of the (i+1)th object
	for (int i = 0; i < maxLabel; i++) sumA[i] = 0;
	long *sumB = new long[maxLabel];//sumB[i] is the "b" of the (i+1)th object
	for (int i = 0; i < maxLabel; i++) sumB[i] = 0;
	long *sumC = new long[maxLabel];//sumC[i] is the "c" of the (i+1)th object
	for (int i = 0; i < maxLabel; i++) sumC[i] = 0;

	for (int i = 0; i<NRows; i++)
	{
		for (int j = 0; j<NCols; j++)
		{
			int pixel = getPixel(im_input,i,j);//gray-level of the input image at (i,j)
			if (pixel!=0)//compute a,b,c for (pixel)th object
			{
				sumA[pixel-1] += ((j - objList[pixel-1].rowPosition)*(j - objList[pixel-1].rowPosition));//(x-xbar)^2
				sumB[pixel-1] += 2*((i - objList[pixel-1].colPosition)*(j - objList[pixel-1].rowPosition));//2(x-xbar)(y-ybar)
				sumC[pixel-1] += ((i - objList[pixel-1].colPosition)*(i - objList[pixel-1].colPosition));//(y-ybar)^1
			}
		}
	}

	for (int i = 0; i < maxLabel; i++)	
	{
		double angle1, angle2;
		if (sumA[i]==sumC[i]) angle1 = PI/4;
		else 
		{
			double tan2angle = double(sumB[i])/(sumA[i]-sumC[i]);//tan(2theta)=b/(a-c)
			angle1 = 0.5*atan(tan2angle);//not in degree!!!
			//we LATER change it to degrees and clamp it into [0,180).
		}
		long double E1 = sumA[i]*(sin(angle1))*(sin(angle1))-sumB[i]*(sin(angle1))*(cos(angle1))+sumC[i]*(cos(angle1))*(cos(angle1));
		angle2 = angle1 + PI/2;
		long double E2 = sumA[i]*(sin(angle2))*(sin(angle2))-sumB[i]*(sin(angle2))*(cos(angle2))+sumC[i]*(cos(angle2))*(cos(angle2));
		if (E1 >= E2)//we compare the two E and decide which one is the maximum and which one is the minimum.
		{
			objList[i].minMomentInertia = E2;
			objList[i].angle = angle2/PI*180;// now in degree
			objList[i].roundness = E2/E1;//Emin/Emax
		}
		else
		{
			objList[i].minMomentInertia = E1;
			objList[i].angle = angle1/PI*180;// now in degree	
			objList[i].roundness = E1/E2;//Emin/Emax		
		}
		
		if (objList[i].angle < 0) objList[i].angle += 180;//clamp it into [0,180)
		if (objList[i].angle >= 180) objList[i].angle -= 180;//clamp it into [0,180)
	}

	/*=====PART THREE=====*/
	/*read the database and compare*/	

	ifstream fDataBase(dataBaseName);
	MyObject *standardObjList = new MyObject[MAXSIZE];
	int numStandardObject = 0;
	while(!fDataBase.eof())
	{
		fDataBase.read((char *)(&standardObjList[numStandardObject]), sizeof(standardObjList[numStandardObject]));
		numStandardObject++;
	}
	numStandardObject--;
	printf("We have %d standard objects.\n", numStandardObject);
	for (int i = 0; i < numStandardObject; i++)
		printf("Attributes of standard object %d:\n%d, %d, %d, %f, %f, %f, %d\n",(i+1),
		standardObjList[i].label,
		standardObjList[i].rowPosition,
		standardObjList[i].colPosition,
		standardObjList[i].minMomentInertia,
		standardObjList[i].angle,
		standardObjList[i].roundness,
		standardObjList[i].area);

	for (int i = 0; i < maxLabel; i++)
		printf("Attributes of object %d:\n%d, %d, %d, %f, %f, %f, %d\n",(i+1),
		objList[i].label,
		objList[i].rowPosition,
		objList[i].colPosition,
		objList[i].minMomentInertia,
		objList[i].angle,
		objList[i].roundness,
		objList[i].area);

	bool *isRecognized = new bool[maxLabel];//1 if recognized, 0 if not recognized

	for (int i = 0; i < maxLabel; i++)
	{
		isRecognized[i] = false;
		for (int j = 0; j < numStandardObject; j++)
		{
			if ((objList[i].area > 0.8*standardObjList[j].area)
				&&(objList[i].area < 1.25*standardObjList[j].area)
				&&(objList[i].minMomentInertia > 0.8*standardObjList[j].minMomentInertia)
				&&(objList[i].minMomentInertia < 1.25*standardObjList[j].minMomentInertia)
				&&(objList[i].roundness > 0.8*standardObjList[j].roundness)
				&&(objList[i].roundness < 1.25*standardObjList[j].roundness))
			{
				isRecognized[i] = true;
				printf("Object(%d) is recognized as standard object(%d)\n",i+1,j+1);
				break;
			}
		}
	}

	/*=====PART FOUR=====*/
	/*draw the output image*/

	for (int i = 0; i < maxLabel; i++)	
	{
		if (isRecognized[i])
		{
			setPixel(im_output, objList[i].colPosition-1, objList[i].rowPosition, maxLabel+5);//draw the center (white point)
			setPixel(im_output, objList[i].colPosition+1, objList[i].rowPosition, maxLabel+5);//draw the center (white point)
			setPixel(im_output, objList[i].colPosition, objList[i].rowPosition-1, maxLabel+5);//draw the center (white point)
			setPixel(im_output, objList[i].colPosition, objList[i].rowPosition+1, maxLabel+5);//draw the center (white point)
			line(im_output, 
				objList[i].colPosition - 50*sin(objList[i].angle/180*PI), 
				objList[i].rowPosition - 50*cos(objList[i].angle/180*PI), 
				objList[i].colPosition + 50*sin(objList[i].angle/180*PI),
				objList[i].rowPosition + 50*cos(objList[i].angle/180*PI),
				maxLabel+2);//draw the line with the length of 100 pixel
		}
	}

	writeImage(im_output, fname_output);
	printf("U got it\n");
	return 0;
}

double round(double x)
{
	if (x<floor(x)+0.50) return floor(x);
	else return (floor(x)+1);
}

int main(int argc, char **argv) 
{ 
	if (argc <= 3){ 
		printf("Usage: %s <input image> <intput database> <output image>\n", argv[0]); 
		return 0; 
	} 
	char *inputFilename = argv[1]; 
	char *dataBaseName = argv[2];//dataBaseName can be data1.dat
	char *outputFilename = argv[3]; 
	p4(inputFilename,dataBaseName,outputFilename); 
	return 0; 
}
