
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
import java.awt.event.MouseEvent;

public class GameOfLife extends Applet implements Runnable {
  
  int[] world;
  int[] newWorld;
  Color[] colors;
  static final int numColors = 32;
  int columns;
  int rows;
  int delayTime = 500;
  boolean mustStop = false;
  Thread loopThread = null;

    public synchronized void start() {
	if (loopThread == null) {
	    loopThread = new Thread(this);
	    loopThread.start();
	}
    }

    public synchronized void destroy() {
	mustStop = true;
	if (loopThread != null) {
		loopThread.interrupt();
		loopThread = null;
	}
    }

    public void run() {
      try {
	while (!mustStop) {
	  Thread.sleep(delayTime);
	  calcNewPopulation();
	  repaint();
	}
      } catch (InterruptedException e) {}
    }

  protected final int parseSize(String sizeStr)
  {
    int v;
    v = Integer.parseInt(sizeStr);
    if (v <= 1)
      v = 2;
    else if (v > 256)
      v = 256;
    return v;
  }

  public void init() {
    int x = 4;
    int y = 4;
    int c;
    String param;
    param = getParameter("size");
    if (param != null) {
      x = parseSize(param);
      y = x;
    }
    param = getParameter("columns");
    if (param != null)
      x = parseSize(param);
    param = getParameter("rows");
    if (param != null)
      y = parseSize(param);
    param = getParameter("delay");
    if (param != null)
      delayTime = Integer.parseInt(param);
    colors = new Color[numColors];
    for (c=0; c<numColors; c++)
      colors[c] = Color.getHSBColor(((float)c)/numColors, 1, 1);
    initWorld(x, y);
    // "subscribe" to mouseclicks
    // enableEvents(MouseEvent.MOUSE_CLICKED);
  }

  // Initialize puzzle to given matrix dimensions
  public void initWorld(int x, int y)
  {
    int i, j;
    Random rnd = new Random();

    columns = x;
    rows = y;
    int size = x * y;
    // Allocate our data storage (array of int)
    world = new int[size];
    newWorld = new int[size];
    // put the numbers into the cells
    for (i=0; i < size; i++)
	world[i] = ((rnd.nextInt() & 7) / 6);
  }

  // Paints the applet
  public void paint(Graphics g) {
    Dimension b = getSize();
    int i, y, x;
    int size = columns * rows;
    int w = b.width / columns;
    int h = b.height / rows;
    b = null;
    // use clip bounding area to do a more efficient repaint.
    Rectangle clipTo = g.getClipBounds();
    int upperx = (clipTo.x + clipTo.width) / w;
    if (upperx > columns) upperx = columns;
    int uppery = (clipTo.y + clipTo.height)/ h;
    if (uppery > rows) uppery = rows;
    for (y = clipTo.y / h; y<uppery; y++) {
	int j = y * columns;
      for (x = clipTo.x / w; x<upperx; x++) {
	i = world[j + x];
	if (i > 0) {
	  g.setColor(colors[i]);
	  g.fillRect(x * w, y * h, w - 2, h - 2);
	}
      }
    }
  }

  protected final int neighbours(int x, int y) {
    int r = 0;
    int index = x + (y * columns);
    //left side
    if (x > 0) {
      if (world[index-1] != 0) r++;
      if ((y > 0) && (world[index - 1 - columns] != 0)) r++;
      if ((y < rows-1) && (world[index - 1 + columns] != 0)) r++;
    }
    if (x < columns-1) {
      if (world[index + 1] != 0) r++;
      if ((y > 0) && (world[index+1-columns] != 0)) r++;
      if ((y < rows-1) && (world[index+1+columns] != 0)) r++;
    }
    if ((y > 0) && (world[index-columns] != 0)) r++;
    if ((y < rows-1) && (world[index+columns] != 0)) r++;
    return r;
  }

  protected synchronized void calcNewPopulation()
  {
    int x, y;
    int index = 0;
    // calculate new world
    for (y=0; y<rows; y++)
      for (x=0; x<columns; x++) {
	int n = neighbours(x, y);
	int i = world[index];
	if (i > 0) {
	  if ((n>1) && (n<=3)) {
	    i++;
	    if (i==numColors)
	      newWorld[index] = 1;
	    else	      
	      newWorld[index] = i;
	  } else {
	    newWorld[index] = 0;
        }
	} else {
	    if (n == 3) newWorld[index] = 1;
	}
	index++;
    }
    // Pageflip
    //int[] tmp = world;
    //world = newWorld;
    //newWorld = tmp;
    int size = rows * columns;
    for (int i = 0; i < size; i++) world[i] = newWorld[i];
  }

  public String getAppletInfo()
  {
    return "Game of Life - (c)1998 by Mike Looijmans (my second applet)";
  }

  public String[][] getParameterInfo()
  {
    String[][] pinfo = {

	{"size",      "int", 	"sides of the world"},
	{"columns",    "int", 	"x-dimension (overrides sides)"},
	{"rows",       "int",	"y-dimension (overrides sides)"},
	{"delay",      "int",   "delay between updates (ms)"}
    };
    return pinfo;    
  }
}

