#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "vision_utilities.h"

int setSize(Image *im, int rows, int columns) {
	im->rows=rows;
	im->columns=columns;
	im->data = (uchar *) malloc(rows * columns * sizeof(char));
	if(im->data == NULL) {
		fprintf(stderr, "Insufficient memory for new image\n");
		exit(1); 
	} 
	setColors(im, 255);
	return 0;
}

int setSizeColor(ImageColor *im, int rows, int columns) {
	im->rows=rows;
	im->columns=columns;
	im->dataR = (uchar *) malloc(rows * columns * sizeof(char));
	if(im->dataR == NULL) {
		fprintf(stderr, "Insufficient memory for new image\n");
		exit(1); 
	}

	im->dataG = (uchar *) malloc(rows * columns * sizeof(char));
	if(im->dataG == NULL) {
		fprintf(stderr, "Insufficient memory for new image\n");
		exit(1); 
	}

	im->dataB = (uchar *) malloc(rows * columns * sizeof(char));
	if(im->dataB == NULL) {
		fprintf(stderr, "Insufficient memory for new image\n");
		exit(1); 
	} 

	setColorsColor(im, 255);
	return 0;
}

int getNCols(const Image *im) {
	return im->columns;
}

int getNColsColor(const ImageColor *im) {
	return im->columns;
}

int getNRows(const Image *im) {
	return im->rows;
}

int getNRowsColor(const ImageColor *im) {
	return im->rows;
}

int setColors(Image *im, int colors) {
	im->colors=colors;
	return 0;
}

int setColorsColor(ImageColor *im, int colors) {
	im->colors=colors;
	return 0;
}

int getColors(const Image *im) {
	return im->colors;
}

int getColorsColor(const ImageColor *im) {
	return im->colors;
}

int setPixel(Image *im, int i, int j, int color) {
	i=CLAMP(i, 0, im->rows-1);
	j=CLAMP(j, 0, im->columns-1);
	im->data[i*(im->columns)+j]=color;
	return 0;
}

int setPixelColor(ImageColor *im, int i, int j, int colorR, int colorG, int colorB) {
	i=CLAMP(i, 0, im->rows-1);
	j=CLAMP(j, 0, im->columns-1);
	im->dataR[i*(im->columns)+j]=colorR;
	im->dataG[i*(im->columns)+j]=colorG;
	im->dataB[i*(im->columns)+j]=colorB;
	return 0;
}

int getPixel(const Image *im, int i, int j) {
	i=CLAMP(i, 0, im->rows-1);
	j=CLAMP(j, 0, im->columns-1);
	return (int) im->data[i*(im->columns)+j];
}

int getPixelColor(const ImageColor *im, int i, int j, int colorNum) {
	i=CLAMP(i, 0, im->rows-1);
	j=CLAMP(j, 0, im->columns-1);
	if (colorNum==1)  return (int) im->dataR[i*(im->columns)+j];
	if (colorNum==2)  return (int) im->dataG[i*(im->columns)+j];
	if (colorNum==3)  return (int) im->dataB[i*(im->columns)+j];
	return -1;
}

int readImage(Image *im, const char *fname) {  
	FILE *input;
	char line[1024];     
	int nCols,nRows;  
	int levels;
	int i, j;

	/* open it */
	if (!fname || (input=fopen(fname,"rb"))==0)
		return-1;

	/* check for the right "magic number" */
	if (
			fread(line,1,3,input)!=3
			||strncmp(line,"P5\n",3)
	   )
	{
		fclose(input);
		return -1;
	}

	/* skip the comments */
	do
		fgets(line,sizeof line,input);
	while(*line=='#');

	/* read the width and height */
	sscanf(line,"%d %d\n",&nCols,&nRows);
	setSize(im, nRows, nCols);

	/* read # of gray levels */
	fgets(line,sizeof line,input);
	sscanf(line,"%d\n",&levels);
	setColors(im, levels);

	/* read pixel row by row */
	for(i=0;i<nRows;i++)
	{
		for(j=0;j<nCols;j++)
		{
			int byte=fgetc(input);

			if (byte==EOF) /* short file */
			{
				fclose(input);
				return -1;
			}
			else
				setPixel(im,i,j,byte);	
		}
	}

	/* close the file */
	fclose(input);
	return 0; /* OK */
}

int readImageColor(ImageColor *im, const char *fname) {  
	FILE *input;
	char line[1024];     
	int nCols,nRows;  
	int levels;
	int i, j;

	/* open it */
	if (!fname || (input=fopen(fname,"rb"))==0)
		return-1;

	/* check for the right "magic number" */
	if (
			fread(line,1,3,input)!=3
			||strncmp(line,"P6\n",3)
	   )
	{
		fclose(input);
		return -1;
	}

	/* skip the comments */
	do
		fgets(line,sizeof line,input);
	while(*line=='#');

	/* read the width and height */
	sscanf(line,"%d %d\n",&nCols,&nRows);
	setSizeColor(im, nRows, nCols);

	/* read # of gray levels */
	fgets(line,sizeof line,input);
	sscanf(line,"%d\n",&levels);
	setColorsColor(im, levels);

	/* read pixel row by row */
	for(i=0;i<nRows;i++)
	{
		for(j=0;j<nCols;j++)
		{
			int byteR, byteG, byteB;
			byteR=fgetc(input);
			if (byteR==EOF) /* short file */
			{
				fclose(input);
				return -1;
			}
			byteG=fgetc(input);
			if (byteG==EOF) /* short file */
			{
				fclose(input);
				return -1;
			}
			byteB=fgetc(input);
			if (byteB==EOF) /* short file */
			{
				fclose(input);
				return -1;
			}
			else
				setPixelColor(im,i,j, byteR, byteG, byteB);	
		}
	}

	/* close the file */
	fclose(input);
	return 0; /* OK */
}

