#include <iostream>
#include <fstream>
#include <cstring> // needed for strcmp
#include <cstdlib> // needed for exit
#include "ppm.h"
using namespace std;

namespace math144_ppm
{
image::image()
{
	width = 0;
	height = 0;
}

image::image(string filename)
{
	open(filename);
}

image::image(const image &src)
{
	width = 0;
	height = 0;
	*this = src;
}

image::~image()
{
	for (unsigned int x = 0; x < width; x++) {
    		delete data[x];
	}
	if (width != 0) {
		delete data;
	}
}

void image::open(string filename)
{
	/* Open the input file */
	FILE *infile;
	infile = fopen(filename.c_str(), "rb");
  	if (infile == NULL) {
		cerr << "Error: Can not open " << filename << endl;
		exit(-1);
	}

	/* Check for the PPM code */
	char temp[100];
	fscanf(infile, "%s\n", temp);
	if (strcmp(temp, "P6") != 0) {
		cerr << "Error: " << filename << " is not a valid PPM file"
		     << endl;
		exit(-1);
	}

	/* Get the width and height */
  	fscanf(infile, "%d %d\n", &width, &height);

	/* Check the bits per sample */
	int colors;
	fscanf(infile, "%d\n", &colors);
	if (colors != 255) {
		cerr << "Error: Can not handle color format of " << filename
		     << endl;
		exit(-1);
	}

	/* Allocate space for the image */
	data = new double *[width];
  	for (unsigned int x = 0; x < width; x++) {
    		data[x] = new double[height];
	}

	/* Get each pixel of data */
  	for (int y = 0; y < height; y++) {
    		for (int x = 0; x < width; x++) {
			/* Get each RGB value */
  			unsigned char r, g, b;
			fread(&r, 1, 1, infile);
			fread(&g, 1, 1, infile);
			fread(&b, 1, 1, infile);

			/*
			 * Make sure each rgb value is the same, since we're
			 * using greyscale.
			 */
			if (r != g || r != b || g != r) {
				cerr << "Error: Can not handle non-greyscale "
				     << "image " << filename << endl;
				exit(-1);
			}

			/* Put the value in the data variable */
			data[x][y] = r;
		}
	}

	/* Close the file */
  	fclose(infile);
}

void image::save(string filename)
{
	/* Open the output file */
	FILE *outfile;
	outfile = fopen(filename.c_str(), "wb");
	if (outfile == NULL) {
		cerr << "Error: Can not write to " << filename << endl;
		exit(-1);
	}

	/* Write the PPM header */
	fprintf(outfile, "P6 %d %d 255\n", width, height);

	/* Write each pixel of data */
  	for (int y = 0; y < height; y++) {
    		for (int x = 0; x < width; x++) {
			/* Get the value to write */
			//unsigned char r = (unsigned char) (data[x][y]);
			unsigned char r;
			if (data[x][y] < 0) {
				r = 0;
			} else if (data[x][y] > 255) {
				r = 255;
			} else {
				r = (unsigned char) data[x][y];
			}

			/*
			 * Write the value three times since it's supposed to
			 * be RGB.
			 */
			fwrite(&r, 1, 1, outfile);
			fwrite(&r, 1, 1, outfile);
			fwrite(&r, 1, 1, outfile);
		}
	}

	/* Close the file */
	fclose(outfile);
}

void image::save_csv()
{
	fstream fout;
	fout.open("out.csv", fstream::out);
  	for (int y = 0; y < height; y++) {
    		for (int x = 0; x < width; x++) {
			unsigned char byte;
			if (data[x][y] < 0) {
				byte = 0;
			} else if (data[x][y] > 255) {
				byte = 255;
			} else {
				byte = (unsigned char) data[x][y];
			}
			fout << (int) byte << ",";
		}
		fout << endl;
	}
	fout.close();
}

image& image::operator =(const image &src)
{
	if (this == &src) {
		return *this;
	}

	/* Free any memory the destination image may be using */
	for (unsigned int i = 0; i < width; i++) {
    		delete data[i];
	}
	if (width != 0) {
		delete data;
	}

	/* Allocate new memory for the destination image */
	data = new double*[src.width];
  	for (unsigned int x = 0; x < src.width; x++) {
    		data[x] = new double[src.height];
	}

	/* Copy the source image */
	for (int x = 0; x < src.width; x++) {
		for (int y = 0; y < src.height; y++) {
			data[x][y] = src.data[x][y];
		}
	}
	width = src.width;
	height = src.height;
}

} // namespace math144

