/*
 * Written by Professor Bill Tomlinson for Winter 2004 Biomorphic Computing class at UCI.
 * wmt@uci.edu
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

public class GameOfLife extends Applet implements Runnable {
	//how many blocks in x
	public static int xSize = 50;
	//how many blocks in y
	public static int ySize = 50;
	//how many pixels per block
	public static int cellSize = 10;
	public static int[][] blocks = new int[xSize][ySize];
	Color backgroundColor = Color.gray;
	Button startStopButton;
	Button stepButton;
	Canvas gridCanvas;
	//initialize the grid world
	public void init() {
		//System.out.println("initting");
		setGameOfLife(this);
		setLayout(new BorderLayout());
		//Panel p = new Panel();
		gridCanvas = new Canvas() {
			public void paint(Graphics g) {
				setBackground(backgroundColor);
				g.clearRect(1, 1, (xSize * cellSize) - 2, (ySize * cellSize) - 2);

				for (int thisX = 0; thisX < xSize; thisX++) {
					for (int thisY = 0; thisY < ySize; thisY++) {

						if (blocks[thisX][thisY] > 0) {
							g.setColor(Color.black);
						} else {
							g.setColor(Color.white);
						}
						g.fillRect(thisX * cellSize, thisY * cellSize, cellSize - 1, cellSize - 1);
					}
				}
			}
		};
		Panel controlPanel = new Panel();
		controlPanel.setSize(30, 10);
		gridCanvas.setSize(500, 500);
		this.setLayout(new BorderLayout());

		add(BorderLayout.CENTER, gridCanvas);
		add(BorderLayout.SOUTH, controlPanel);
		gridCanvas.addMouseMotionListener(new GameOfLifeMouseMotionListener());
		gridCanvas.addMouseListener(new GameOfLifeMouseMotionListener());
		// start and stop buttom
		startStopButton = new Button("Start");
		stepButton = new Button("Step");

		// when start/stop button is clicked
		startStopButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				startStopButtonClicked();
			}
		});
		stepButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				doOneStep();
			}
		});
		controlPanel.add(stepButton);
		controlPanel.add(startStopButton);
		setVisible(true);
		validate();
		gridCanvas.repaint();
	} // init

	public void paint(Graphics g) {
		gridCanvas.repaint();
	}

	public void run() {
		while (true) {
			doOneStep();
		}
	}
	private void doOneStep() {
		System.out.println("doing one step");

		//Please read the following short paper:
		//http://ddi.cs.uni-potsdam.de/HyFISCH/Produzieren/lis_projekt/proj_gamelife/ConwayScientificAmerican.htm
		//and implement John Conway's Game of Life.  

		//Fields you might be interest in using:
		//blocks[][] is an array of ints that correspond to the blocks on the game grid
		//Its dimensions are xSize by ySize (both ints).
		//Any blocks with value > 0 when repaint is called (near the end of this method) 
		//will be painted black.  Others will be painted white.

		//methods you might be interested in using:
		//getNumberOfNeighbors(x, y) returns the number of neighboring blocks that have value > 0

		//Please comment your code so that it is clear what each section is doing.

		//Please email this entire file to wmt@uci.edu by Monday, 1/19/04 at noon.

		//insert code below this line





		//insert code above this line.
		gridCanvas.repaint();
		try {
			Thread.sleep(200);
		} catch (Exception e) {}
	}
	private int getNumberOfNeighbors(int x, int y) {
		int numNeighbors = 0;
		//go through each neighbor and add one to numNeighbors if it is >0
		if (x > 0 && y > 0 && blocks[x - 1][y - 1] > 0) {
			numNeighbors++;
		}
		if (y > 0 && blocks[x][y - 1] > 0) {
			numNeighbors++;
		}
		if (x < GameOfLife.xSize - 1 && y > 0 && blocks[x + 1][y - 1] > 0) {
			numNeighbors++;
		}
		if (x > 0 && blocks[x - 1][y] > 0) {
			numNeighbors++;
		}
		if (x < GameOfLife.xSize - 1 && blocks[x + 1][y] > 0) {
			numNeighbors++;
		}
		if (x > 0 && y < GameOfLife.ySize - 1 && blocks[x - 1][y + 1] > 0) {
			numNeighbors++;
		}
		if (y < GameOfLife.ySize - 1 && blocks[x][y + 1] > 0) {
			numNeighbors++;
		}

		if (x < GameOfLife.xSize - 1 && y < GameOfLife.ySize - 1 && blocks[x + 1][y + 1] > 0) {
			numNeighbors++;
		}
		return numNeighbors;
	}
	public static GameOfLife thisGameOfLife;
	public static GameOfLife getGameOfLife() {
		return thisGameOfLife;
	}
	public static void setGameOfLife(GameOfLife gw) {
		thisGameOfLife = gw;
	}
	Thread GameOfLifeThread;
	public void startStopButtonClicked() {
		//System.out.println("Start/Stop Button clicked.");
		if (GameOfLifeThread != null) {
			GameOfLifeThread.stop();
			GameOfLifeThread = null;
			System.out.println("STOPPED.");
			startStopButton.setLabel("Start");
		} else {
			if (GameOfLifeThread == null) {
				GameOfLifeThread = new Thread(this);
			}
			GameOfLifeThread.start();
			System.out.println("STARTED.");
			startStopButton.setLabel("Stop");
		}
	}
}
class GameOfLifeMouseMotionListener implements MouseListener, MouseMotionListener {
	static int[][] blocksAtStartOfDrag;
	//this happens when you drag.  It always starts with a mousePressed
	//it should be able to reverse everything exactly once, except the one where mouse was pressed.
	public void mouseDragged(MouseEvent e) {
		int xPos = e.getX() / GameOfLife.cellSize;
		int yPos = e.getY() / GameOfLife.cellSize;
		if ((xPos < GameOfLife.xSize) && (yPos < GameOfLife.ySize) && xPos >= 0 && yPos >= 0) {
			if (GameOfLife.blocks[xPos][yPos] == blocksAtStartOfDrag[xPos][yPos]) {
				if (GameOfLife.blocks[xPos][yPos] == 0) {
					GameOfLife.blocks[xPos][yPos] = 1;
				} else {
					GameOfLife.blocks[xPos][yPos] = 0;
				}
			}
		}
		GameOfLife.getGameOfLife().gridCanvas.repaint();
	}
	//this happens when you click, or at the beginning of a drag
	//whenever this happens it should reverse the block
	public void mousePressed(MouseEvent e) {
		int xPos = e.getX() / GameOfLife.cellSize;
		int yPos = e.getY() / GameOfLife.cellSize;
		blocksAtStartOfDrag = new int[GameOfLife.xSize][GameOfLife.ySize];
		for (int i = 0; i < blocksAtStartOfDrag.length; i++) {
			for (int j = 0; j < blocksAtStartOfDrag[i].length; j++) {
				blocksAtStartOfDrag[i][j] = GameOfLife.blocks[i][j];
			}
		}
		if ((xPos < GameOfLife.xSize) && (yPos < GameOfLife.ySize) && xPos >= 0 && yPos >= 0) {
			if (GameOfLife.blocks[xPos][yPos] == 0) {
				GameOfLife.blocks[xPos][yPos] = 1;
			} else {
				GameOfLife.blocks[xPos][yPos] = 0;
			}
		}
		GameOfLife.getGameOfLife().gridCanvas.repaint();
	}
	public void mouseMoved(MouseEvent e) {
	}
	public void mouseReleased(MouseEvent e) {
	}
	public void mouseEntered(MouseEvent e) {
	}
	public void mouseExited(MouseEvent e) {
	}
	public void mouseClicked(MouseEvent e) {
	}
}