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

public class Ahlife extends Applet implements Runnable {
    static final int bwidth = 80;
    static final int bheight = 80;
    static final int mag = 5;
    Image offscreen;
    int imagewidth, imageheight;
    Thread animator = null;
    boolean please_stop = false;
    byte[][] oldboard;
    byte[][] board;
    Color[] board_colors;
    byte dragtype = 0;
  long oldtime;
  long waittime = 500; // time to wait between frames in msec.

    public void init() {
      oldboard = new byte[bwidth][bheight];
      board = new byte[bwidth][bheight];
      Random rand = new Random();
      for (int i = 1; i < bheight-1; i++) {
	  for (int j = 1; j < bwidth-1; j++) {
	      board[i][j] = (byte)(Math.abs((rand.nextInt()))%2);
	  }
      }
      oldtime = System.currentTimeMillis();

      board_colors = new Color[128];
      double r1,g1,b1;
      double r2,g2,b2;
      double dr,dg,db;
      int start = 0;
      int steps = 32;
      r1=255; g1=0; b1=0;
      r2=0; g2=255; b2=255;
      dr = (r2-r1)/steps;
      dg = (g2-g1)/steps;
      db = (b2-b1)/steps;
      for (int i =start; i<start+steps; i++) {
	board_colors[i]=new Color((int)r1, (int)g1, (int)b1);
	r1+=dr;
	g1+=dg;
	b1+=db;
      }
      start =32;
      r1=0; g1=255; b1=255;
      r2=255; g2=0; b2=255;
      dr = (r2-r1)/steps;
      dg = (g2-g1)/steps;
      db = (b2-b1)/steps;
      for (int i =start; i<start+steps; i++) {
	board_colors[i]=new Color((int)r1, (int)g1, (int)b1);
	r1+=dr;
	g1+=dg;
	b1+=db;
      }
      start =64;
      steps = 64;
      r1=255; g1=0; b1=255;
      r2=0; g2=0; b2=200;
      dr = (r2-r1)/steps;
      dg = (g2-g1)/steps;
      db = (b2-b1)/steps;
      for (int i =start; i<start+steps; i++) {
	board_colors[i]=new Color((int)r1, (int)g1, (int)b1);
	r1+=dr;
	g1+=dg;
	b1+=db;
      }


    }

    // Start the animation
    public void start() { 
        animator = new Thread(this);
        animator.start();
    }
    // Stop it.
    public void stop() { 
        if (animator != null) animator.stop();
        animator = null;
    }
    // Stop and start animating on meta-mouse clicks.
    // Flip the state of a cell on a normal mouse click. 
    public boolean mouseDown(Event e, int x, int y) {

      if (e.modifiers == Event.META_MASK){
        // if running, stop it.  Otherwise, start it. On a right click.
	if (animator != null) please_stop = true;
	else { please_stop = false; start(); } 

      } else {
	int i = y/mag;
	int j = x/mag;
	dragtype = 1;
	//System.out.println(i+ " " +j);
	if (!((i<=0) || (i>=bheight-1) || (j<=0)||(j >= bwidth -1))) {
	  try {
	    if (board[i][j] == 0) {
	      board[i][j] = 1;
	      dragtype = 1;
	    } else {
	      board[i][j] = 0;
	      dragtype = 0;
	    }
	    repaint();
	  } catch (ArrayIndexOutOfBoundsException err) { }
	}
      }
      return true;
    }

  // allow a mouse drag to draw or erase a string of cells.
    public boolean mouseDrag(Event e, int x, int y) {
	int i = y/mag;
	int j = x/mag;
	if (!((i<=0) || (i>=bheight-1) || (j<=0)||(j >= bwidth -1))) {
	  try {
	    board[i][j] = dragtype;
	    repaint();
	  } catch (ArrayIndexOutOfBoundsException err) { }
	}
      return true;
    }

    // Draw the board, based on the current state of the board array.
    void drawBoard(Graphics gr) {
        int r=255, g=100, b=20;
        Dimension size = this.size();
        int w = size.width, h = size.height;
        
        for(int i=0; i<bheight; i++) {
	  for (int j=0; j<bwidth; j++) {
	    if (board[i][j] > 0) {
	      gr.setColor(board_colors[board[i][j]]);
	      gr.fillRect(mag*j, mag*i, mag, mag);
	    }
	  }
	}
    }


    void updateBoard() 
    {
      // first, save the contents of the current board for reference.
        for(int i=0; i<bheight; i++) {
	  for (int j=0; j<bwidth; j++) {
	    oldboard[i][j] = board[i][j];
	  }
	}
	// set up the border conditions (nothing for now).

	// now update the new board - the borders are not updated.
        for(int i=1; i<bheight-1; i++) {
	  for (int j=1; j<bwidth-1; j++) {
	    int nlive = 0;
	    if (oldboard[i+1][j]>0) nlive++;
	    if (oldboard[i+1][j+1]>0) nlive++;
	    if (oldboard[i][j+1]>0) nlive++;
	    if (oldboard[i-1][j+1]>0) nlive++;
	    if (oldboard[i-1][j]>0) nlive++;
	    if (oldboard[i-1][j-1]>0) nlive++;
	    if (oldboard[i][j-1]>0) nlive++;
	    if (oldboard[i+1][j-1]>0) nlive++;
	    
	    if (board[i][j] <0)
	      System.out.println("LTZ " + i +" "+j);
	    
	    if (nlive==2) {  //alive remains alive, dead remains dead.
	      if ((board[i][j]>0)&&(board[i][j]<125)) board[i][j]++;
	    } else if (nlive==3) { // come alive.
	      if (board[i][j]<125) board[i][j]++;
	    } else { // die.
	      board[i][j]=0;
	    }

	  }
	}
	//System.out.println(board[bheight-2][bwidth-2]);
    }

  // New update method to avoid flicker.
     public void update(Graphics g) {
       paint(g);
     }


    // This method draws the background and  text at its current position.
    public void paint(Graphics g) {
      g.setColor(Color.black);
        Dimension size = this.size();
        g.fillRect(0, 0, size.width, size.height);
	drawBoard(g);
    }

    // The body of the animator thread.
    public void run() {
        while(!please_stop) {
            Dimension d = this.size();
            
            // Make sure the offscreen image is created and is the right size.
            if ((offscreen == null) ||
                ((imagewidth != d.width) || (imageheight != d.height))) {
                // if (offscreen != null) offscreen.flush();
                offscreen = this.createImage(d.width, d.height);
                imagewidth = d.width;
                imageheight = d.height;
            }

	    // update the world
	    updateBoard();
            Graphics g = offscreen.getGraphics();
            // Draw into the off-screen image.
            paint(g);
            // Copy it all at once to the screen, using clipping.
            g = this.getGraphics();

	    // Try gathering an average time, and printing it out every 20 
	    // iterations or so. Time taken by updateBoard and by drawImage.
            // wait for some time, then draw the image!
            //long newtime = System.currentTimeMillis();
	    //if (newtime - oldtime <waittime) {
	    try { Thread.sleep(waittime); } catch (InterruptedException e) { ; }
	    //}
	    //oldtime = newtime;
	    //System.out.println(difftime);
            g.drawImage(offscreen, 0, 0, this);

        }
        animator = null;
    }
}
