// "life.java": primitive simulation of life

// Derived from Peter Cappello

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

public class life extends JFrame implements KeyListener {
    // constants
    public final static int WIDTH = 500, HEADER_SPACE = 40, MARGIN = 10;
    public final static int DEFAULT_SIDE = 16;
    int width_cell;

    // instance variable:
    int[][] cell;  // all the cells

    public final static int DEAD = 0, ALIVE = 1;

    public life(String[] args) throws IOException {
        super("life");

        // Initialize life board:
        cell = init_cell(args);
        width_cell = (WIDTH - MARGIN*2)/cell.length;

        addKeyListener(this);

        setSize(WIDTH, HEADER_SPACE + WIDTH);
        setVisible(true);
        // requestFocus();
        repaint();
        }

    int[][] init_cell(String[] args) throws IOException {
        int[][] result;
        if ( args.length == 0 ) {
            result = new int[DEFAULT_SIDE][DEFAULT_SIDE];
            // (Boundary cells are initialized to 0 by system.)
            for( int c = 1; c < result.length-1; c++ )
                for( int r = 1; r < result[c].length-1; r++ )
                    result[c][r] = (Math.random() < 0.5) ? DEAD : ALIVE;
            }
        else if ( args.length == 1 ) {
            BufferedReader file_in_br =
                new BufferedReader(new FileReader(args[0]));
            ArrayList file_contents = new ArrayList();
            int high_size = 0;
            for ( String line; (line = file_in_br.readLine()) != null; ) {
                line = line.replaceAll("\t", "        ");
                high_size = Math.max(line.length(), high_size);
                file_contents.add(line);
                }
            file_in_br.close();
            high_size = Math.max(file_contents.size(), high_size);
            result = new int[1 + high_size + 1][1 + file_contents.size() + 1];
            // (Boundary cells are initialized to 0 by system.)
            for ( int i = 0; i < file_contents.size(); i++ ) {
                String line = (String) file_contents.get(i);
                int r = i + 1;
                for ( int c = 0; c < line.length(); c++ )
                    result[c][r] =  line.charAt(c) == ' ' ? DEAD : ALIVE;
                }
            }
        else
            throw new IllegalArgumentException("excessive arguments");
        return  result;
        }

    public void paint(Graphics g) {
	super.paint(g);

        // Paint everything DEAD:
        g.setColor(Color.BLUE);
        g.fillRect(MARGIN, HEADER_SPACE,
                    width_cell*cell.length, width_cell*cell[0].length);

        // Paint ALIVE cells:
        g.setColor(Color.RED);
        for( int c = 1; c < cell.length-1; c++ )
            for( int r = 1; r < cell[c].length-1; r++ )
                if( cell[c][r] == ALIVE )
                    g.fillOval(
                        MARGIN + c*width_cell, HEADER_SPACE + r*width_cell,
                        width_cell, width_cell
                        );
	}

    public static void main(String[] args) throws IOException {
        life frame = new life(args);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void keyTyped(KeyEvent ke) {
        // Compute next generation:
        int[][] cell_next = new int[cell.length][cell[0].length];
        for( int c = 1; c < cell.length-1; c++ )
            for( int r = 1; r < cell[c].length-1; r++ ) {
                // determine |cell_next[c][r]| :

                int count =   cell[c-1][r-1] + cell[c][r-1] + cell[c+1][r-1]
                            + cell[c-1][r] 		    + cell[c+1][r]
                            + cell[c-1][r+1] + cell[c][r+1] + cell[c+1][r+1];

                switch( count ) {
                    case 0: case 1: case 4: case 5: case 6: case 7: case 8:
                    // no birth; death from loneliness or overcrowding
                        cell_next[c][r] = DEAD;
                        break;
                    case 3: // birth; no death
                        cell_next[c][r] = ALIVE;
                        break;
                    case 2:
                        cell_next[c][r] = cell[c][r];
                        break;
                    }
                }
        cell = cell_next;

        repaint();
        }

    public void keyPressed(KeyEvent ke) { }

    public void keyReleased(KeyEvent ke) { }

    }
