package cluster;

import java.util.ArrayList;
import java.util.BitSet;
import misc.ExtToolsDriver;

public class SplitTreeNode implements Comparable<SplitTreeNode>{
	
	public float v;
	public float sse;
	public BitSet els;
	public SplitTreeNode p;
	
	// If this node is split according to vantage point
	// graph node, all elements at distance<=dist go
	// to the left child and the rest go to the right one
	public int vantagepoint = -1;
	public float dist = -1;
	public SplitTreeNode lc;
	public SplitTreeNode rc;
	
	public SplitTreeNode(BitSet _els, SplitTreeNode _p, float _v, float _sse) {
		els = _els;
		p = _p;
		v = _v;
		sse = _sse;
	}
	
	public void pruneSubtree() {
		if (null!=lc) lc.pruneSubtree();
		if (null!=rc) rc.pruneSubtree();
		lc=null;
		rc=null;
	}
	
	@Override 
	public boolean equals(Object aThat) {
		if ( this == aThat ) return true;
		if ( !(aThat instanceof SplitTreeNode) ) return false;
		return els.equals(((SplitTreeNode)aThat).els);
	}
	
	@Override
	public int compareTo(SplitTreeNode o) {
		if (((Float)this.sse).equals(o.sse)) {
			return ((Integer)this.hashCode()).compareTo(o.hashCode());
		} else {
			return ((Float)this.sse).compareTo(o.sse);
		}
	}
	
	public void getAllNotSplitDecendants(ArrayList<SplitTreeNode> nsdecend) {
		if (null==lc) {
			nsdecend.add(this);
		} else {
			lc.getAllNotSplitDecendants(nsdecend);
			rc.getAllNotSplitDecendants(nsdecend);
		}
	}
	
	public int numSplitDecendants() {
		if(lc == null) return 0;
		else return 1 + lc.numSplitDecendants() + rc.numSplitDecendants();
	}
	
	public void getAllSplitDecendants(ArrayList<SplitTreeNode> sdecend) {
		if (null!=lc) {
			sdecend.add(this);
			lc.getAllSplitDecendants(sdecend);
			rc.getAllSplitDecendants(sdecend);
		}	
	}
	
	public void getAllDescendants(ArrayList<SplitTreeNode> decend) {
		decend.add(this);
		if (null!=lc) {
			lc.getAllSplitDecendants(decend);
			rc.getAllSplitDecendants(decend);
		}
	}
	
	public float aggregateSSE() {
		if(lc == null) return sse;
		else return lc.aggregateSSE() + rc.aggregateSSE();
	}
	
	public String getPartitions() {
		if (lc == null) return v + ":" + els.toString() + "\n";
		else return lc.getPartitions() + "" + rc.getPartitions();
	}
	
	// Visualization
	
	public void visualize() {
		visualize("/dev/shm/graph");
	}
	
	public void visualize(String fn) {
		ExtToolsDriver.saveGraphViz(toGraphViz(), fn);
		ExtToolsDriver.showGraphViz(fn + ".png");
	}
	
	public void save(String fn) {
		ExtToolsDriver.saveGraphViz(toGraphViz(), fn);
	}
	
	public String toString() {
		if (null == lc) {
			return "\"" + String.format("%.2f", v) + 
					"[" + String.format("%.2f", sse) + "]" + 
					Common.toCompressedString(els,els.size()) + "\"";
		} else {
			return " \"" + vantagepoint + " " + els.hashCode() + "\"";
		}
	}
	
	public String toGraphViz() {
		// HELLO->World [color = red, label = \"aaa\"]; HELLO [style=filled, color = blue]
		return "digraph G { " + nodeSubtreeToGraphViz() + "}";
	}
	
	public String nodeSubtreeToGraphViz() {
		String res = toString() + ((null==lc)?"[fontcolor = grey"+ ((50+((int)v))%100) + 
				" , shape = box, style=filled, fillcolor = grey"+ ((int)v) + " ]":"") + 
				";\n";
		if (null != p) { // no splits for the whole graph
			String label = "";
			if (p.lc.toString().equals(toString())) label = String.format("<=%.0f", p.dist);
			else label = String.format(">%.0f", p.dist);
			res += p.toString() + "->" + toString() + "[ label = \"" + label + "\"] ;\n";
		}
		if (lc !=null) {
			return res + lc.nodeSubtreeToGraphViz() + rc.nodeSubtreeToGraphViz();
		} else {
			return res;
		}
	}
	
	
	
	public static void main(String[] args) {
		SplitTreeNode spn = new SplitTreeNode(null, null, 0.1F, 0.1F);
		spn.visualize();
		//ExtToolsDriver.kill("eog");
		System.err.print("done");
	}

	

	


}