

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

/** 
 * The class Mosaic makes available a window made up of a grid
 * of colored rectangles.  Routines are provided for opening and
 * closing the window and for setting and testing the color of rectangles
 * in the grid.
 *
 * <p>Each rectangle in the grid has a color.  The color can be
 * specified by red, green, and blue amounts in the range from
 * 0 to 255.  (It can also be given as an object belonging
 * to the class Color.)
 */
public class Mosaic {
	
	private static JFrame window;         // A mosaic window, null if no window is open.
	private static MosaicCanvas canvas;   // A component that actually manages and displays the rectangles.
	
	/**
	 *  Open a mosaic window with a 20-by-20 grid of squares, where each
	 *  square is 15 pixels on a side.
	 */
	public static void open() {
		// Open a mosaic window with a 20-by-20 grid of squares, where each
		// square is 15 pixels on a side.
		open(20,20,15,15);
	}
	
	/**
	 * Open a mosaic window with the specified numbers or rows and columns
	 * of squares, where each square is 15 pixels on a side.
	 * @param rows the number of rows of squares in the grid
	 * @param columns the number of columns of squares in the grid
	 */
	public static void open(int rows, int columns) {
		open(rows,columns,15,15);
	}
	
	/**
	 * Open a window that shows a mosaic with the specified number of rows and
	 * columns of rectangles. If a mosaic window is already open, it will be 
	 * closed before the new window is open.
	 * @param rows the number of rows of squares in the grid
	 * @param columns the number of columns of squares in the grid
	 * @param blockWidth the width of each rectangle, in pixels
	 * @param blockHeight the height of each rectangle, in pixels
	 */
	public static void open(int rows, int columns, int blockWidth, int blockHeight) {
		if (window != null)
			window.dispose();
		canvas = new MosaicCanvas(rows,columns,blockWidth,blockHeight);
		window = new JFrame("Mosaic Window");
		JPanel content = new JPanel();
		content.setLayout(new BorderLayout());
		content.add(canvas, BorderLayout.CENTER);
		content.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2));
		window.setContentPane(content);
		window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		window.addWindowListener(
				new WindowAdapter() {  // close the window when the user clicks its close box
					public void windowClosing(WindowEvent evt) {
						close();
					}
				});
		window.pack();
		Dimension windowSize = window.getSize();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		int top = (screenSize.height - windowSize.height)/2;
		int left = (screenSize.width - windowSize.width)/2;
		if (top < 0)
			top = 0;
		if (left < 0)
			left = screenSize.width - windowSize.width;  // to make sure close box is on screen
		window.setLocation(left,top);
		window.setVisible(true);
	}
	
	/**
	 * Closes the Mosaic window.  If the Mosaic window is open, calling this method
	 * has no effect.
	 */
	synchronized public static void close() {
		if (window != null) {
			window.dispose();
			window = null;
			canvas = null;
		}
	}
	
	/**
	 * Test whether the Mosaic window is currently open
	 * @return returns true if the window is visible on the screen, and returns false if it is not
	 */
	synchronized public static boolean isOpen() {
		return (window != null);
	}
	
	/**
	 * Returns the number of rows in an open window.
	 * @throws IllegalStateException if the window is not open.
	 */
	public static int getRows() {
		if (canvas == null)
			throw new IllegalStateException("The Mosaic winodw does not exist at this time.");
		return canvas.getRows();
	}
	
	/**
	 * Returns the number of columns in an open window.
	 * @throws IllegalStateException if the window is not open.
	 */
	public static int getColumns() {
		if (canvas == null)
			throw new IllegalStateException("The Mosaic winodw does not exist at this time.");
		return canvas.getColumns();
	}
	
	/**
	 * Inserts a pause into the execution of the program.  When this method is called,
	 * it will return only after a delay of a specified length of time.  During that time,
	 * nothing will happen.  It is possible that the actual delay time will be longer than
	 * the specified time.
	 * @param milliseconds the delay time, in milliseconds.  One second is 1000 milliseconds.
	 * If the value of this parameter is less than or equal to zero, the method has no effect.
	 */
	public static void delay(int milliseconds) {
		// Calling this routine causes a delay of the specified number
		// of milliseconds in the program that calls the routine.  It is
		// provided here as a convenience.
		if (milliseconds > 0) {
			try { Thread.sleep(milliseconds); }
			catch (InterruptedException e) { }
		}
	}
	
	/**
	 * Change the title displayed in the titlebar of the mosaic window.  Calling this method
	 * has no effect if the window is not open.
	 * @param title the new title for the window.  If title is null, then the original
	 * title, "Mosaic Window", is restored.
	 */
	public static void setTitle(String title) {
		if (window != null) {
			if (title == null)
				title = "Mosaic Window";
			window.setTitle(title);
		}
	}
	
	/**
	 * Get the color of a specified rectangle in the grid.
	 * @param row the row number of the rectangle whose color is being tested
	 * @param col the column number of the rectangle whose color is being tested
	 * @return the color of the rectangle in the specified row and column.  If the row or
	 * column number is outside the range of legal values, then the return value will be
	 * black. Also, if the Mosaic window is not open, then the return value is
	 * black.
	 */
	public static Color getColor(int row, int col) {
		if (canvas == null)
			return Color.BLACK;
		return canvas.getColor(row, col);
	}
	
	/**
	 * Get the red component color of a specified rectangle in the grid.  The red component of
	 * a color is an ineger in the range 0 to 255.
	 * @param row the row number of the rectangle whose color is being tested
	 * @param col the column number of the rectangle whose color is being tested
	 * @return the red component of the color of the rectangle in the specified row and column. 
	 * If the row or column number is outside the range of legal values, then the return value 
	 * will be 0. Also, if the Mosaic window is not open, then the return value is 0.
	 */
	public static int getRed(int row, int col) {
		if (canvas == null)
			return 0;
		return canvas.getRed(row, col);
	}
	
	/**
	 * Get the green component color of a specified rectangle in the grid.  The green component of
	 * a color is an integer in the range 0 to 255.
	 * @param row the row number of the rectangle whose color is being tested
	 * @param col the column number of the rectangle whose color is being tested
	 * @return the green component of the color of the rectangle in the specified row and column. 
	 * If the row or column number is outside the range of legal values, then the return value 
	 * will be 0. Also, if the Mosaic window is not open, then the return value is 0.
	 */
	public static int getGreen(int row, int col) {
		if (canvas == null)
			return 0;
		return canvas.getGreen(row, col);
	}
	
	/**
	 * Get the blue component color of a specified rectangle in the grid.  The blue component of
	 * a color is an integer in the range 0 to 255.
	 * @param row the row number of the rectangle whose color is being tested
	 * @param col the column number of the rectangle whose color is being tested
	 * @return the blue component of the color of the rectangle in the specified row and column. 
	 * If the row or column number is outside the range of legal values, then the return value 
	 * will be 0. Also, if the Mosaic window is not open, then the return value is 0.
	 */
	public static int getBlue(int row, int col) {
		if (canvas == null)
			return 0;
		return canvas.getBlue(row, col);
	}
	
	/**
	 * Set the color of a specified rectangle in the grid.  If the Mosaic window is not open, this has
	 * no effect.  Also, if the specified row or column is outside the legal range for the grid,
	 * then nothing is done.
	 * @param row the row number of the rectangle whose color is being set
	 * @param col the column number of the rectangle whose color is being set
	 * @param c the new color for the rectangle in the specified row and column.  If c is null, then
	 * the color will be black.
	 */
	public static void setColor(int row, int col, Color c) {
		if (canvas == null)
			return;
		canvas.setColor(row,col,c);
	}
	
	/**
	 * Set the color of a specified rectangle in the grid.  If the Mosaic window is not open, this has
	 * no effect.  Also, if the specified row or column is outside the legal range for the grid,
	 * then nothing is done.
	 * @param row the row number of the rectangle whose color is being set
	 * @param col the column number of the rectangle whose color is being set
	 * @param red the red component of the color, in the range 0 to 255.  The value is clamped to this range;
	 * that is, a value less than 0 is replaced by 0 and a value less than 255 is replaced by 255.
	 * @param green the green component of the color, in the range 0 to 255.  The value is clamped to this range.
	 * @param blue the blue component of the color, in the range 0 to 255.  The value is clamped to this range.
	 */
	public static void setColor(int row, int col, int red, int green, int blue) {
		// Set the rectangle in the specified row and column to have
		// the color with the specified red, green, and blue components.
		if (canvas == null)
			return;
		canvas.setColor(row,col,red,green,blue);
	}

	/**
	 * Set all rectangles in the grid to a specified color.  If the Mosaic window is not open,
	 * calling this method has no effect.
	 * @param c the color for all the rectangles.  If c is null, black is used.
	 */
	public static void fill(Color c) {
		if (canvas == null)
			return;
		canvas.fill(c);
	}
	
	/**
	 * Set all rectangles in the grid to a specified color.  If the Mosaic window is not open,
	 * calling this method has no effect.
	 * @param red the red component of the color, in the range 0 to 255.  The value is clamped to this range;
	 * that is, a value less than 0 is replaced by 0 and a value less than 255 is replaced by 255.
	 * @param green the green component of the color, in the range 0 to 255.  The value is clamped to this range.
	 * @param blue the blue component of the color, in the range 0 to 255.  The value is clamped to this range.
	 */
	public static void fill(int red, int green, int blue) {
		if (canvas == null)
			return;
		canvas.fill(red,green,blue);
	}

	/**
	 * Sets each rectangle in the grid to its own randomly selected color.  If the Mosaic window
	 * is not open, then calling this method has no effect.
	 */
	public static void fillRandomly() {
		if (canvas == null)
			return;
		canvas.fillRandomly();
	}
	
}  // end of class Mosaic