/*----------------------------------------------------------------------+
|      Title:	Genebat.java                                            |
|                                                                       |
|      Author:	David E. Joyce                                          |
|           Department of Mathematics and Computer Science              |
|           Clark University                                            |
|           Worcester, MA 01610-1477                                    |
|           U.S.A.                                                      |
|                                                                       |
|           http://aleph0.clarku.edu/~djoyce/                           |
|           djoyce@clarku.edu                                           |
|                                                                       |
|      Date:    May, 2002.                                              |
+----------------------------------------------------------------------*/

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Genebat extends Applet
  implements ActionListener, AdjustmentListener, KeyListener, Runnable {

  // parameters and their initial values
  int players, iPlayers = 100; // number of players, must be even
  int gamesPerMatch, igamesPerMatch = 20;
  int deathRate, ideathRate = 5; // per 100
  int mutationRate, imutationRate = 10; // per 1000
  int asexual, iasexual = 95; // percent
  int ipayoff[][] = {{3,0},{5,1}};
  int payoff[][] = new int[2][2];

  // components

  BarGraph  barGraph;
  Button startButton, resetButton, pauseButton;
  Label players_label = new Label();
  Scrollbar players_scrollbar;
  Label gamesPerMatch_label = new Label();
  Scrollbar gamesPerMatch_scrollbar;
  Label deathRate_label = new Label();
  Scrollbar deathRate_scrollbar;
  Label mutationRate_label = new Label();
  Scrollbar mutationRate_scrollbar;
  Label asexual_label = new Label();
  Scrollbar asexual_scrollbar;
  Panel payoffMatrixPanel;
  TextField payoffField[][] = new TextField[2][2];
  final static int statWidth=300, statHeight=200;
  final static int barWidth=300, barHeight=100;
  ColorGraph timeGraph;

  // data
  Range range;
  int matchNumber;

  // Initialize the applet
  public void init() {
    // construct and place the components in the applet window
    BorderLayout appletLayout = new BorderLayout();
    appletLayout.setHgap(5);
    appletLayout.setVgap(5);
    setLayout(appletLayout);
    setFont(new Font("Serif",Font.PLAIN,10));

    // Set up the control panel
    Panel controlPanel = new Panel();
    BorderLayout controlLayout = new BorderLayout();
    controlLayout.setHgap(20);
    controlPanel.setLayout(controlLayout);
    add("North",controlPanel);

    // The buttonPanel will make up part of the control panel
    Panel buttonPanel = new Panel();
    buttonPanel.setLayout(new GridLayout(3,1));
    controlPanel.add("West",buttonPanel);

    startButton = new Button("Start");
    startButton.addActionListener(this);
    buttonPanel.add(startButton);

    pauseButton = new Button("Pause");
    pauseButton.addActionListener(this);
    pauseButton.setEnabled(false);
    buttonPanel.add(pauseButton);

    resetButton = new Button("Reset");
    resetButton.addActionListener(this);
    buttonPanel.add(resetButton);

    // Each parameter has a label and a scrollbar
    Panel parameterPanel = new Panel();
    GridLayout parameterPanelLayout = new GridLayout(3,4);
    parameterPanelLayout.setHgap(10);
    parameterPanel.setLayout(parameterPanelLayout);
    controlPanel.add(parameterPanel);

    players = iPlayers;
    parameterPanel.add(players_label);
    players_label.setText("players: "+players);
    players_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,players,20,2,519);
    players_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(players_scrollbar);

    gamesPerMatch = igamesPerMatch;
    parameterPanel.add(gamesPerMatch_label);
    gamesPerMatch_label.setText("games/match: "+gamesPerMatch);
    gamesPerMatch_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,gamesPerMatch,10,1,210);
    gamesPerMatch_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(gamesPerMatch_scrollbar);

    deathRate = ideathRate;
    parameterPanel.add(deathRate_label);
    deathRate_label.setText("death rate: "+deathRate/100.0);
    deathRate_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,deathRate,3,1,23);
    deathRate_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(deathRate_scrollbar);

    mutationRate = imutationRate;
    parameterPanel.add(mutationRate_label);
    mutationRate_label.setText("mutate rate: "+mutationRate/1000.0);
    mutationRate_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,mutationRate,10,0,110);
    mutationRate_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(mutationRate_scrollbar);

    asexual = iasexual;
    parameterPanel.add(asexual_label);
    asexual_label.setText("asexual: "+asexual+"%");
    asexual_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,asexual,10,0,110);
    asexual_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(asexual_scrollbar);

    // set up the payoff matrix and its panel
    payoffMatrixPanel = new Panel();
    payoffMatrixPanel.setLayout(new GridLayout(2,2));
    for (int i=0; i<=1; ++i)
      for (int j=0; j<=1; ++j) {
        payoff[i][j] = ipayoff[i][j];
        payoffField[i][j] = new TextField(3);
        payoffField[i][j].setText(Integer.toString(payoff[i][j]));
        payoffField[i][j].addKeyListener(this);
        payoffMatrixPanel.add(payoffField[i][j]);
    }
    controlPanel.add("East",payoffMatrixPanel);

    // set up the report area
    barGraph = new BarGraph();
    barGraph.setSize(barWidth,barHeight);
    add("South",barGraph);

    // set up the statistics timeGraph
    timeGraph = new ColorGraph();
    timeGraph.setSize(statWidth,statHeight);
    add(timeGraph);
  }//init

