#define PI 3.141592654

#include <windows.h>
#include "common.h"


typedef struct {
	int gradient;
	int direction;
} edge_info;



/* Function to return the gradient image */

Image_data *GetGradientImage(Image_data *piData)
{
	long i, width, height, numpixels;
	double dx, dy;
	Image_data *pproData;

	width = piData->n_cols;
	height = piData->n_rows;

	numpixels = width * height;

/* ----------- Memory allocation stuff ------------- */

	pproData = malloc(sizeof(Image_data));
	pproData->image_G_ptr = malloc(numpixels);
	pproData->n_cols = piData->n_cols;
	pproData->n_rows = piData->n_rows;
	lstrcpy(pproData->filename, "Gradient Image");

/* ------------------------------------------------- */



	/* Loop through all pixels and apply sobel operator */

	for (i = width + 1; i < numpixels - width - 1; i++)
	{
		/* Apply the Sobel operator */

		dx = (piData->image_G_ptr[i-1]*-2 +
			  piData->image_G_ptr[i+1]*2 +
			  piData->image_G_ptr[i-width-1]*-1 +
			  piData->image_G_ptr[i-width+1]*1 +
			  piData->image_G_ptr[i+width-1]*-1 +
			  piData->image_G_ptr[i+width+1]*1) / 8.0;

		
		dy = (piData->image_G_ptr[i-width]*2 +
			  piData->image_G_ptr[i+width]*-2 +
			  piData->image_G_ptr[i-width-1]*1 +
			  piData->image_G_ptr[i-width+1]*1 +
			  piData->image_G_ptr[i+width-1]*-1 +
			  piData->image_G_ptr[i+width+1]*-1) / 8.0;


		pproData->image_G_ptr[i] = (int)sqrt(dx*dx + dy*dy);
	}

	return pproData;
}



/* Function to return both gradient and direction info */

edge_info *GetGradientDirectionImage(Image_data *piData)
{
	long i, width, height, numpixels;
	double dx, dy, dir;
	edge_info	*edge;

	width = piData->n_cols;
	height = piData->n_rows;

	numpixels = width * height;

	edge = malloc(numpixels * sizeof(edge_info));


	/* Loop through all pixels and apply the Sobel operator */

	for (i = width + 1; i < numpixels - width - 1; i++)
	{
		/* Apply the Sobel operator */

		dx = (piData->image_G_ptr[i-1]*-2 +
			  piData->image_G_ptr[i+1]*2 +
			  piData->image_G_ptr[i-width-1]*-1 +
			  piData->image_G_ptr[i-width+1]*1 +
			  piData->image_G_ptr[i+width-1]*-1 +
			  piData->image_G_ptr[i+width+1]*1) / 8.0;

		
		dy = (piData->image_G_ptr[i-width]*2 +
			  piData->image_G_ptr[i+width]*-2 +
			  piData->image_G_ptr[i-width-1]*1 +
			  piData->image_G_ptr[i-width+1]*1 +
			  piData->image_G_ptr[i+width-1]*-1 +
			  piData->image_G_ptr[i+width+1]*-1) / 8.0;


		if (dx == 0)	/* Avoid divide by zero */
		{
			if (dy == 0)
				dir = 0.0;
			else if (dy > 0)
				dir = PI/2.0;
			else
				dir = PI/-2.0;
		}
		else			/* It's okay to compute atan() */
			dir = atan2(dy, dx);


		edge[i].gradient = (int)sqrt(dx*dx + dy*dy);
		edge[i].direction = (int)(dir * 180.0/ PI);  /* Converted to degress */
	}

	return edge;
}



/* Function to mark edges using just the gradient information */

Image_data *FindEdgesUsingGradient(Image_data *piData, int threshold)
{
	Image_data *pproData, *gradient;
	long i, numpixels;

	numpixels = piData->n_cols * piData->n_rows;

	/* ----------- Memory allocation stuff -------------- */

	pproData = malloc(sizeof(Image_data));
	pproData->image_G_ptr = calloc(numpixels, sizeof(unsigned char));
	pproData->n_cols = piData->n_cols;
	pproData->n_rows = piData->n_rows;
	lstrcpy(pproData->filename, "Edge Image");

	/* ------------------------------------------------- */


	gradient = GetGradientImage(piData);


	/* Loop through all pixels and mark edges with 
	   gradient greater than threshold as 255 (white) */

	for (i = 0; i < numpixels; i++)
	{
		if (gradient->image_G_ptr[i] > threshold)
			pproData->image_G_ptr[i] = 255;
	}


	/* Free up allocated memory */

	free(gradient->image_G_ptr);
	free(gradient);

	return pproData;
}



/* Function to mark edges using gradient and direction info */

Image_data *FindEdgesUsingGradientDirection(Image_data *piData, int threshold, int direction)
{
	Image_data *pproData;
	edge_info *edge;
	long i, numpixels;

	numpixels = piData->n_cols * piData->n_rows;

	/* ----------- Memory allocation stuff -------------- */

	pproData = malloc(sizeof(Image_data));
	pproData->image_G_ptr = calloc(numpixels, sizeof(unsigned char));
	pproData->n_cols = piData->n_cols;
	pproData->n_rows = piData->n_rows;
	lstrcpy(pproData->filename, "Edge Image");

	/* ------------------------------------------------- */


	edge = GetGradientDirectionImage(piData);


	/* 
	   Loop through all pixels and check gradient threshold
	   as well as direction with a +/- 10 degrees tolerance 
	   Note that user may enter any angle from -180 to +180,
	   so we must take into account pairs like 90 and -90, 
	   100 and -80 and so on since they lie on the same 
	   straight line...
	*/

	for (i = 0; i < numpixels; i++)
	{
		if (edge[i].gradient > threshold)
			if (((edge[i].direction > direction-10) && 
				 (edge[i].direction < direction+10)) ||
				((edge[i].direction > direction-180-10) && 
				 (edge[i].direction < direction-180+10)) ||
				((edge[i].direction > direction+180-10) && 
				 (edge[i].direction < direction+180+10)))
				pproData->image_G_ptr[i] = 255;
	}

	
	/* free up allocated memory */
	
	free(edge);

	return pproData;
}
