/*----------------------------------------------------------------------+
|      Title:	Moth.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/home.html                  |
|           djoyce@clarku.edu                                           |
|                                                                       |
|      Date:    February, 2001.                                         |
+----------------------------------------------------------------------*/


import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
import java.util.StringTokenizer;

public class Moth extends Applet implements ActionListener, AdjustmentListener
 {

  // default parameters determined by html page
  int benefit;           // benefit from altruist, in 10ths of a unit
  int cost;              // cost to altruist, in 10ths of a unit
  int herd_size;         // each herd has this population
  int init_alt;          // how many initial altruists
  int n_herds;           // the number of herds
  int deaths;            // this many die and are born each day in each herd
  int max_days;          // how many days to run the simulation
  int band_size;         // each migrating band has this size
  int migration_period;  // how many days between migrations
  int timeout;           // milliseconds between days
  Color background;      // background color for applet

  
  Range R = new Range(this); // the range where the herds of animals live
  RangeDisplay RD;       // the panel to display the range

  // Panels
  Panel controlPanel = new Panel();
  CardLayout controlManager = new CardLayout();
  Panel startControlPanel = new Panel();
  Panel runControlPanel = new Panel();
  Panel statisticsPanel = new Panel();

  // Start Control Panel components
  Button startButton = new Button("Start");
  Button resetButton = new Button("Reset");
  Label n_herds_label = new Label();
  Scrollbar n_herds_scrollbar;
  Label deaths_label = new Label();
  Scrollbar deaths_scrollbar;
  Label herd_size_label = new Label();
  Scrollbar herd_size_scrollbar;
  Label init_alt_label = new Label();
  Scrollbar init_alt_scrollbar;
  Label cost_label = new Label();
  Scrollbar cost_scrollbar;
  Label benefit_label = new Label();
  Scrollbar benefit_scrollbar;
  Label band_size_label = new Label();
  Scrollbar band_size_scrollbar;
  Label migration_period_label = new Label();
  Scrollbar migration_period_scrollbar;

  // Run Control Panel components
  Button pauseButton = new Button("Pause");
  Button restartButton = new Button("Restart");
  Button stopButton = new Button("Stop");
  Label cost_label2 = new Label();
  Scrollbar cost_scrollbar2;
  Label benefit_label2 = new Label();
  Scrollbar benefit_scrollbar2;
  Label deaths_label2 = new Label();
  Scrollbar deaths_scrollbar2;
  Label band_size_label2 = new Label();
  Scrollbar band_size_scrollbar2;
  Label migration_period_label2 = new Label();
  Scrollbar migration_period_scrollbar2;
  Label timeout_label = new Label();
  Scrollbar timeout_scrollbar;
  
  // Statistics Panel components
  TimeGraph indivStats;
  TimeGraph totStats;


  public String getAppletInfo() {
    return "Velcro. Copyright 2001, David Joyce, Clark University. Version 1.0";
  }

  public String[][] getParameterInfo() {
    String[][] pinfo = {
    {"cost",            "int","cost to altruist in 10ths of a unit"},
    {"benefit",         "int","benefit from altruist in 10ts of a unit"},
    {"deaths",          "int","deaths per day in each herd"},
    {"herd_size",       "int","each herd has this population"},
    {"init_alt",        "int","how many initial altruists"},
    {"n_herds",         "int","the number of herds"},
    {"deaths",   "int","this many die and are born each day in each herd"},
    {"max_days",        "int","how many days to run the simulation"},
    {"band_size",       "int","each migrating band has this size"},
    {"migration_period","int","how many days between migrations"},
    {"timeout",         "int","milliseconds between days"},
    {"background",      "color","applet background color"},
    };
    return pinfo;
  }

  static String colorName[] = {
	"black", "blue", "cyan", "darkGray", "gray", "green","lightGray", 
	"magenta", "orange", "pink", "red", "white", "yellow"};
  static Color constColor[] = {
	Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray,
	Color.green, Color.lightGray, Color.magenta, Color.orange,
	Color.pink, Color.red, Color.white, Color.yellow};

  Color parseColor (String str) {
    if (str==null || "none".equals(str)) return null;
    for (int i=0; i<colorName.length; ++i)
      if (colorName[i].equals(str)) return constColor[i];
    try { return new Color(Integer.parseInt(str,16));}
    catch (NumberFormatException exc) {}
    StringTokenizer t = new StringTokenizer(str,",");
    if (!t.hasMoreTokens()) return null;
    try {
      float hue = (float)(Integer.parseInt(t.nextToken())/360.0);
      if (!t.hasMoreTokens()) return null;
      float sat = (float)(Integer.parseInt(t.nextToken())/100.0);
      if (!t.hasMoreTokens()) return null;
      float bri = (float)(Integer.parseInt(t.nextToken())/100.0);
      return new Color(Color.HSBtoRGB(hue,sat,bri));
    } catch (NumberFormatException exc) {
      return null;
  } }

  public void init() {
    // initialize the parameters
    String param = getParameter("cost");
    cost = (param == null) ? 10 : Integer.parseInt(param);

    param = getParameter("benefit");
    benefit = (param == null) ? 50 : Integer.parseInt(param);

    param = getParameter("deaths");
    deaths = (param == null) ? 5 : Integer.parseInt(param);

    param = getParameter("herd_size");
    herd_size = (param == null) ? 100 : Integer.parseInt(param);

    param = getParameter("init_alt");
    init_alt = (param == null) ? 10 : Integer.parseInt(param);

    param = getParameter("n_herds");
    n_herds = (param == null) ? 10 : Integer.parseInt(param);

    param = getParameter("deaths");
    deaths = (param == null) ? 5 : Integer.parseInt(param);

    param = getParameter("max_days");
    max_days = (param == null) ? 50 : Integer.parseInt(param);

    param = getParameter("band_size");
    band_size = (param == null) ? 5 : Integer.parseInt(param);

    param = getParameter("migration_period");
    migration_period = (param == null) ? 20 : Integer.parseInt(param);

    param = getParameter("background");
    background = parseColor(param);
    if (background == null) background = Color.lightGray;

    param = getParameter("timeout");
    int timeout = 1000;
    if (param != null) {
      try { timeout = Integer.parseInt(param);}
      catch (NumberFormatException exc) {}
    }

    // create the range and its display
    R.reset(cost, benefit, herd_size, init_alt, n_herds, deaths,
            max_days, band_size, migration_period, timeout);
    RD = new RangeDisplay(R, background);
    R.start();
    
    // construct and place the components in the window
    setLayout(new BorderLayout());
    add("Center",RD);
    add("West",controlPanel);
    add("East",statisticsPanel);
    
    // the control panel is a deck of two different control panels
    controlPanel.setLayout(controlManager);
    controlPanel.add("Start controls",startControlPanel);
    controlPanel.add("Run controls",runControlPanel);
    
    // components of the start control panel
    startControlPanel.setLayout(new GridLayout(18,1));

    startButton.addActionListener(this);
    startControlPanel.add(startButton);

    resetButton.addActionListener(this);
    startControlPanel.add(resetButton);

    startControlPanel.add(n_herds_label);
    n_herds_label.setText(n_herds+" herds");
    n_herds_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, n_herds, 0, 1, 51);
    n_herds_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(n_herds_scrollbar);

    startControlPanel.add(herd_size_label);
    herd_size_label.setText("herd size: "+herd_size+"   ");
    herd_size_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, herd_size, 0, 1,
 10001);
    herd_size_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(herd_size_scrollbar);

    startControlPanel.add(deaths_label);
    deaths_label.setText("death rate: "+deaths);
    deaths_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, deaths, 0, 1,
 herd_size/2);
    deaths_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(deaths_scrollbar);
	
    startControlPanel.add(init_alt_label);
    init_alt_label.setText("altruists: "+init_alt+"   ");
    init_alt_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,
                         init_alt, 0, 1, herd_size+1);
    init_alt_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(init_alt_scrollbar);

    startControlPanel.add(band_size_label);
    band_size_label.setText("band size: "+band_size);
    band_size_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL,
                          band_size, 0, 0, herd_size/3);
    band_size_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(band_size_scrollbar);

    startControlPanel.add(migration_period_label);
    migration_period_label.setText("migration period: "+migration_period);
    migration_period_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, 
                                 migration_period, 0, 0, 101);
    migration_period_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(migration_period_scrollbar);

    startControlPanel.add(cost_label);
    cost_label.setText("cost: "+cost/10.0);
    cost_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, cost, 0, 1, 101);
    cost_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(cost_scrollbar);

    startControlPanel.add(benefit_label);
    benefit_label.setText("benefit: "+benefit/10.0);
    benefit_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, benefit, 0, 1,
 101);
    benefit_scrollbar.addAdjustmentListener(this);
    startControlPanel.add(benefit_scrollbar);

    // components of the run control panel
    runControlPanel.setLayout(new GridLayout(15,1));
    pauseButton.addActionListener(this);
    runControlPanel.add(pauseButton);
    restartButton.addActionListener(this);
    runControlPanel.add(restartButton);
    stopButton.addActionListener(this);
    runControlPanel.add(stopButton);

    runControlPanel.add(deaths_label2);
    deaths_label2.setText("death rate: "+deaths);
    deaths_scrollbar2 = new Scrollbar (Scrollbar.HORIZONTAL, deaths, 0, 1,
 herd_size/2);
    deaths_scrollbar2.addAdjustmentListener(this);
    runControlPanel.add(deaths_scrollbar2);

    runControlPanel.add(band_size_label2);
    band_size_label2.setText("band size: "+band_size);
    band_size_scrollbar2 = new Scrollbar (Scrollbar.HORIZONTAL, band_size, 0, 0,
 herd_size/3);
    band_size_scrollbar2.addAdjustmentListener(this);
    runControlPanel.add(band_size_scrollbar2);

    runControlPanel.add(migration_period_label2);
    migration_period_label2.setText("migration period: "+migration_period);
    migration_period_scrollbar2 = new Scrollbar (Scrollbar.HORIZONTAL,
 migration_period, 0, 0, 101);
    migration_period_scrollbar2.addAdjustmentListener(this);
    runControlPanel.add(migration_period_scrollbar2);

    runControlPanel.add(cost_label2);
    cost_label2.setText("cost: "+cost/10.0);
    cost_scrollbar2 = new Scrollbar (Scrollbar.HORIZONTAL, cost, 0, 1, 101);
    cost_scrollbar2.addAdjustmentListener(this);
    runControlPanel.add(cost_scrollbar2);

    runControlPanel.add(benefit_label2);
    benefit_label2.setText("benefit: "+benefit/10.0);
    benefit_scrollbar2 = new Scrollbar (Scrollbar.HORIZONTAL, benefit, 0, 1,
 101);
    benefit_scrollbar2.addAdjustmentListener(this);
    runControlPanel.add(benefit_scrollbar2);


    runControlPanel.add(timeout_label);
    timeout_label.setText("timeout: "+timeout/1000.0+"   ");
    timeout_scrollbar = new Scrollbar (Scrollbar.HORIZONTAL, timeout, 0, 0,
 1001);
    timeout_scrollbar.addAdjustmentListener(this);
    runControlPanel.add(timeout_scrollbar);
	
    // components of the statistics panel
    statisticsPanel.setLayout(new GridLayout(2,1,4,4));
    int statWidth = getSize().width/4-4;
    int statHeight = getSize().height/2-4;
    indivStats = new TimeGraph(statWidth, background);
    indivStats.setSize(statWidth,statHeight);
    statisticsPanel.add(indivStats);
    totStats = new TimeGraph(statWidth, background);
    totStats.setSize(statWidth,statHeight);
    statisticsPanel.add(totStats);
    R.addStats(totStats, indivStats);
  }

  public void actionPerformed(ActionEvent e) {
    if (e.getActionCommand().equals("Pause")) {
      pauseButton.setLabel("Continue");
      R.running = false;
    } else if (e.getActionCommand().equals("Continue")) {
      pauseButton.setLabel("Pause");
      R.running = true;
    } else if (e.getActionCommand().equals("Restart")) {
      pauseButton.setLabel("Pause");
      R.resetHerds();
      totStats.clear();
      indivStats.clear();
      R.running = true;
    } else if (e.getActionCommand().equals("Start")) {
      pauseButton.setLabel("Pause");
      controlManager.last(controlPanel);
      R.resetHerds();
      totStats.clear();
      indivStats.clear();
      R.running = true;
    } else if (e.getActionCommand().equals("Stop")) {
      pauseButton.setLabel("Pause");
      controlManager.first(controlPanel);
      R.running = false;
    } else if (e.getActionCommand().equals("Reset")) {
      R.reset(cost, benefit, herd_size, init_alt, n_herds, deaths,
            max_days, band_size, migration_period, timeout);
      R.resetHerds();
      RD.resetRangeDisplay();
      resetScrollBars();
    }
  }
  
  public void adjustmentValueChanged(AdjustmentEvent e) {
    if (e.getSource() == n_herds_scrollbar) {
      R.n_herds = e.getValue();
      R.resetHerds();
      RD.resetRangeDisplay();
      resetScrollBars();
    } else if (e.getSource() == deaths_scrollbar
            || e.getSource() == deaths_scrollbar2) {
      R.deaths = e.getValue();
      resetScrollBars();
    } else if (e.getSource() == herd_size_scrollbar) {
      R.herd_size = e.getValue();
      if (R.init_alt > R.herd_size)
        R.init_alt = R.herd_size;
      if (R.band_size > R.herd_size/3)
        R.band_size = R.herd_size/3;
      resetScrollBars();
    } else if (e.getSource() == init_alt_scrollbar) {
      R.init_alt = e.getValue();
      R.resetHerds();
      resetScrollBars();
    } else if (e.getSource() == band_size_scrollbar
            || e.getSource() == band_size_scrollbar2) {
      R.band_size = e.getValue();
      if (e.getSource() == band_size_scrollbar)
        R.resetHerds();
      resetScrollBars();
    } else if (e.getSource() == migration_period_scrollbar
            || e.getSource() == migration_period_scrollbar2) {
      R.migration_period = e.getValue();
      if (e.getSource() == migration_period_scrollbar)
        R.resetHerds();
      resetScrollBars();
    } else if (e.getSource() == cost_scrollbar
            || e.getSource() == cost_scrollbar2) {
      R.cost = e.getValue();
      resetScrollBars();
    } else if (e.getSource() == benefit_scrollbar
            || e.getSource() == benefit_scrollbar2) {
      R.benefit = e.getValue();
      resetScrollBars();
    } else if (e.getSource() == timeout_scrollbar) {
      R.timeout = e.getValue();
      resetScrollBars();
	}
  }
  
  void resetScrollBars () {
    n_herds_label.setText(R.n_herds+" herds");
    n_herds_scrollbar.setValue(R.n_herds);
    herd_size_label.setText("herd size: "+R.herd_size);
    herd_size_scrollbar.setValue(R.herd_size);
    deaths_label.setText("death rate: "+R.deaths);
    deaths_scrollbar.setValue(R.deaths);
    deaths_scrollbar.setMaximum(R.herd_size/2);
    deaths_label2.setText("death rate: "+R.deaths);
    deaths_scrollbar2.setValue(R.deaths);
    deaths_scrollbar2.setMaximum(R.herd_size/2);
    init_alt_label.setText("altruists: "+R.init_alt);
    init_alt_scrollbar.setValue(R.init_alt);
    init_alt_scrollbar.setMaximum(R.herd_size+1);
    band_size_label.setText("band size: "+R.band_size);
    band_size_label2.setText("band size: "+R.band_size);
    band_size_scrollbar.setValue(R.band_size);
    band_size_scrollbar.setMaximum(R.herd_size/3);
    band_size_scrollbar2.setValue(R.band_size);
    band_size_scrollbar2.setMaximum(R.herd_size/3);
    migration_period_label.setText("migration period: "+R.migration_period);
    migration_period_scrollbar.setValue(R.migration_period);
    migration_period_label2.setText("migration period: "+R.migration_period);
    migration_period_scrollbar2.setValue(R.migration_period);
    cost_label.setText("cost: "+R.cost/10.0);
    cost_scrollbar.setValue(R.cost);
    cost_label2.setText("cost: "+R.cost/10.0);
    cost_scrollbar2.setValue(R.cost);
    benefit_label.setText("benefit: "+R.benefit/10.0);
    benefit_scrollbar.setValue(R.benefit);
    benefit_label2.setText("benefit: "+R.benefit/10.0);
    benefit_scrollbar2.setValue(R.benefit);
    timeout_label.setText("timeout: "+R.timeout/1000.0);
    timeout_scrollbar.setValue(R.timeout);
    repaint();
  }
  
}
