/*----------------------------------------------------------------------+
|      Title:	ArenaApplet.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 ArenaApplet
  extends Applet
  implements ActionListener, AdjustmentListener, MouseListener,
             KeyListener, Runnable, WindowListener {

  // 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 = 5; // per 1000
  int mutationSize, imutationSize = 10; // percent
  int numberOfHerds, inumberOfHerds = 20;
  int asexual, iasexual = 95; // percent
  int bandSize, ibandSize = 4;
  int migrationPeriod, imigrationPeriod = 50;
  int ipayoff[][] = {{3,0},{5,1}};
  int payoff[][] = new int[2][2];

  // red/green/blue gene selection
  int colorFocus = -1;

  // components
  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 mutationSize_label = new Label();
  Scrollbar mutationSize_scrollbar;
  Label asexual_label = new Label();
  Scrollbar asexual_scrollbar;
  Label numberOfHerds_label = new Label();
  Scrollbar numberOfHerds_scrollbar;
  Label bandSize_label = new Label();
  Scrollbar bandSize_scrollbar;
  Label migrationPeriod_label = new Label();
  Scrollbar migrationPeriod_scrollbar;

  Panel payoffMatrixPanel;
  TextField payoffField[][] = new TextField[2][2];

  Frame remoteFrame;
  ColorGraph timeGraph;
  BarGraph  barGraph;
  final static int remoteFrameWidth=400;
  final static int remoteFrameHeight=350;
  final static int timeGraphHeight=200;
  final static int barGraphHeight=100;
  Button redButton = new Button("Red");
  Button greenButton = new Button("Green");
  Button blueButton = new Button("Blue");
 Button whiteButton = new Button("White");

  // data
  Range range;
  RangeDisplay RD;       // the panel to display the 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("West",controlPanel);

    // The buttonPanel will make up part of the control panel
    Panel buttonPanel = new Panel();
    buttonPanel.setLayout(new GridLayout(1,3));
    controlPanel.add("North",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(18,1);
    parameterPanelLayout.setHgap(10);
    parameterPanel.setLayout(parameterPanelLayout);
    controlPanel.add(parameterPanel);

    parameterPanel.add(players_label);
    players_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,players,20,2,519);
    players_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(players_scrollbar);

    parameterPanel.add(gamesPerMatch_label);
    gamesPerMatch_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,gamesPerMatch,10,1,210);
    gamesPerMatch_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(gamesPerMatch_scrollbar);

    parameterPanel.add(deathRate_label);
    deathRate_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,deathRate,3,1,23);
    deathRate_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(deathRate_scrollbar);

    parameterPanel.add(mutationRate_label);
    mutationRate_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,mutationRate,10,0,110);
    mutationRate_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(mutationRate_scrollbar);

    parameterPanel.add(mutationSize_label);
    mutationSize_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,mutationSize,10,0,110);
    mutationSize_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(mutationSize_scrollbar);

    parameterPanel.add(asexual_label);
    asexual_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,asexual,10,0,110);
    asexual_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(asexual_scrollbar);

    parameterPanel.add(numberOfHerds_label);
    numberOfHerds_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,numberOfHerds,10,0,110);
    numberOfHerds_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(numberOfHerds_scrollbar);

    parameterPanel.add(bandSize_label);
    bandSize_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,bandSize,5,0,55);
    bandSize_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(bandSize_scrollbar);

    parameterPanel.add(migrationPeriod_label);
    migrationPeriod_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,migrationPeriod,10,0,110);
    migrationPeriod_scrollbar.addAdjustmentListener(this);
    parameterPanel.add(migrationPeriod_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) {
        payoffField[i][j] = new TextField(3);
        payoffField[i][j].addKeyListener(this);
        payoffMatrixPanel.add(payoffField[i][j]);
    }
    controlPanel.add("South",payoffMatrixPanel);

    // set up the range display
    RD = new RangeDisplay(Color.white);
    add(RD);

    // set up the remote frame
    remoteFrame = new Frame("Statistics");
    remoteFrame.setSize(remoteFrameWidth,remoteFrameHeight);
    BorderLayout remoteFrameLayout = new BorderLayout();
    remoteFrameLayout.setHgap(5);
    remoteFrame.setLayout(remoteFrameLayout);
    remoteFrame.addWindowListener(this);
    timeGraph = new ColorGraph();
    timeGraph.setSize(remoteFrameWidth,timeGraphHeight);
    remoteFrame.add(timeGraph);
    barGraph = new BarGraph();
    barGraph.setSize(remoteFrameWidth,barGraphHeight);
    barGraph.addMouseListener(this);
    remoteFrame.add("South",barGraph);

    Panel remoteButtonPanel = new Panel();
    remoteButtonPanel.setLayout(new FlowLayout());
    redButton.addActionListener(this);
    remoteButtonPanel.add(redButton);
    greenButton.addActionListener(this);
    remoteButtonPanel.add(greenButton);
    blueButton.addActionListener(this);
    remoteButtonPanel.add(blueButton);
    whiteButton.addActionListener(this);
    remoteButtonPanel.add(whiteButton);

    remoteFrame.add("North",remoteButtonPanel);

    resetParameters();
   }//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.getLength(),Color.white,Color.gray);
    barGraph.setBarColor(Genome.getRedGene(),Color.red);
    barGraph.setBarColor(Genome.getGreenGene(),Color.green);
    barGraph.setBarColor(Genome.getBlueGene(),Color.blue);
    remoteFrame.setVisible(true);
    range = new Range(players,numberOfHerds);
    RD.setRange(range);
    matchNumber = 0;
    go();
  }

  public void run() {
    do {
      matchNumber++;
      range.graphUpdate(timeGraph);
      range.doSimulation(deathRate/100.0,gamesPerMatch,payoff,
                         mutationRate/1000.0,mutationSize/100.0,asexual/100.0);
      if (migrationPeriod != 0
          && bandSize != 0
          && matchNumber % migrationPeriod == 0)
        range.migrate(bandSize);
      if (matchNumber%5 == 0) {
        range.statUpdate();
        range.barGraphUpdate(barGraph);
        barGraph.repaint();
        RD.repaint();
        timeGraph.repaint();
        try {thread.sleep(30);} // wait this many milliseconds, some browsers need time
        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);
      numberOfHerds_scrollbar.setEnabled(false);
      startRun();
    } else if (e.getActionCommand().equals("Stop")) {
      stop();
      startButton.setLabel("Start");
      pauseButton.setLabel("Resume");
      resetButton.setEnabled(true);
      players_scrollbar.setEnabled(true);
      numberOfHerds_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")) {
      resetParameters();
    } else if (e.getActionCommand().equals("Red")) {
      colorFocus = 0;
      redButton.setBackground(Color.red);
      greenButton.setBackground(Color.lightGray);
      blueButton.setBackground(Color.lightGray);
      whiteButton.setBackground(Color.lightGray);
   } else if (e.getActionCommand().equals("Green")) {
      colorFocus = 1;
      redButton.setBackground(Color.lightGray);
      greenButton.setBackground(Color.green);
      blueButton.setBackground(Color.lightGray);
      whiteButton.setBackground(Color.lightGray);
    } else if (e.getActionCommand().equals("Blue")) {
      colorFocus = 2;
      redButton.setBackground(Color.lightGray);
      greenButton.setBackground(Color.lightGray);
      blueButton.setBackground(Color.blue);
      whiteButton.setBackground(Color.lightGray);
    } else if (e.getActionCommand().equals("White")) {
      colorFocus = 3;
      redButton.setBackground(Color.lightGray);
      greenButton.setBackground(Color.lightGray);
      blueButton.setBackground(Color.lightGray);
      whiteButton.setBackground(Color.white);
    }
  } // actionPerformed

  private void resetParameters() {
    players = iPlayers;
    gamesPerMatch = igamesPerMatch;
    deathRate = ideathRate;
    mutationRate = imutationRate;
    mutationSize = imutationSize;
    asexual = iasexual;
    numberOfHerds = inumberOfHerds;
    bandSize = ibandSize;
    migrationPeriod = imigrationPeriod;
    for (int i=0; i<=1; ++i)
      for (int j=0; j<=1; ++j)
        payoff[i][j] = ipayoff[i][j];
    Genome.setColorGenes(0,1,2);
    colorFocus = 0;
    resetControlPanel();
   } // resetparameters

  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() == mutationSize_scrollbar) {
      mutationSize = e.getValue();
    } else if (e.getSource() == asexual_scrollbar) {
      asexual = e.getValue();
    } else if (e.getSource() == numberOfHerds_scrollbar) {
      numberOfHerds = e.getValue();
    } else if (e.getSource() == bandSize_scrollbar) {
      bandSize = e.getValue();
    } else if (e.getSource() == migrationPeriod_scrollbar) {
      migrationPeriod = 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);
    mutationSize_label.setText("mutate size: "+mutationSize+"%");
    mutationSize_scrollbar.setValue(mutationSize);
    asexual_label.setText("asexual: "+asexual+"%");
    asexual_scrollbar.setValue(asexual);
    numberOfHerds_label.setText("herds: "+numberOfHerds);
    numberOfHerds_scrollbar.setValue(numberOfHerds);
    if (bandSize > players/3)
      bandSize = players/3;
    bandSize_label.setText("bandSize: "+bandSize);
    bandSize_scrollbar.setValue(bandSize);
    migrationPeriod_label.setText("migration period: "+migrationPeriod);
    migrationPeriod_scrollbar.setValue(migrationPeriod);
    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 keyTyped(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;
        } // if
  } // keyReleased

  // window listener methods
  public void windowActivated(WindowEvent e) {}
  public void windowClosed(WindowEvent e) {}
  public void windowClosing(WindowEvent e) {
    remoteFrame.setVisible(false);
  }
  public void windowDeactivated(WindowEvent e) {}
  public void windowDeiconified(WindowEvent e) {}
  public void windowIconified(WindowEvent e) {}
  public void windowOpened(WindowEvent e) {}

  // mouse methods
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  public void mousePressed(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}

  public void mouseClicked(MouseEvent e) {
      // only from barGraph
      int b = barGraph.getBar(e.getX(),e.getY());
      if (b == -1) return;
      if (colorFocus == 0) {
        barGraph.setBarColor(Genome.getRedGene(),Color.gray);
        Genome.setRedGene(b);
        barGraph.setBarColor(b,Color.red);
      } else if (colorFocus == 1) {
        barGraph.setBarColor(Genome.getGreenGene(),Color.gray);
        Genome.setGreenGene(b);
        barGraph.setBarColor(b,Color.green);
      } else if (colorFocus == 2) {
        barGraph.setBarColor(Genome.getBlueGene(),Color.gray);
        Genome.setBlueGene(b);
        barGraph.setBarColor(b,Color.blue);
      }else { // colorFocus == 3
        barGraph.setBarColor(Genome.getRedGene(),Color.gray);
        barGraph.setBarColor(Genome.getGreenGene(),Color.gray);
        barGraph.setBarColor(Genome.getBlueGene(),Color.gray);
        Genome.setRedGene(b);
        Genome.setGreenGene(b);
        Genome.setBlueGene(b);
        barGraph.setBarColor(b,Color.lightGray);
      }
      barGraph.repaint();
    } // mouseClicked

} // applet