// thread control variables and methods

  private Thread thread = null;
  private boolean doStop = false;

  public void go() {
    if (thread == null) {
      thread = new Thread(this);
      thread.start();
      doStop = false;
    }
  }

  public void stop() {
    doStop = true;
  }

  private void startRun () {
    timeGraph.init(Color.gray,players,200);
    barGraph.init(Genome.length,Color.white,Color.gray);
    barGraph.setBarColor(0,Color.red);
    barGraph.setBarColor(1,Color.green);
    barGraph.setBarColor(2,Color.blue);
    range = new Range(players);
    matchNumber = 0;
    go();
  }

  public void run() {
    do {
      matchNumber++;
      range.graphUpdate(timeGraph);
      range.doSimulation(deathRate/100.0,gamesPerMatch,payoff,mutationRate/1000.0,asexual/100.0);
      if (matchNumber%5 == 0) {
        range.barGraphUpdate(barGraph);
        barGraph.repaint();
        timeGraph.repaint();
        try {thread.sleep(50);} // wait this many milliseconds
        catch(Exception e) {};
      } // if
    } while (!doStop);
    timeGraph.repaint();
    thread = null;
    doStop = false;
  } // run

  public void actionPerformed(ActionEvent e) {
    if (e.getActionCommand().equals("Start")) {
      startButton.setLabel("Stop");
      pauseButton.setLabel("Pause");
      pauseButton.setEnabled(true);
      resetButton.setEnabled(false);
      players_scrollbar.setEnabled(false);
      startRun();
    } else if (e.getActionCommand().equals("Stop")) {
      stop();
      startButton.setLabel("Start");
      pauseButton.setLabel("Resume");
      resetButton.setEnabled(true);
      players_scrollbar.setEnabled(true);    
    } else if (e.getActionCommand().equals("Pause")) {
      stop();
      pauseButton.setLabel("Resume");
    } else if (e.getActionCommand().equals("Resume")) {
      go();
      pauseButton.setLabel("Pause");
    } else if (e.getActionCommand().equals("Reset")) {
      players = iPlayers;
      gamesPerMatch = igamesPerMatch;
      deathRate = ideathRate;
      mutationRate = imutationRate;
      asexual = iasexual;
      for (int i=0; i<=1; ++i)
        for (int j=0; j<=1; ++j)
          payoff[i][j] = ipayoff[i][j];
      resetControlPanel();
    }
  } // actionPerformed

  public void adjustmentValueChanged(AdjustmentEvent e) {
    if (e.getSource() == players_scrollbar) {
      players = e.getValue();
      players += (players%2); // make sure it's even
    } else if (e.getSource() == gamesPerMatch_scrollbar) {
      gamesPerMatch = e.getValue();
    } else if (e.getSource() == deathRate_scrollbar) {
      deathRate = e.getValue();
    } else if (e.getSource() == mutationRate_scrollbar) {
      mutationRate = e.getValue();
    } else if (e.getSource() == asexual_scrollbar) {
      asexual = e.getValue();
    }
    resetControlPanel();
  } // adjustmentValueChanged

  void resetControlPanel () {
    players_label.setText("players: "+players);
    players_scrollbar.setValue(players);
    gamesPerMatch_label.setText("games/match: "+gamesPerMatch);
    gamesPerMatch_scrollbar.setValue(gamesPerMatch);
    deathRate_label.setText("death rate: "+deathRate/100.0);
    deathRate_scrollbar.setValue(deathRate);
    mutationRate_label.setText("mutate rate: "+mutationRate/1000.0);
    mutationRate_scrollbar.setValue(mutationRate);
    asexual_label.setText("asexual: "+asexual+"%");
    asexual_scrollbar.setValue(asexual);
    for (int i=0; i<=1; ++i)
      for (int j=0; j<=1; ++j) {
        payoffField[i][j].setText(Integer.toString(payoff[i][j]));
    }
    repaint();
  } //resetScrollBars

  public void keyPressed(KeyEvent e) {}

  public void keyReleased(KeyEvent e) {
    for (int i=0; i<=1; ++i)
      for (int j=0; j<=1; ++j)
        if (e.getSource() == payoffField[i][j]) {
          String text = payoffField[i][j].getText();
          int newvalue;
          try { payoff[i][j] = Integer.parseInt(text);}
          catch (NumberFormatException exc) {
            payoffField[i][j].setText(Integer.toString(payoff[i][j]));          }
          break;
        }
  }

  public void keyTyped(KeyEvent e) {}

} // applet


