import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PushbackReader;

public class imagemap {
	public int width;
	public int height;
	public int maxColorValue = 255;
	public pixel[][] pixels;
	public PushbackReader stream;
	
	public imagemap(int width, int height, pixel[][] pixels) {
		this.width = width;
		this.height = height;
		this.pixels = pixels;
	}
	
	public imagemap(String path) throws Exception {
		try {
			stream = new PushbackReader(new FileReader(path));
		} catch (Exception e) {
			throw new Exception("File not found.");
		}
		try {
			readHeader();
			pixels = new pixel[height][width];
			readPixels();
		} catch (Exception e) {
			throw new Exception("File format not recognized.");
		}
	}
	
	public imagemap set(int r, int c, pixel p) {
		pixels[r][c] = p;
		return this;
	}

	public pixel get(int r, int c) {
		return pixels[r][c];
	}
	
	
	private imagemap readHeader() throws Exception {
		return
			uniqueNum()
			.getDims()
			.getMaxColor();
	}
	
	public pixel[] get(int r, int c1, int c2) {
		if (c2 >= pixels[r].length) c2 = pixels[r].length - 1;
		pixel[] curPixels = new pixel[c2 - c1 + 1];
		for (int i = 0; c1 <= c2; i++, c1++) {
			curPixels[i] = get(r, c1);
		}
		return curPixels;
	}
    
	private imagemap uniqueNum() throws Exception {
		invalChar();
		if (!readWord().equals("P3")) {
			throw new Exception("File format not recognized.");
		}
		invalChar();
		return this;
	}
	
	private imagemap readPixels() throws NumberFormatException, IOException {
		for (int r = 0; r < height; r++) {
			for (int c = 0; c < width; c++) {
				int red = Integer.parseInt(readWord());
				invalChar();
				int green = Integer.parseInt(readWord());
				invalChar();
				int blue = Integer.parseInt(readWord());
				invalChar();
				pixels[r][c] = new pixel(red, green, blue);
			}
		}
		return this;
	}

	private imagemap getDims() throws NumberFormatException, IOException {
		width = Integer.parseInt(readWord());
		invalChar();
		height = Integer.parseInt(readWord());
		invalChar();
		return this;
	}
	
	private imagemap getMaxColor() throws NumberFormatException, IOException {
		maxColorValue = Integer.parseInt(readWord());
		invalChar();
		return this;
	}
	
	
	private String readWord() throws IOException {
		StringBuffer word = new StringBuffer();
		int c;
		while (isChar(c = stream.read())) {
			word.append((char) c);
		}
		stream.unread(c);
		return word.toString();
	}
	
	private imagemap invalChar() throws IOException {
		int c;
		while (!isChar(c = stream.read())) {
			if (c == '#') {
				while ((char) (c = stream.read()) != '\n' && c > -1);
				stream.unread(c);
			}
		}
		stream.unread(c);
		return this;
	}
	
	public imagemap copyAll() {
		pixel[][] curPixels = new pixel[pixels.length][];
		for (int i = 0; i < pixels.length; i++)
			curPixels[i] = pixels[i].clone();
		return new imagemap(width, height, curPixels);			
	}

	private boolean isChar(char c) {
		return (c != '#' && c != '\n' && c != '\r' && c != '\t' && c != ' ');
	}
	
	public String headerString() {
		return "P3\n" + width + " " + height + "\n" + maxColorValue + "\n";
	}

	private boolean isChar(int c) {
		return isChar((char) c);
	}
	
	public imagemap getEveryOne(pkpix cb) {
		for (int r = 0; r < pixels.length; r++) {
			for (int c = 0; c < pixels[r].length; c++) {
				cb.handle(this, pixels[r][c], r, c);
			}
		}
		return this;
	}

	public imagemap fileWritter(String path) throws IOException {
		FileWriter w = new FileWriter(path);
		String header = "P3\n" + width + " " + height + "\n" + maxColorValue + "\n";
		w.write(header);
		for (pixel[] row : pixels) {
			for (pixel p : row) {
				w.write(p.toString());
			}
			w.write('\n');
		}
		w.close();
		return this;
	}
	
}
