import java.io.*;
import java.lang.*;
import choco.integer.IntVar;

public class Node  {
    private int branchLength;
    private int divergenceDate;
    private int speciesNo;
    private Node parent;
    private NodeVector children;
    private String species;
    private String label;
    private int depth;
    private Tree tree;
    private int id;

    public Node() {children = new NodeVector();}

    public Node(int bl,int dd,String s,Node p,Dict D,Tree tree) {
	branchLength = bl;
	divergenceDate = dd;
	label = "" + dd;
	parent = p;
	id = tree.getCount();
	tree.incCount();
	if (p != null) {
	    p.children.addElement(this);
	    branchLength = dd - p.getDivergenceDate();
	    depth = p.depth + 1;}
	else depth = 0;
	this.tree = tree;
	if (s == "") {
	    children = new NodeVector();
	    tree.addInteriorNode(this);
	    speciesNo = -1;
	}
	else {
	    speciesNo = D.find(s);
	    children = null;
	    species = s;
	    label = s;
	    tree.addLeafNode(this);
	}
    }

    public Node leafNode(int dd,int speciesNo,Dict D,Tree tree) {
	return new Node(1,dd,D.elementAt(speciesNo),this,D,tree);
    }

    public Node interiorNode(int dd,Dict D,Tree tree) {
	if (divergenceDate == dd) return this;
	else return new Node(1,dd,"",this,D,tree);
    }

    protected static int readInt(MyIo fin) throws IOException {
	char c = fin.getNextChar();
	int v = 0;
	while (Character.isDigit(c)){v = v * 10 + Character.digit(c,10);c = fin.getNextChar();}
	return v;
    }
       
   
    public static Node readNode(String fname,Dict D,Tree T) throws FileNotFoundException, IOException {
	MyIo fin = new MyIo(fname);
	Node N = new Node();
	int v = 0;
	char c = fin.getNextChar();
	while (c != ';') {
	    if (c == '(') {
		N = new Node(-1,-1,"",N,D,T); // interior node
		c = fin.getNextChar(); 
	    }
	    else if (c == ')') {
		N.divergenceDate = -1;
		N.branchLength = -1;
		N = N.parent;
		c = fin.getNextChar();
	    }
	    else if (Character.isLetter(c)){
		String s = "";
		while (Character.isLetter(c) || c == '_'){
		    s = s + c;
		    c = fin.getNextChar();
		}
		new Node(-1,-1,s,N,D,T);
	    }
	    else c = fin.getNextChar();
	}
	fin.close();
	return N.children.elementAt(0);
    }

    public boolean isLeaf(){return children == null || children.isEmpty();}
    public boolean isRoot(){return parent == null;}
    public NodeVector getChildren(){return children;}
    public String getSpecies(){return species;}
    public int getSpeciesNo(){return speciesNo;}
    public int getDivergenceDate(){return divergenceDate;}
    public int degree(){return children.size();}
    public int getDepth(){return depth;}
    public int getId(){return id;}
    public Node getParent(){return parent;}
    public void setSpecies(Node v){
	species = v.species; 
	speciesNo = v.speciesNo;
	label = species;
    }

    public void setSpecies(Integer i,Dict d){
	speciesNo = i.intValue();
	species = (String)d.get(i.intValue());
	label = species;
    }
    public void clearChildren(){children.clear();}

    public String look(){
	String s = "id: " + id + " Depth: " + depth + " Species: " + species + " DDate: " + divergenceDate +
	           " BLen: " + branchLength + " SpecNo: " + speciesNo + " children: ";
	if (!isLeaf()){
	    NodeEnumeration e = children.elements();
	    while (e.hasMoreElements()) s = s + e.nextElement().getId() + " ";
	}
	if (!isRoot()) s = s + " parent: " + parent.getId();
	return s;
    }

    //
    // ---------------------------------------------------------------
    //                    breakUp functions
    //

    public static Node getAChildOf(Node v){
	if (v.isLeaf()) return v;
	else return getAChildOf(v.children.get(0));
    }

    public static Node getASiblingOf(Node v){
	Node p  = v.parent;
	Node gp = p.parent;
	int i = 0;
	while (p == gp.children.get(i)) i++;
	return getAChildOf(gp.children.get(i));
    }			
    //
    //
    // ---------------------------------------------------------------
    //

     public static Node mrca(Node n1,Node n2){
	if (n1.depth > n2.depth) return mrca(n1.parent,n2);
	else if (n1.depth < n2.depth) return mrca(n1,n2.parent);
	else if (n1.depth == n2.depth && !(n1.equals(n2))) return mrca(n1.parent,n2.parent);
	else {
	    //System.out.println("MRCA: " + n1.look()); 
	    return n1;
	}
    }
	
	
    public String toString(){
	//System.out.println(look());
	String s = "";
	if (isLeaf()) return label;
	else {int n = children.size();
	      s = "(";
	      for (int i = 0;i < n-1;i++) s = s + children.elementAt(i) + ",";
	      s = s + children.elementAt(n-1) + ")" + label; //divergenceDate;
	      return s;}
    }
}