int writeImage(const Image *im, const char *fname) {
	FILE *output; 
	int nRows;
	int nCols;
	int colors;
	int i, j;

	/* open the file */
	if (!fname || (output=fopen(fname,"wb"))==0)
		return(-1);

	nRows=getNRows(im);
	nCols=getNCols(im);
	colors=getColors(im);

	/* write the header */
	fprintf(output,"P5\n"); /* magic number */
	fprintf(output,"#\n");  /* empty comment */
	fprintf(output,"%d %d\n%03d\n",nCols,nRows,colors); /* image info */

	/* write pixels row by row */
	for(i=0;i<nRows;i++)
	{
		for(j=0;j<nCols;j++)
		{
			int byte=getPixel(im,i,j);

			if (fputc(byte,output)==EOF) /* couldn't write */
			{
				fclose(output);
				return -1;
			}      
		}
	}

	/* close the file */
	fclose(output);
	return 0; /* OK */
}

int writeImageColor(const ImageColor *im, const char *fname) {
	FILE *output; 
	int nRows;
	int nCols;
	int colors;
	int i, j;

	/* open the file */
	if (!fname || (output=fopen(fname, "wb"))==0)
		return(-1);

	nRows=getNRowsColor(im);
	nCols=getNColsColor(im);
	colors=getColorsColor(im);

	/* write the header */
	fprintf(output,"P6\n"); /* magic number */
	fprintf(output,"#\n");  /* empty comment */
	fprintf(output,"%d %d\n%03d\n",nCols,nRows,colors); /* image info */

	/* write pixels row by row */
	for(i=0;i<nRows;i++)
	{
		for(j=0;j<nCols;j++)
		{
			int byteR, byteG, byteB;
			byteR=getPixelColor(im,i,j, 1);
			if (fputc(byteR,output)==EOF) /* couldn't write */
			{
				fclose(output);
				return -1;
			}      
			byteG=getPixelColor(im,i,j, 2);
			if (fputc(byteG,output)==EOF) /* couldn't write */
			{
				fclose(output);
				return -1;
			}      
			byteB=getPixelColor(im,i,j, 3);
			if (fputc(byteB,output)==EOF) /* couldn't write */
			{
				fclose(output);
				return -1;
			}      
		}
	}

	/* close the file */
	if (fclose(output)==EOF) return -1;
	return 0; /* OK */
}

int line(Image *im, int y0, int x0, int y1, int x1, int color) {  
	int xmin,xmax; /* line coordinates */
	int ymin,ymax;
	int dir;       /* scan direction */
	int dx;        /* distance along X */
	int dy;        /* distance along Y */

	/* increments: East, North-East, South, South-East, North */
	int incrE,
		incrNE,
		incrSE;
	int d;         /* the D */
	int x,y;       /* running coordinates */
	int mpCase;    /* midpoint algorithm's case */
	int done;      /* set to 1 when done */

	/* TODO note that the x and y are switched! */
	xmin=y0;
	xmax=y1;
	ymin=x0;
	ymax=x1;

	dx=xmax-xmin;
	dy=ymax-ymin;

	if (dx*dx>dy*dy) /* horizontal scan */
	{
		dir=DIR_X;
		if (xmax<xmin)
		{
			SWAP(xmin,xmax);
			SWAP(ymin,ymax);
		}
		dx=xmax-xmin;
		dy=ymax-ymin;

		if (dy>=0)
		{
			mpCase=1;
			d=2*dy-dx;      
		}
		else
		{
			mpCase=2;
			d=2*dy+dx;      
		}

		incrNE=2*(dy-dx);
		incrE=2*dy;
		incrSE=2*(dy+dx);
	}
	else /* vertical scan */
	{
		dir=DIR_Y;
		if (ymax<ymin)
		{
			SWAP(xmin,xmax);
			SWAP(ymin,ymax);
		}
		dx=xmax-xmin;
		dy=ymax-ymin;    

		if (dx>=0)
		{
			mpCase=1;
			d=2*dx-dy;      
		}
		else
		{
			mpCase=2;
			d=2*dx+dy;      
		}

		incrNE=2*(dx-dy);
		incrE=2*dx;
		incrSE=2*(dx+dy);
	}

	/* start the scan */
	x=xmin;
	y=ymin;
	done=0;

	while(!done)
	{
		setPixel(im,x,y,color);

		/* move to the next point */
		switch(dir)
		{
			case DIR_X: /* horizontal */
				{
					if (x<xmax)
					{
						switch(mpCase)
						{
							case 1:
								if (d<=0)
								{
									d+=incrE;  x++;
								}
								else
								{
									d+=incrNE; x++; y++;
								}
								break;

							case 2:
								if (d<=0)
								{
									d+=incrSE; x++; y--;
								}
								else
								{
									d+=incrE;  x++;
								}
								break;
						} /* mpCase */
					} /* x<xmax */
					else
						done=1;
				}  
				break;

			case DIR_Y: /* vertical */
				{
					if (y<ymax)
					{
						switch(mpCase)
						{
							case 1:
								if (d<=0)
								{
									d+=incrE;  y++;
								}
								else
								{
									d+=incrNE; y++; x++;
								}
								break;

							case 2:
								if (d<=0)
								{
									d+=incrSE; y++; x--;
								}
								else
								{
									d+=incrE;  y++;
								}
								break;
						} /* mpCase */
					} /* y<ymin */
					else
						done=1;
				}
				break;    
		}
	}

	return 0; /* no error */
}
