/*
	Trivial applet that displays a string - 4/96 PNL
*/

import java.awt.*;
import java.applet.Applet;
import java.util.*;

public class LifeApplet extends Applet
{
	private Button resetButton, stepButton;
	private LifeCanvas lifeGame;
	
	public void init() {
		initButtons();
		lifeGame=new LifeCanvas(10,10);
		lifeGame.resize(size().width-20,size().height-40);
		add(lifeGame);
	}
	
	private void initButtons()
	{
		resetButton=new Button("Reset");
		resetButton.setBackground(Color.white);
		add(resetButton);
		
		stepButton=new Button("Step");
		stepButton.setBackground(Color.white);
		add(stepButton);
	}

	public boolean action(Event e,Object arg)
	{
		if(e.target==resetButton)
		{
			lifeGame.reset();
		}else if(e.target==stepButton)
		{
			lifeGame.TakeTurn();
		}
		return true;
	}
}

public class LifeCanvas extends Canvas
{
	private int xDown,yDown;
	private LifeBoard board;
	
	protected LifeCanvas() {}
	
	public LifeCanvas(int cols,int rows)
	{
		setBackground(Color.white);
		board=new LifeBoard(cols,rows,5);
	}
	
	public void paint(Graphics g)
	{
		int squareSize=Math.min((size().width-1)/board.numCols,(size().height-1)/board.numRows);
		for(int i=0;i<=board.numCols;i++)
		{
			g.drawLine(i*squareSize,0,i*squareSize,board.numRows*squareSize);
		}
		for(int i=0;i<=board.numRows;i++)
		{
			g.drawLine(0,i*squareSize,board.numCols*squareSize,i*squareSize);
		}
		for(int i=0;i<board.numCols;i++)
		{
			for(int j=0;j<board.numRows;j++)
			{
				if(board.GetCellAt(i,j).IsAlive())
				{
					g.setColor(board.GetCellAt(i,j).GetColor());
					g.drawOval((int)((i+1.0/3)*squareSize),(int)((j+1.0/3)*squareSize),squareSize/3,squareSize/3);
				}
			}
		}
	}
		
	private void SwitchCell(int col,int row)
	{
		if(board.GetCellAt(col,row).IsAlive())
		{
			board.GetCellAt(col,row).KillCell();
		}else
		{
			board.GetCellAt(col,row).NewLife();
		}
		board.GetCellAt(col,row).UpdateLastTurn();
	}	
	
	public boolean mouseDown(Event e,int x,int y)
	{
		xDown=x;
		yDown=y;
		return true;
	}
	
	public boolean mouseUp(Event e,int xUp,int yUp)
	{
		int squareSize=Math.min((size().width-1)/board.numCols,(size().height-1)/board.numRows);
		if(xUp/squareSize==xDown/squareSize&&yUp/squareSize==yDown/squareSize)
		{
			SwitchCell(xUp/squareSize,yUp/squareSize);
			repaint();
		}
		return true;
	}
	
	public void reset()
	{
		for(int i=0;i<board.numCols;i++)
		{
			for(int j=0;j<board.numRows;j++)
			{
				board.GetCellAt(i,j).KillCell();
				board.GetCellAt(i,j).UpdateLastTurn();
			}
		}
		repaint();
	}
	
	public void TakeTurn()
	{
		int neighbors;
		for(int i=0;i<board.numCols;i++)
		{
			for(int j=0;j<board.numRows;j++)
			{
				neighbors=board.CountLivingNeighbors(i,j);
				switch(neighbors)
				{
					case 3: if(!board.GetCellAt(i,j).IsAlive())
							{
								board.GetCellAt(i,j).NewLife();
							}else
							{
								board.GetCellAt(i,j).AgeCell();
							}
							break;
					case 2: if(board.GetCellAt(i,j).IsAlive())
							{
								board.GetCellAt(i,j).AgeCell();
							}
							break;
					default: board.GetCellAt(i,j).KillCell(); break;
				}
			}
		}
		for(int i=0;i<board.numCols;i++)
		{
			for(int j=0;j<board.numRows;j++)
			{
				board.GetCellAt(i,j).UpdateLastTurn();
			}
		}
		repaint();
	}
}

public abstract class GameBoard extends Vector
{
	protected int numRows,numCols;
	
	protected GameBoard() {}
	
	protected void InitBoard(int numCols,int numRows)
	{
		this.numRows=numRows;
		this.numCols=numCols;
		for(int i=0;i<numCols;i++)
		{
			addElement(new Vector());
		}
	}
	
	protected void SetValueAt(Object value,int col,int row) {}
	
	protected Object GetValueAt(int col,int row)
	{
		return ((Vector)elementAt(col)).elementAt(row);
	}
}

public class LifeBoard extends GameBoard
{
	protected LifeBoard() {}
	
	public LifeBoard(int numCols,int numRows,int maxAge)
	{
		InitBoard(numCols,numRows);
		for(int i=0;i<numCols;i++)
		{
			for(int j=0;j<numRows;j++)
			{
				((Vector)elementAt(i)).addElement(new LifeCell(0,maxAge));
			}
		}
	}
	
	public LifeCell GetCellAt(int col,int row)
	{
		return (LifeCell) GetValueAt(col,row);
	}
	
	public int CountLivingNeighbors(int col,int row)
	{
		int count=0;
		for(int i=-1;i<=1;i++)
		{
			for(int j=-1;j<=1;j++)
			{
				if((i!=0||j!=0)&&GetCellAt((col+i+numCols)%numCols,(row+j+numRows)%numRows).WasAliveLastTurn())
				{
					count++;
				}
			}
		}
		return count;
	}
}
	
	
	
public class LifeCell
{
	private int age,maxAge;
	private boolean aliveLastTurn;
	
	protected LifeCell() {};
	
	public LifeCell(int age,int maxAge)
	{
		this.age=age;
		this.maxAge=maxAge;
		aliveLastTurn=(age!=0);
	}
	
	public boolean WasAliveLastTurn() {return aliveLastTurn;}
	
	public void KillCell() {age=0;}
	
	public void AgeCell()
	{
		if(age!=maxAge&&age!=0)
		{
			age++;
		}
	}
	
	public int GetAge() {return age;}
	
	public boolean IsAlive() {return age!=0;}
	
	public void NewLife() {age=1;}
	
	public void UpdateLastTurn() {aliveLastTurn=(age!=0);}
	
	public Color GetColor()
	{
		switch(age)
		{
			case 1: return Color.red;
			case 2: return Color.magenta;
			case 3: return Color.green;
			case 4: return Color.yellow;
			case 5: return Color.cyan;
			case 6: return Color.blue;
			default: return Color.orange;
		}
	}	
}
			
		
	

	
