
#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;

double round(double x);

int p3(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++)//this loop is for getting the max label in the input image
	{
		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[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)
			//here the pixel is the LABEL!
			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)
			if (atan(tan2angle)>0) {angle1 = 0.5*atan(tan2angle);}//not in degree!!!
			else {angle1 = 0.5*atan(tan2angle)+PI/2;}//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=====*/
	/*draw the output image*/	

	for (int i = 0; i < maxLabel; 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);

	/*=====PART FOUR=====*/
	/*write the output database to a file*/
	ofstream fDataBase(dataBaseName);
	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);
		fDataBase.write((char *)(&objList[i]),sizeof(objList[i]));
	}
	fDataBase.close();
	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> <output 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]; 
	p3(inputFilename,dataBaseName,outputFilename); 
	return 0; 
}
