/*----------------------------------------------------------------------+
|       Title:  Phyltree.java                                           |
|               Java Class                                              |
|               (used in Phylap.java applet)                            |
|                                                                       |
|       Author: David E. Joyce                                          |
|               Department of Mathematics and Computer Science          |
|               Clark University                                        |
|               Worcester, MA 01610-1477                                |
|               U.S.A.                                                  |
|                                                                       |
|               http://aleph0.clarku.edu/~djoyce/                       |
|                                                                       |
|       Date:   January, 1996, updated November, 2002.                  |
+----------------------------------------------------------------------*/

import java.awt.*;
import java.lang.*;

/*----------------------------------------------------------------------+
|       Class   PhylPanel                                               |
+----------------------------------------------------------------------*/

public class PhylPanel extends Panel {
  private Phyltree t;
  private int displayNumbers;   // used when painting the component:
                        //   if 0, always display dots; if 1 display numbers
                        //   for leaves only; if 2, always display numbers.
  private int compare[];  // resulting comparison with another tree, if needed

  public PhylPanel (Phyltree tVal) {
    t = tVal;
    setFont(new Font("TimesRoman",Font.PLAIN,10));
  }

  public void setComparison (int[] compareVal) {
    compare = compareVal;
  }

  private double compute_x(int i, double current_x, double dx,
                   int x[], boolean computed[]) {
    if (computed[i]) return current_x;
    computed[i]=true;
    if (i < t.getLeaves()) {
      x[i] = (int)current_x;
      current_x += dx;
    } else {
      int lj=t.getEldest(i), rj=t.getEldest(i);
      for (int j = t.getEldest(i); j != -1; j = t.getSib(j)) {
        current_x = compute_x(j,current_x,dx,x,computed);
        if (x[j] < x[lj]) lj = j;
        if (x[j] > x[rj]) rj = j;
      }
      if (2.0*t.getHeight(i) == t.getHeight(lj) + t.getHeight(rj))
        x[i] = (x[lj]+x[rj])/2;
      else
        x[i] = (int)((t.getHeight(i)*(x[lj]+x[rj])
                      - x[lj]*t.getHeight(rj)
                      - x[rj]*t.getHeight(lj))
                   / (2.0*t.getHeight(i)
                      - t.getHeight(lj)
                      - t.getHeight(rj)));
    } // if/else
    if (t.getParent(i) != -1)
      current_x = compute_x(t.getParent(i),current_x,dx,x,computed);
    return current_x;
  } // compute_x

  private void drawNumber(Graphics g, int i, int x, int y, Color bg) {
    String digits = Integer.toString(i);
    Color curColor = g.getColor();
    g.setColor(bg);
    FontMetrics fm = g.getFontMetrics();
    int w = fm.stringWidth(digits)+10;
    int h = fm.getHeight()+4;
    g.fillRoundRect(x-w/2+1,y-h/2, w-3,h-1, 6,5);
    g.setColor(curColor);
    g.drawRoundRect(x-w/2+1,y-h/2, w-3,h-1, 6,5);
    g.drawString(digits, x-(w-10)/2, y-(h-4)/2 + fm.getAscent());
  } // drawNumber

  private void drawScale(Graphics g, double max, int x0, int x1, int y0, int y1) {
    double logmax = Math.log(max)/Math.log(10.0);
    double base = Math.pow(10.0,Math.floor(logmax));
    g.setColor(Color.black);
    g.drawLine(x1,y0,x1,y1);
    FontMetrics fm = g.getFontMetrics();
    int h = fm.getHeight()/3;
    for (int i=0; i<10; ++i) {
      int x = x0;
      double numeral = Math.round(i*base);
      if (numeral > t.getTreeHeight()) {
        numeral /= 10.0;
        x += 4;
      }
      int y = y0-(int)((y0-y1)*numeral/max);
      if (i==1 || i==2 || i==3 || i==5) {
        Double dnum = new Double(numeral);
        String digits = dnum.toString();
        g.drawString(digits, x, y+h);
        x += fm.stringWidth(digits) + 2;
      }
      else
        x += 8;
      if (x < x1) g.drawLine(x,y,x1,y);
    } // for
  }  // drawScale

  private void drawCurve(Graphics g, int theta, int a, int b, int c, int d) {
    if (theta == 0.0 || Math.abs(a-c)<=1 || Math.abs(b-d)<=1)
      g.drawLine(a,b,c,d);
    else {
      double thetaRad = Math.PI*theta/180.0;
      int r = (int)((a-c)/(1.0-Math.cos(thetaRad)));
      r = Math.abs(r);
      int s = (int)((b-d)/Math.sin(thetaRad));
      if (a > c)
        g.drawArc(a-2*r,b-s,2*r,2*s,0,theta);
      else
        g.drawArc(a,b-s,2*r,2*s,180-theta,theta);
    } // if/else
  } // drawCurve

  public void update(Graphics g) {
    int n = t.getLeaves();
    displayNumbers = (n>25)? 0 : 1;
    int assigned = t.getAssigned();
    double treeHeight = t.getTreeHeight();
    Color bg = getBackground();
    Dimension d = getSize();
    int h = d.height;
    int w = d.width;
    g.setColor(bg);
    g.fillRect(0,0,w,h);
    double dx = w/(n+1.0);
    int border = (int)(dx/2.0);
    // select a convenient vertical scale and draw it
    drawScale (g,treeHeight,0,(int)dx,h-border,border);
    // first determine the coordinates of the vertices
    int i,j;
    boolean computed[] = new boolean[assigned];
    for (i=0; i<assigned; ++i)
      computed[i] = false;
    int x[] = new int[assigned];
    int y[] = new int[assigned];
    double current_x = 1.5*dx;
    for (i=0; i<assigned; ++i) {
      y[i] = h - border - (int)((h-dx)*t.getHeight(i)/treeHeight);
      if (!computed[i])
        current_x = compute_x(i,current_x,dx,x,computed);
    }
    // About the colors: black if corresponding clade in other tree or
    //   if no other tree; red if corresponding clade miscorresponds;
    //   gree if no corresponding clade in other tree.
    // Now draw the lines
    for (i=0; i<assigned; ++i)
      if ((j=t.getParent(i)) != -1) {
        if (compare==null || compare[j-n]==0) g.setColor(Color.black);
        else if (compare[j-n]>0)              g.setColor(Color.red);
        else                                  g.setColor(Color.green);
        drawCurve(g,45, x[i],y[i], x[j],y[j]);
      }
    // draw the vertices
    for (i=0; i<assigned; ++i) {
      if (i<n || compare==null || compare[i-n]==0)
                                  g.setColor(Color.black);
      else if (compare[i-n] > 0)  g.setColor(Color.red);
      else                        g.setColor(Color.green);
      if (displayNumbers==1 || (displayNumbers==1 && i<n))
        drawNumber(g, i+1, x[i],y[i], bg);
      else      // display dot
        g.fillOval(x[i]-2,y[i]-2,4,4);
    } // for
  } // update

  public void paint(Graphics g) {
    repaint();
  }

} //PhylPanel
