/*
 *  TotalisticCellularAutomaton.
 *  Copyright (C) 2002  Frank Buß (fb@frank-buss.de)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You can get the GNU General Public License at
 *  http://www.gnu.org/licenses/gpl.html
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

/**
 * Totalistic explorer cellular automaton applet. Can be started as a normal
 * application as well.
 * 
 * @author Frank Buß
 */
public class TotalisticCellularAutomaton
	extends Applet
	implements Runnable, ActionListener {
	/**
	 * Used for applet title lable and Frame title.
	 */
	private final static String titleText = "Totalistic Explorer V 1.0";

	/**
	 * Current animation.
	 */
	private Thread animation;

	private TextField ruleTextfield;

	/**
	 * Sets a new rule.
	 */
	private Button setButton;

	/**
	 * Sets a new random rule.
	 */
	private Button randomButton;

	/**
	 * Switch flickering flag.
	 */
	private Button flickeringButton;

	private TextField densityTextfield;

	/**
	 * Seeds the universe.
	 */
	private Button seedButton;

	/**
	 * Starts and pauses the animation.
	 */
	private Button animationButton;

	/**
	 * One animation step.
	 */
	private Button stepButton;

	/**
	 * CA model and view.
	 */
	private TotalisticCellularAutomatonCanvas automatonCanvas;

	/**
	 * If true, step will not be called for automatonCanvas. 
	 */
	private boolean pause;

	/**
	 * The animation thread runs until this is false.
	 */
	private boolean running = true;

	/**
	 * Starts the animation thread, but in paused state.
	 * @see java.applet.Applet#start()
	 */
	public void start() {
		if (animation == null) {
			animation = new Thread(this);
			animation.start();
		}
	}

	/**
	 * Stops the animation thread.
	 * @see java.applet.Applet#stop()
	 */
	public void stop() {
		running = false;
		animation = null;
	}

	/**
	 * If not paused, animates the automaton.
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		while (running) {
			if (pause) {
				try {
					Thread.sleep(100);
					automatonCanvas.repaint();
				} catch (Exception e) {
				}
			} else {
				automatonCanvas.step();
				automatonCanvas.repaint();
				try {
					Thread.sleep(10);
				} catch (Exception e) {
				}
			}
		}
	}

	/**
	 * Creates GUI and starts the animation.
	 */
	public void init() {
		// create GUI
		setLayout(new BorderLayout());
		setBackground(Color.white);

		Panel inputContainer = new Panel();
		inputContainer.setLayout(new GridLayout(2, 5));

		inputContainer.add(new Label("Rule: "));

		ruleTextfield = new TextField();
		ruleTextfield.setText("B134S3");
		inputContainer.add(ruleTextfield);

		setButton = new Button("Set");
		inputContainer.add(setButton);
		setButton.addActionListener(this);

		randomButton = new Button("Random");
		inputContainer.add(randomButton);
		randomButton.addActionListener(this);

		flickeringButton = new Button("Switch Flickering");
		inputContainer.add(flickeringButton);
		flickeringButton.addActionListener(this);

		inputContainer.add(new Label("Density: "));

		densityTextfield = new TextField();
		densityTextfield.setText("0.99");
		inputContainer.add(densityTextfield);

		seedButton = new Button("Seed");
		inputContainer.add(seedButton);
		seedButton.addActionListener(this);

		animationButton = new Button("Animate / Pause");
		inputContainer.add(animationButton);
		animationButton.addActionListener(this);

		stepButton = new Button("Step");
		inputContainer.add(stepButton);
		stepButton.addActionListener(this);

		Panel p = new Panel(new BorderLayout());
		p.add(BorderLayout.NORTH, inputContainer);
		add(BorderLayout.SOUTH, p);

		automatonCanvas = new TotalisticCellularAutomatonCanvas();
		add(BorderLayout.CENTER, automatonCanvas);

		Label title = new Label(titleText, Label.CENTER);
		title.setFont(new Font("SansSerif", Font.PLAIN, 20));
		add(BorderLayout.NORTH, title);

		// init automaton		
		densityToAutomaton();
		ruleToAutomaton();

		// start animation thread
		start();
	}

	/**
	 * Seed automaton with density.
	 */
	private void densityToAutomaton() {
		float density = 0.99f;
		try {
			density = Float.valueOf(densityTextfield.getText()).floatValue();
		} catch (NumberFormatException e) {
		}
		automatonCanvas.seed(density);
	}

	/**
	 * Set new rule for automaton.
	 */
	private void ruleToAutomaton() {
		automatonCanvas.initRule(ruleTextfield.getText());
	}

	/**
	 * Shows the applet in a AWT Frame.
	 * @param args Unused.
	 */
	public static void main(String args[]) {
		Frame f = new Frame(titleText);
		f.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		f.setLayout(new BorderLayout());
		TotalisticCellularAutomaton a = new TotalisticCellularAutomaton();
		f.add(BorderLayout.CENTER, a);
		a.init();
		a.start();
		f.pack();
		f.setVisible(true);
	}

	public void actionPerformed(ActionEvent evt) {
		synchronized (automatonCanvas) {
			if (evt.getSource() == setButton) {
				ruleToAutomaton();
				automatonCanvas.repaint();
			} else if (evt.getSource() == randomButton) {
				ruleTextfield.setText(automatonCanvas.initRandomRule());
				automatonCanvas.repaint();
			} else if (evt.getSource() == flickeringButton) {
				automatonCanvas.switchFlickering();
				automatonCanvas.repaint();
			} else if (evt.getSource() == seedButton) {
				densityToAutomaton();
				automatonCanvas.repaint();
			} else if (evt.getSource() == animationButton) {
				pause = !pause;
				automatonCanvas.repaint();
			} else if (evt.getSource() == stepButton) {
				pause = true;
				automatonCanvas.step();
				automatonCanvas.repaint();
			}
		}
	}
}
