package org.limewire.mojito.visual.graph;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.SwingUtilities;
import org.limewire.mojito.routing.Bucket;
import org.limewire.mojito.routing.RouteTable;
import org.limewire.mojito.routing.RouteTable.RouteTableEvent;
import org.limewire.mojito.routing.RouteTable.RouteTableEvent.EventType;
import org.limewire.mojito.visual.RouteTableGraphCallback;
import org.limewire.mojito.visual.components.BinaryEdge;
import org.limewire.mojito.visual.components.BucketVertex;
import org.limewire.mojito.visual.components.InteriorNodeVertex;
import org.limewire.mojito.visual.components.BinaryEdge.EdgeType;
import edu.uci.ics.jung.graph.ArchetypeVertex;
import edu.uci.ics.jung.graph.Vertex;
public class BucketGraph extends RouteTableGraph {
public BucketGraph(RouteTable routeTable, RouteTableGraphCallback callback) {
super(routeTable, callback);
List<Bucket> buckets = new ArrayList<Bucket>(routeTable.getBuckets());
//create new sparse tree graph with one bucket
root = new BucketVertex(buckets.get(0), true);
tree = new RootableSparseTree(root);
}
@Override
public void populateGraph() {
List<Bucket> buckets = new ArrayList<Bucket>(routeTable.getBuckets());
int count = buckets.size();
if(count < 2) {
root = new BucketVertex(buckets.get(0), true);
tree.newRoot(root);
return;
} else {
root = new InteriorNodeVertex();
tree.newRoot(root);
}
//TODO: optimization -- or not?
//BucketUtils.sortByDepth(buckets);
Bucket currentBucket;
for(Iterator<Bucket> it = buckets.iterator(); it.hasNext();) {
currentBucket = it.next();
updateGraphBucket(currentBucket);
it.remove();
}
}
@Override
public String getGraphInfo() {
String rtString = routeTable.toString();
return rtString.substring(rtString.indexOf("Total"));
}
private void updateGraphBucket(Bucket bucket) {
InteriorNodeVertex InteriorNode = getVertexForBucket(bucket);
//now add the bucket
if(bucket.getBucketID().isBitSet(bucket.getDepth()-1)) {
createBucketVertex(bucket, InteriorNode, EdgeType.RIGHT);
} else {
createBucketVertex(bucket, InteriorNode, EdgeType.LEFT);
}
}
private InteriorNodeVertex splitBucket(BucketVertex vertex, EdgeType type) {
InteriorNodeVertex predecessor = removeRouteTableVertex(vertex);
return createInteriorNode(predecessor, type);
}
private InteriorNodeVertex getVertexForBucket(Bucket bucket) {
int depth = bucket.getDepth();
InteriorNodeVertex vertex = (InteriorNodeVertex)root;
Vertex child;
EdgeType type;
for(int i=1; i < depth ; i++) {
child = null;
if(bucket.getBucketID().isBitSet(i-1)) {
child = vertex.getRightChild();
type = EdgeType.RIGHT;
} else {
child = vertex.getLeftChild();
type = EdgeType.LEFT;
}
if(child == null) {
vertex = createInteriorNode(vertex, type);
} else if(child instanceof BucketVertex) {
//we have found a bucket along this bucket path
//--> split it in order to be able to insert new bucket
BucketVertex bv = (BucketVertex)child;
vertex = splitBucket(bv, type);
} else {
vertex = (InteriorNodeVertex)child;
}
}
return vertex;
}
private BucketVertex createBucketVertex(Bucket bucket, InteriorNodeVertex predecessor, EdgeType type) {
boolean isLocalBucket = bucket.contains(routeTable.getLocalNode().getNodeID());
BucketVertex bv = new BucketVertex(bucket, isLocalBucket);
tree.addVertex(bv);
tree.addEdge(new BinaryEdge(predecessor, bv, type));
return bv;
}
@Override
public String getLabelForVertex(ArchetypeVertex v) {
if(v instanceof BucketVertex) {
return v.toString()+"("+((BucketVertex)v).getNode().getActiveSize()+")";
}
else return "";
}
public void handleRouteTableEvent(final RouteTableEvent event) {
if (event.getEventType().equals(EventType.ADD_ACTIVE_CONTACT)) {
callback.handleGraphInfoUpdated();
} else if (event.getEventType().equals(EventType.SPLIT_BUCKET)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Bucket left = event.getLeftBucket();
Bucket right = event.getRightBucket();
//are we splitting the root bucket
if(left.getDepth() == 1) {
root = new InteriorNodeVertex();
tree.newRoot(root);
}
updateGraphBucket(left);
updateGraphBucket(right);
callback.handleGraphLayoutUpdated();
}
});
}
}
}