import java.applet.Applet;
import java.awt.*;
import java.util.Observer;
import java.util.Observable;
import java.util.StringTokenizer;

class Cell extends Observable {
	boolean alive;
	boolean willGrow;
	boolean willDie;
	public void reset() {
		if( alive ) {
			alive= false;
			setChanged(); notifyObservers();
		}
	}
	public void setup() {
		alive= ! alive;
		setChanged(); notifyObservers();
	}
	public void evaluate( int neighbors ) {
		willGrow= false;
		willDie= false;
		if( alive ) {
			if( neighbors < 2 || neighbors > 3 ) willDie= true;
		}
		else {
			if( neighbors == 3 ) willGrow= true;
		}
	}
	public int update() {
		int activity= 0;
		if( willGrow ) { alive= true; setChanged(); ++activity; }
		if( willDie  ) { alive= false; setChanged(); ++activity; }
		notifyObservers();
		return activity;
	}
	public int status() { return alive?1:0; }
}

class Grid {
	Cell theCell[][];
	int size;
	int generation;
	public Grid( int s) {
		size= s;
		generation= 0;
		theCell= new Cell[size][size];
		for( int y= 0; y != size; ++y ) {
			for( int x= 0; x != size; ++x ) {
				theCell[x][y]= new Cell();
			}
		}
	}
	public void clear() {
		for( int y= 0; y != size; ++y ) {
			for( int x= 0; x != size; ++x ) {
				theCell[x][y].reset();
			}
		}
	}
	int neighborStatus( int x, int y ) {
		return theCell[x][y].status();
	}
	int neighbors( int x, int y ) {
		return neighborStatus(x-1,y-1)
		+ neighborStatus(x,y-1)
		+ neighborStatus(x+1,y-1)
		+ neighborStatus(x-1,y)
		+ neighborStatus(x+1,y)
		+ neighborStatus(x-1,y+1)
		+ neighborStatus(x,y+1)
		+ neighborStatus(x+1,y+1);
	}
	public int cycle() {
		++generation;
		System.out.println( "Generation " + generation );
		for( int y= 1; y != size-1; ++y ) {
			for( int x= 1; x != size-1; ++x ) {
				theCell[x][y].evaluate( neighbors(x,y) );
			}
		}
		int active= 0;
		for( int y= 0; y != size; ++y ) {
			for( int x= 0; x != size; ++x ) {
				active += theCell[x][y].update();
			}
		}
		return active;
	}
}

class GridThread extends Thread {
	Grid myGrid;
	Applet myParent;
	public GridThread( Grid aGrid, Applet anApplet ) { 
		myGrid= aGrid;
		myParent= anApplet;
	}
	public void run() {
		setPriority( NORM_PRIORITY-2 );
		int active= 1;
		while( active != 0 ) {
			active= myGrid.cycle();
			try {
				yield();
				sleep(500);
			}
			catch( InterruptedException ok ) {}
		}
		myParent.stop();
	}
}

class LifeCellPanel extends Panel implements Observer {
	Color state;
	Cell myCell;
	public LifeCellPanel( Cell aCell ) { 
		super();
		state= Color.white;
		myCell= aCell;
	}
	public void update(Observable o, Object arg) {
		if( myCell.alive ) state= Color.red;
		else state= Color.white;
		repaint();
	}
	public void paint( Graphics g ) {
		Dimension s= size();
		g.setColor( state );
		g.fillRect(0,0,s.width-1,s.height-1);
	}
	public boolean mouseUp( Event e, int x, int y) {
		//System.out.println( "click "+e );
		myCell.setup();
		return super.mouseUp( e, x, y );
	}
}

