// EE231002 Lab14. Image Processing
// 108061112, 林靖
// Date: Dec. 28, 2019

#include <stdio.h>				// Standard input and output library.
#include <stdlib.h>				// Standard library (malloc).

typedef struct sPIXEL {			// a single pixel
	unsigned char r, g, b;		//   three color components
} PIXEL;

typedef struct sIMG {			// an image of PPM style
	char header[3];				//   header, either P3 or P6
	int W, H;					//   width and height of the image
	int level;					//   intensity level of each color component
	PIXEL **PX;					//   two-dimensional array for all the pixels
} IMG;

IMG *PPMin(char *inFile);
// This function opens the inFile, reads the image data and returns a pointer
//   pointing to a newly created image data structure. 

void PPMout(IMG *p1, char *outFile);
// This function writes the image pointed by p1 to the output file outFile.

IMG *PPMcvt(IMG *p1, IMG *ee, IMG *nthu, int x1, int y1, int x2, int y2);
// This function processes the image pointed by p1 performing the modifications
//   and returns the new image as a result. The arguments x1, y1 are the
//   coordinates of the lower-left corner of the box which retains the color
//   image; while x2, y2 are the coordinates of the upper-right corner of the
//   box.

int main(int argc, char *argv[])				 // Called at program startup.
{
	IMG *p1,									 // The group photo.
		*ee,									 // EE department logo.
		*nthu;									 // NTHU logo.

	p1 = PPMin(argv[1]);						 // Read group photo from .ppm
	ee = PPMin(argv[2]);						 // Read EE logo from .ppm
	nthu = PPMin(argv[3]);						 // Read NTHU logo from .ppm
	PPMcvt(p1, ee, nthu, 910, 1780, 1090, 1600); // Perform modification
	PPMout(p1, argv[4]);						 // Print result as .ppm
	return 0;									 // Normal program termination.
}

// This function opens the inFile, reads the image data and returns a pointer
//   pointing to a newly created image data structure. 
IMG *PPMin(char *inFile)
{
	int i, j;								// Indices for looping.
	IMG *pic;								// Store newly created image.
	FILE *fin;								// To perform operation on the file.

	pic = (IMG *)malloc(sizeof(IMG));				  // Request memory to use.
	fin = fopen(inFile, "r");						  // Open the .ppm file.
	fscanf(fin, "%s", pic->header);					  // Read "P6"
	fscanf(fin, "%d %d\n%d\n",						  // Read width, height, and
		   &pic->W, &pic->H, &pic->level);			  //   the intensity level.
	pic->PX = (PIXEL **)malloc(pic->W * sizeof(PIXEL *));	  // Request a block
	for (i = 0; i < pic->W; i++) {							  //   of memory for
		pic->PX[i] = (PIXEL *)malloc(pic->H * sizeof(PIXEL)); //   each pixel to
	}														  //   use.
	for (j = 0; j < pic->H; j++) {					  // Pixels are arranged in
		for (i = 0; i < pic->W; i++) {				  //   column-major fashion.
			pic->PX[i][j].r = fgetc(fin);
			pic->PX[i][j].g = fgetc(fin);			  // Read each component.
			pic->PX[i][j].b = fgetc(fin);
		}
	}
	fclose(fin);							// Close this .ppm file.
	return pic;								// Return the newly created image.
}

// This function writes the image pointed by p1 to the output file outFile.
void PPMout(IMG *p1, char *outFile)
{
	int i, j;								// Indices for looping
	FILE *fout;								// To perform operation on the file

	fout = fopen(outFile, "w");					  // Open the .ppm file.
	fprintf(fout, "%s\n", p1->header);			  // Write "P6"
	fprintf(fout, "%d %d\n%d\n",				  // Write width, height, and
			p1->W, p1->H, p1->level);			  //   the intensity level.
	for (j = 0; j < p1->H; j++) {				  // The pixels are arranged in
		for (i = 0; i < p1->W; i++) {			  //   column-major fashion.
			fprintf(fout, "%c", p1->PX[i][j].r);
			fprintf(fout, "%c", p1->PX[i][j].g);  // Write each color component.
			fprintf(fout, "%c", p1->PX[i][j].b);
		}
	}
	fclose(fout);								  // Close this .ppm file.
	return;										  // Function termination.
}

// This function processes the image pointed by p1 performing the modifications
//   and returns the new image as a result. The arguments x1, y1 are the
//   coordinates of the lower-left corner of the box which retains the color
//   image; while x2, y2 are the coordinates of the upper-right corner of the
//   box.
IMG *PPMcvt(IMG *p1, IMG *ee, IMG *nthu, int x1, int y1, int x2, int y2)
{
	int i, j,							// Indices for looping
		gray,							// The luminance of a pixel
		X, Y;							// EE or NTHU logo's origin coordinate

	for (j = 0; j < p1->H; j++) {				  // The pixels are arranged in 
		for (i = 0; i < p1->W; i++) {			  //   column-major fashion.
			if (i < x1 ||						  // If the coordinate of this
				i > x2 ||						  //   pixel is outside the box.
				j < y2 ||
				j > y1) {
				gray = p1->PX[i][j].r * 0.2126 +  // Compute the luminance of
					   p1->PX[i][j].g * 0.7152 +  //   this pixel.
					   p1->PX[i][j].b * 0.0722;
				p1->PX[i][j].r = gray;
				p1->PX[i][j].g = gray;			  // Make this pixel gray-scale.
				p1->PX[i][j].b = gray;
			}
		}
	}

	X = p1->W - ee->W;								 // Locate the coordinate
	Y = p1->H - ee->H;								 //   origin of the EE logo.
	for (j = 0; j < ee->H; j++) {					 // Pixels are arranged in
		for (i = 0; i < ee->W; i++) {				 //   column-major fashion.
			if (ee->PX[i][j].r != 255 ||			 // If this EE logo pixel is
				ee->PX[i][j].g != 255 ||			 //   not white.
				ee->PX[i][j].b != 255) {
				p1->PX[X + i][Y + j] = ee->PX[i][j]; // Simply replace the pixel
			}										 //   of the original image
		}											 //   by the logo pixels.
	}

	X = (p1->W - nthu->W) / 2;						 // Locate the coordinate
	Y = (p1->H - nthu->H) / 2;						 //   origin of NTHU logo.
	for (j = 0; j < nthu->H; j++) {					 // Pixels are arranged in
		for (i = 0; i < nthu->W; i++) {				 //   column-major fashion.
			if (nthu->PX[i][j].r != 255 ||			 // If this NTHU logo pixel
				nthu->PX[i][j].g != 255 ||			 //   is not white.
				nthu->PX[i][j].b != 255) {
				p1->PX[X + i][Y + j].r = 255;		 // Make the pixel of the
				p1->PX[X + i][Y + j].b = 255;		 //   original image purple.
			}
		}
	}

	for (j = (y2 - 1); j <= (y1 + 1); j++) {		 // Pixels are arranged in
		for (i = (x1 - 1); i <= (x2 + 1); i++) {	 //   column-major fashion.
			if (i <= (x1 + 1) ||					 // If the coordinate of
				i >= (x2 - 1) ||					 //   this pixel is on the
				j <= (y2 + 1) ||					 //   3-pixel-wide frame of
				j >= (y1 - 1)) {					 //   the box.
				p1->PX[i][j].r = 0;
				p1->PX[i][j].g = 255;				 // Make this pixel cyan.
				p1->PX[i][j].b = 255;
			}
		}
	}
	return p1;						 		// Return the new image as result.
}
