// Applet user interface for algorithms for building phylogenetic trees
// from Durbin et al: Biological Sequence Analysis, CUP 1998, chapter 7.
// Peter Sestoft, sestoft@dina.kvl.dk 1999-12-07 version 0.3
// Reference:  http://www.dina.kvl.dk/~sestoft/bsa.html

// License: Anybody can use this code for any purpose, including
// teaching, research, and commercial purposes, provided proper
// reference is made to its origin.  Neither the author nor the Royal
// Veterinary and Agricultural University, Copenhagen, Denmark, can
// take any responsibility for the consequences of using this code.

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


// Applet user interface to the phylogenetic (clustering) algorithms
// UPGMA and Neighbour Joining

public class Match7Applet extends Applet {
  TextField dimInput    = new TextField("4", 10);
  Button newInputButton = new Button("New input size");
  Button randomButton   = new Button("Random data");
  Button buildButton    = new Button("Build trees");
  TreeFrame upgma, nj;
  ScrollPane scp = new ScrollPane();
  Panel insidescp = new Panel();
  int dim;
  InputPanel inp;  

  public void init() {
    setLayout(new BorderLayout());
    add(scp, "Center");
    scp.add(insidescp);
    Panel cont = new Panel();
    cont.setLayout(new GridLayout(1, 5));
    cont.add(new Label("Sequence count"));
    cont.add(dimInput);
    cont.add(newInputButton);
    cont.add(randomButton);
    cont.add(buildButton);
    add(cont, "South");

    dimInput.addActionListener(new dimInputListener());
    newInputButton.addActionListener(new dimInputListener());
    randomButton.addActionListener(new RandomButListener());
    buildButton.addActionListener(new buildListener());
    fillExample();
  }
   
  public void destroy() {
    if (upgma != null)
      upgma.dispose();
    if (nj != null)
      nj.dispose();
  }

  class dimInputListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      newInputPanel(Integer.parseInt(dimInput.getText()));
    }
  }

  class buildListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (inp != null) {
	double[][] ds = inp.getData();
	UPGMA upclu = new UPGMA(ds);
	if (upgma == null) {
	  upgma = new TreeFrame("UPGMA tree", upclu.getRoot());
	  upgma.addWindowListener(new UPGMAFrameClosed());
	}
	else
	  upgma.setCluster(upclu.getRoot());
	NJ njclu = new NJ(ds);
	if (nj == null) {
	  nj = new TreeFrame("Neighbour tree", njclu.getRoot());
	  nj.addWindowListener(new NJFrameClosed());
	}
	else
	  nj.setCluster(njclu.getRoot());
      }
    }
  }

  class RandomButListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      // Create Euclidean random data (to satisfy the triangle inequality)
      if (inp == null) 
	if (dimInput.getText().equals(""))
	  return;
	else
	  newInputPanel(Integer.parseInt(dimInput.getText()));
      double[] xs = new double[dim];
      double[] ys = new double[dim];
      for (int i=0; i<dim; i++) {
	xs[i] = Math.random();
	ys[i] = Math.random();
      }
      for (int i=0; i<dim; i++) 
	for (int j=0; j<i; j++) {
	  double dij = Math.sqrt(sqr(xs[i]-xs[j])+sqr(ys[i]-ys[j]));
	  dij = (int)(1000 * dij + 0.5) / 10.0;
	  inp.input[i][j].setText(Double.toString(dij));
	  inp.input[j][i].setText(Double.toString(dij));
	}
    }
    
    double sqr(double x) 
    { return x * x; }
  }

  class UPGMAFrameClosed extends WindowAdapter {
    public void windowClosed(WindowEvent e) {
      upgma = null;
    }
  }

  class NJFrameClosed extends WindowAdapter {
    public void windowClosed(WindowEvent e) {
      nj = null;
    }
  }

  private void newInputPanel(int dim) {
    if (inp != null)
      insidescp.remove(inp);
    this.dim = dim;
    inp = new InputPanel(dim);
    insidescp.add(inp);	
    scp.validate();
  }

  private void fillExample() {
    double[][] ex = { { },	// Figure 7.7, page 170 of Durbin et al.
		      { 0.3 },
		      { 0.5, 0.6 },
		      { 0.6, 0.5, 0.9 } };
    newInputPanel(ex.length);
    for (int i=0; i<dim; i++) 
      for (int j=0; j<i; j++) {
	double dij = ex[i][j];
	inp.input[i][j].setText(Double.toString(dij));
	inp.input[j][i].setText(Double.toString(dij));
      }
  }
}


// A lower triangular matrix for inputting sequence-to-sequence distances

class InputPanel extends Panel {
  TextField[][] input;
  int dim;
    
  public InputPanel(int dim) {
    this.dim = dim;
    setLayout(new GridLayout(dim+1, dim+1));
    add(new Label());
    input = new TextField[dim][dim];
    for (int j=1; j<=dim; j++) 
      add(new Label(Integer.toString(j), Label.CENTER));
    for (int i=1; i<=dim; i++) {
      add(new Label(Integer.toString(i), Label.CENTER));
      for (int j=1; j<=dim; j++) {
	input[i-1][j-1] = new TextField(10);
	add(input[i-1][j-1]);
	input[i-1][j-1].setEditable(i>j);
      }
      input[i-1][i-1].setText("0.0");
    }
  }

  public double[][] getData() { 
    double[][] ds = new double[dim][];
    for (int i=0; i<dim; i++) {
      ds[i] = new double[i];
      for (int j=0; j<i; j++) {
	input[j][i].setText(input[i][j].getText());
	ds[i][j] = (new Double(input[i][j].getText())).doubleValue();
      }
    }
    return ds;
  }	
}