class LifeSetup {
	int ox, oy, width;
	String body;
	public LifeSetup() {
		ox= 0;
		oy= 0;
		width= 0;
		body= "";
	}
	public LifeSetup( String aString ) {
		StringTokenizer parse= new StringTokenizer( aString, "," );
		try {
			String xStr= parse.nextToken();
			String yStr= parse.nextToken();
			String wStr= parse.nextToken();
			body= parse.nextToken();
			ox= Integer.valueOf( xStr ).intValue();
			oy= Integer.valueOf( yStr ).intValue();
			width= Integer.valueOf( wStr ).intValue();
		}
		catch( Exception e ) {
			System.err.println( e );
			ox= 0; oy= 0; width= 0; body= "";
		}
	}
	public void copyTo( Grid aGrid ) {
		aGrid.clear();
		int y= oy;
		int c= 0;
		while( c != body.length() ) {
			int x= ox;
			for( int i= 0; i != width; ++i ) {
				if( body.charAt( c ) == 'X' ) aGrid.theCell[x][y].setup();
				++c; ++x;
			}
			++y;
		}
	}
	public String toString() {
		return "LifeSetup[x="+ox+",y="+oy+",width="+width+",body=\""+body+"\"]";
	}
}

public class LifeDemo extends Applet {
	Panel cellDisplay[][];
	Grid theGrid;
	GridThread theCycle;
	Panel footer;
	Panel body;
	Button start;
	Button stop;
	Button clear;
	int size;
	LifeSetup setup;
	
	public LifeDemo() {
		super();
		body= new Panel();
		footer= new Panel();
		start= new Button( "Start" );
		stop= new Button( "Stop" );
		clear= new Button( "Clear" );
		theCycle= null;
	}

	public void init() {
		showStatus( "Initializing..." );
		
		String szStr= getParameter( "size" );
		if( szStr == null ) szStr= "20";
		String initStr= getParameter( "setup" );
		if( initStr == null ) initStr= "2,4,3,-X---XXXX";
		System.err.println( "size="+szStr+",setup="+initStr );
		
		try {
			size= Integer.valueOf( szStr ).intValue();
		}
		catch( Exception e ) { 
			System.err.println( e );
			size= 20;
		}
		if( size < 10 || size > 60 ) size= 20;
		System.err.println( "size="+size );
		
		setup= new LifeSetup( initStr );
		System.err.println( "setup="+setup );
				
		cellDisplay= new LifeCellPanel[size][size];
		theGrid= new Grid(size);
		for( int y= 0; y != size; ++y ) {
			for( int x= 0; x != size; ++x ) {																							cellDisplay[x][y]= 
					new LifeCellPanel( theGrid.theCell[x][y] );
				theGrid.theCell[x][y].addObserver(
					(Observer)cellDisplay[x][y] );
			}
		}
		body.setLayout( new GridLayout(size,size) );
		for( int y= 0; y != size; ++y ) {
			for( int x= 0; x != size; ++x ) {
				body.add( cellDisplay[x][y] );
			}
		}
		showStatus( "Control Panel..." );
		footer.setLayout( new FlowLayout() );
		footer.add( start );
		footer.add( stop );
		footer.add( clear );
		showStatus( "Applet Layout..." );
		setLayout( new BorderLayout() );
		add( "South", footer );
		add( "Center", body );
		start.enable();
		stop.disable();
		clear.enable();
		showStatus( "Drawing..." );
		setup.copyTo( theGrid );
	}
	public void stop() {
		System.out.println( "Stopping" );
		start.enable();
		clear.enable();
		stop.disable();
		if( theCycle != null ) theCycle.stop();
		theCycle= null;
	}
	public boolean action( Event evt, Object what ) {
		System.out.println( evt );
		if( evt.target == start ) {
			start.disable();
			clear.disable();
			stop.enable();
			theCycle= new GridThread( theGrid, this );
			theCycle.start();
			return true;
		}
		if( evt.target == stop ) {
			stop();
			return true;
		}
		if( evt.target == clear ) {
			theGrid.clear();
			return true;
		}
		return super.action( evt, what );
	}
}