
// A high-performance class for drawing a window full
// of colored rectangles, using direct drawing with
// the getGraphics() method -- which is not considered
// to be good form.
//
// This MosaicCanvas is for use with MosaicFrame;
// see that class for details.
//
// David Eck (eck@hws.edu), 17 January 1998.
// Modified October 2005 to avoid deprecated methods.
// Modified October 2009 to use setPreferredSize().


import java.awt.*;

class MosaicCanvas extends Canvas {
	
	private int rows, columns;
	
	private Color defaultColor = Color.BLACK;
	
	private Color[][] grid;
	private boolean blemished = false;
	
	public MosaicCanvas(int rows, int columns) {
		this(rows,columns,0,0);
	}
	
	public MosaicCanvas(int rows, int columns, int blockWidth, int blockHeight) {
		this.rows = rows;
		this.columns = columns;
		grid = new Color[rows][columns];
		for (int i = 0; i < rows; i++)
			for (int j = 0; j < columns; j++)
				grid[i][j] = defaultColor;
		setBackground(defaultColor);
		setPreferredSize( new Dimension(columns*blockWidth, rows*blockHeight) );
	}
	
	public int getRows() {
		return rows;
	}
	
	public int getColumns() {
		return columns;
	}
	
	public synchronized void paint(Graphics g) {
		if (!blemished) {
			g.setColor(grid[0][0]);
			g.fillRect(0,0,getSize().width,getSize().height);
		}
		else {
			double rowHeight = (double)getSize().height / rows;
			double colWidth = (double)getSize().width / columns;
			for (int i = 0; i < rows; i++) {
				int y = (int)Math.round(rowHeight*i);
				int h = (int)Math.round(rowHeight*(i+1)) - y;
				for (int j = 0; j < columns; j++) {
					int x = (int)Math.round(colWidth*j);
					int w = (int)Math.round(colWidth*(j+1)) - x;
					g.setColor(grid[i][j]);
					g.fillRect(x,y,w,h);
				}
			}
			
		}
	}
	
	public synchronized void drawSquare(int row, int col) {
		double rowHeight = (double)getSize().height / rows;
		double colWidth = (double)getSize().width / columns;
		int y = (int)Math.round(rowHeight*row);
		int h = (int)Math.round(rowHeight*(row+1)) - y;
		int x = (int)Math.round(colWidth*col);
		int w = (int)Math.round(colWidth*(col+1)) - x;
		Graphics g = getGraphics();
		if (g == null)
			return;
		g.setColor(grid[row][col]);
		g.fillRect(x,y,w,h);
	}
	
	public void update(Graphics g) {
		paint(g);
	}
	
	public Color getColor(int row, int col) {
		if (row >=0 && row < rows && col >= 0 && col < columns)
			return grid[row][col];
		else
			return defaultColor;
	}
	
	public int getRed(int row, int col) {
		return getColor(row,col).getRed();
	}
	
	public int getGreen(int row, int col) {
		return getColor(row,col).getGreen();
	}
	
	public int getBlue(int row, int col) {
		return getColor(row,col).getBlue();
	}
	
	public void setColor(int row, int col, Color c) {
		if (row >=0 && row < rows && col >= 0 && col < columns && c != null) {
			grid[row][col] = (c == null)? defaultColor : c;
			blemished = true;
			drawSquare(row,col);
		}
	}
	
	public void setColor(int row, int col, int red, int green, int blue) {
		if (row >=0 && row < rows && col >= 0 && col < columns) {
			red = (red < 0)? 0 : ( (red > 255)? 255 : red);
			green = (green < 0)? 0 : ( (green > 255)? 255 : green);
			blue = (blue < 0)? 0 : ( (blue > 255)? 255 : blue);
			grid[row][col] = new Color(red,green,blue);
			drawSquare(row,col);
			blemished = true;
		}
	}
	
	public void fill(Color c) {
		if (c == null)
			c = defaultColor;
		for (int i = 0; i < rows; i++)
			for (int j = 0; j < columns; j++)
				grid[i][j] = c;
		blemished = false;
		repaint();      
	}
	
	public void fill(int red, int green, int blue) {
		red = (red < 0)? 0 : ( (red > 255)? 255 : red);
		green = (green < 0)? 0 : ( (green > 255)? 255 : green);
		blue = (blue < 0)? 0 : ( (blue > 255)? 255 : blue);
		fill(new Color(red,green,blue));
	}
	
	public void fillRandomly() {
		for (int i = 0; i < rows; i++)
			for (int j = 0; j < columns; j++) {
				int r = (int)(256*Math.random());
				int g = (int)(256*Math.random());
				int b = (int)(256*Math.random());
				grid[i][j] = new Color(r,g,b);
			}
		blemished = true;
		repaint();
	}
	
	public void delay(int milliseconds) {
		if (milliseconds > 0) {
			try { Thread.sleep(milliseconds); }
			catch (InterruptedException e) { }
		}
	}
	
}