package net.dirtyfilthy.Bitten;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.awt.event.MouseEvent;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.text.html.HTMLDocument.Iterator;
import com.google.bitcoin.core.GraphAddress;
import com.google.bitcoin.core.GraphTransaction;
import com.google.bitcoin.core.GraphTransactionOutput;
import com.google.bitcoin.core.GraphWallet;
import com.google.bitcoin.core.TransactionOutput;
import com.google.bitcoin.core.Utils;
import com.google.bitcoin.core.WalletIdable;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.assignment.DataSizeAction;
import prefuse.action.layout.Layout;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.ControlAdapter;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.data.expression.Predicate;
import prefuse.data.expression.parser.ExpressionParser;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.FontLib;
import prefuse.util.PrefuseLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.ForceSimulator;
import prefuse.util.force.NBodyForce;
import prefuse.util.force.SpringForce;
import prefuse.util.ui.JForcePanel;
import prefuse.visual.DecoratorItem;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;
public class WalletView extends Display {
private String label = "label";
private Table walletNodeTable;
private Table walletEdgeTable;
private Graph graph;
private Visualization viz;
private ArrayList<Tuple> highlightedTuples;
private ForceDirectedLayout forceDirected;
private boolean showValues=false;
public ControlPanel panel; // tightly coupled but fuck it
private static final Schema DECORATOR_SCHEMA = PrefuseLib.getVisualItemSchema();
static {
DECORATOR_SCHEMA.setDefault(VisualItem.INTERACTIVE, false);
DECORATOR_SCHEMA.setDefault(VisualItem.TEXTCOLOR, ColorLib.rgb(0,255,0));
DECORATOR_SCHEMA.setDefault(VisualItem.FONT, FontLib.getFont("Tahoma",8));
DECORATOR_SCHEMA.setDefault(VisualItem.FILLCOLOR, ColorLib.rgb(0,0,0));
}
class LabelLayout2 extends Layout {
public LabelLayout2(String group) {
super(group);
}
public void run(double frac) {
java.util.Iterator iter = viz.items(m_group);
while ( iter.hasNext() ) {
DecoratorItem decorator = (DecoratorItem)iter.next();
VisualItem decoratedItem = decorator.getDecoratedItem();
Rectangle2D bounds = decoratedItem.getBounds();
double x = bounds.getCenterX();
double y = bounds.getCenterY();
/*
double x2 = 0, y2 = 0, x3 = 0, y3 = 0;
double m;
if (decoratedItem instanceof EdgeItem){
VisualItem dest = ((EdgeItem)decoratedItem).getTargetItem();
VisualItem src = ((EdgeItem)decoratedItem).getSourceItem();
x2 = dest.getX();
y2 = dest.getY();
y3 = -(x2-x);
x3 = (y2-y);
m=Math.sqrt(x3*x3+y3*y3);
x3=x3/m;
y3=y3/m;
x=x+(x3*10);
y=y+(y3*10);
}
*/
boolean isSelf=((EdgeItem)decoratedItem).getTargetItem().equals(((EdgeItem)decoratedItem).getSourceItem());
if(isSelf){
decoratedItem.setVisible(false);
}
if(showValues && !isSelf){
decorator.setVisible(true);
}
else{
decorator.setVisible(false);
}
setX(decorator, null, x);
setY(decorator, null, y);
}
}
} // end of inner class LabelLayout
WalletView() {
this.setBackground(new Color(0, 0, 0));
int[] palette = new int[] { ColorLib.rgb(255, 180, 180),
ColorLib.rgb(190, 190, 255), ColorLib.rgb(190, 190, 0) };
int coinbaseColor=ColorLib.rgb(0, 0, 255);
highlightedTuples = new ArrayList<Tuple>();
walletNodeTable = new Table();
walletEdgeTable = new Table();
walletNodeTable.addColumn("id", long.class);
walletNodeTable.addColumn("label", String.class);
walletNodeTable.addColumn("coinbase", boolean.class);
walletNodeTable.addColumn("labelled", boolean.class);
walletEdgeTable.addColumn("btc", long.class);
walletEdgeTable.addColumn("source", long.class);
walletEdgeTable.addColumn("target", long.class);
walletEdgeTable.addColumn("btc_d", double.class);
graph = new Graph(walletNodeTable, walletEdgeTable, true, "id",
"source", "target");
Node n1 = graph.addNode();
Node n2 = graph.addNode();
n1.setLong(0, 1);
n2.setLong(0, 2);
Edge e2=graph.addEdge(n1, n2);
viz = new Visualization();
viz.add("graph", graph);
viz.getVisualItem("graph", n1).setVisible(false);
viz.getVisualItem("graph", n2).setVisible(false);
viz.getVisualItem("graph", e2).setVisible(false);
// draw the "name" label for NodeItems
// viz.getVisualItem("graph", e2).setVisible(false);
LabelRenderer r = new LabelRenderer("label");
r.setRoundedCorner(8, 8); // round the corners
r.setHorizontalPadding(5);
EdgeRenderer e = new EdgeRenderer(Constants.EDGE_TYPE_CURVE);
e.setArrowType(Constants.EDGE_ARROW_FORWARD);
e.setArrowHeadSize(10, 10);
DefaultRendererFactory df = new DefaultRendererFactory(r, e);
viz.setRendererFactory(df);
LabelRenderer r2=new LabelRenderer("btc_d");
r2.setRoundedCorner(5, 5);
r2.setRenderType(LabelRenderer.RENDER_TYPE_DRAW_AND_FILL);
df.add("INGROUP('edgeDeco')", r2);
viz.addDecorators("edgeDeco", "graph.edges",DECORATOR_SCHEMA);
ActionList layout = new ActionList(Activity.INFINITY);
forceDirected = new ForceDirectedLayout("graph");
ForceSimulator fs=new ForceSimulator();
fs.addForce(new NBodyForce(-4.0f, -1, 0.9f));
fs.addForce(new SpringForce(SpringForce.DEFAULT_SPRING_COEFF,60.0f));
fs.addForce(new DragForce(0.02f));
// f.getForceSimulator().setIntegrator(new EulerIntegrator());
// forceDirected.getForceSimulator().addForce(new DragForce((float) 0.01));
// forceDirected.getForceSimulator().
forceDirected.setForceSimulator(fs);
layout.add(forceDirected);
// layout.add(new NodeLinkTreeLayout("graph"));
layout.add(new RepaintAction());
layout.add(new LabelLayout2("edgeDeco"));
ColorAction fill = new ColorAction("graph.nodes", VisualItem.FILLCOLOR,
ColorLib.rgb(40,40,40));
ColorAction text = new ColorAction("graph.nodes", VisualItem.TEXTCOLOR,
ColorLib.gray(0));
fill.add("labelled", ColorLib.rgb(255,0,0));
// use light grey for edges
ColorAction stroke = new ColorAction("graph.nodes",
VisualItem.STROKECOLOR, ColorLib.rgb(40, 230, 40));
// stroke.add("coinbase", coinbaseColor);
stroke.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 255, 0));
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
edges.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 255, 0));
ColorAction fill2 = new ColorAction("graph.edges",
VisualItem.FILLCOLOR, ColorLib.gray(200));
// fill2.add("coinbase", coinbaseColor);
fill2.add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 255, 0));
DataSizeAction edgeWidth = new DataSizeAction("graph.edges", "btc_d");
edgeWidth.setScale(Constants.LOG_SCALE);
edgeWidth.setMaximumSize(50);
ActionList color = new ActionList();
color.add(text);
color.add(new ColorAction("graph.edges",VisualItem.TEXTCOLOR,ColorLib.rgb(255,255,255)));
color.add(new ColorAction("graph.nodes",VisualItem.TEXTCOLOR,ColorLib.rgb(255,255,255)));
color.add(fill);
color.add(fill2);
color.add(stroke);
color.add(edges);
color.add(edgeWidth);
viz.putAction("layout", layout);
viz.putAction("color", color);
setVisualization(viz);
class NodeControl extends ControlAdapter {
public void itemClicked(VisualItem item, MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) return;
if ( e.getClickCount() == 2 ) {//DoubleClick
NodeItem ni = (NodeItem) item;
GraphWallet w=new GraphWallet(panel.getBlockStore().graph().getNodeById(ni.getLong("id")));
panel.showWallet(w);
}
}
}
addControlListener(new DragControl()); // drag items around
addControlListener(new PanControl()); // pan with background left-drag
addControlListener(new ZoomControl()); // zoom with vertical right-drag
addControlListener(new ZoomToFitControl()); // zoom with vertical right-drag
addControlListener(new NodeControl()); // zoom with vertical right-drag
registerKeyboardAction(new ActionListener () {
public void actionPerformed(ActionEvent e) {
JForcePanel.showForcePanel(forceDirected.getForceSimulator());
repaint();
}
}, "show force panel", KeyStroke.getKeyStroke("alt F"),
WHEN_IN_FOCUSED_WINDOW );
registerKeyboardAction(new ActionListener () {
public void actionPerformed(ActionEvent e) {
if(showValues){
showValues=false;
}
else{
showValues=true;
}
repaint();
}
}, "show edges", KeyStroke.getKeyStroke("alt S"),
WHEN_IN_FOCUSED_WINDOW );
this.setHighQuality(true);
viz.run("color");
// start up the animated layout
viz.run("layout");
}
public Node findOrCreateWalletNode(GraphWallet w) {
System.out.println("adding node " + w.node().getId());
Node n = graph.getNodeFromKey(w.node().getId());
if (n == null) {
n = graph.addNode();
}
n.setLong(0, w.node().getId());
n.setString(1, w.label());
if(w.equals(GraphWallet.coinbaseWallet(w.node().getGraphDatabase()))){
n.setBoolean(2,true);
}
else{
n.setBoolean(2,false);
}
n.setBoolean(3,w.isLabelled());
return n;
}
public void removeOutputEdge(GraphWallet source, GraphWallet target, long amount) {
Node sourceNode = graph.getNodeFromKey(source.node().getId());
System.out.println("source node " + sourceNode);
Node targetNode = graph.getNodeFromKey(target.node().getId());
if(sourceNode==null || targetNode == null){
return;
}
Edge edge = graph.getEdge(sourceNode, targetNode);
long amt = edge.getLong(0);
amt = amt - amount;
if (amt > 0) {
edge.setLong(0, amt);
edge.setDouble(3, Utils.btcToDouble(amt));
} else {
System.out.println("removing edge");
graph.removeEdge(edge);
}
if (!sourceNode.edges().hasNext()) {
graph.removeNode(sourceNode);
}
if (!targetNode.edges().hasNext()) {
graph.removeNode(targetNode);
}
}
public Edge addOutputEdge(GraphWallet source, GraphWallet target, long amount) {
Node sourceNode = graph.getNodeFromKey(source.node().getId());
System.out.println("source node " + sourceNode);
Node targetNode = graph.getNodeFromKey(target.node().getId());
Edge edge = graph.getEdge(sourceNode, targetNode);
if (edge == null) {
edge = graph.addEdge(sourceNode, targetNode);
edge.setLong(0, amount);
edge.setDouble(3, Utils.btcToDouble(amount));
} else {
edge.setDouble(3, Utils.btcToDouble(edge.getLong(0) + amount));
edge.setLong(0, edge.getLong(0) + amount);
}
return edge;
}
public void removeTransaction(GraphTransaction transaction) {
synchronized (viz) {
GraphWallet source;
if (transaction.inputs.get(0).isCoinBase()) {
return;
//source=GraphWallet.coinbaseWallet(transaction.node().getGraphDatabase());
}
else{
source = transaction.inputs.get(0).address().wallet();
}
ArrayList<Long> targetKeys = new ArrayList<Long>();
for (GraphTransactionOutput o : transaction.outputs) {
GraphWallet target = o.address().wallet();
removeOutputEdge(source, target, o.getValue().longValue());
}
}
viz.run("color");
viz.run("layout");
}
public void addTransaction(GraphTransaction transaction) {
Node srcNode;
synchronized (viz) {
GraphWallet source;
if (transaction.inputs.get(0).isCoinBase()) {
// source = GraphWallet.coinbaseWallet(transaction.node().getGraphDatabase());
return;
}
else{
source = transaction.inputs.get(0).address().wallet();
}
ArrayList<Long> targetKeys = new ArrayList<Long>();
srcNode = findOrCreateWalletNode(source);
for (GraphTransactionOutput o : transaction.outputs) {
GraphWallet target = o.address().wallet();
findOrCreateWalletNode(target);
addOutputEdge(source, target, o.getValue().longValue());
highlightNodes(source,target);
}
}
viz.run("layout");
viz.run("color");
}
public void panToNodeId(long id){
Node n=graph.getNodeFromKey(id);
panToNode(n);
}
public void panToNode(Node n){
VisualItem vi = viz.getVisualItem("graph", n);
Rectangle2D bounds=vi.getBounds();
this.animatePanToAbs(
new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()), 500);
}
public void panToAddress(GraphAddress a){
panToNodeId(a.wallet().node().getId());
}
public void clearHighlights() {
for (Tuple t : highlightedTuples) {
try {
VisualItem vi = viz.getVisualItem("graph", t);
vi.setHighlighted(false);
} catch (NullPointerException e) {
continue;
}
}
highlightedTuples.clear();
}
public void highlightTuple(Tuple t) {
highlightedTuples.add(t);
viz.getVisualItem("graph", t).setHighlighted(true);
}
public void highlightNodes(GraphWallet source, GraphWallet target) {
Node sourceNode = graph.getNodeFromKey(source.node().getId());
Node targetNode = graph.getNodeFromKey(target.node().getId());
if (targetNode == null || sourceNode == null) {
viz.run("color");
return;
}
System.out.println("setting highlight");
highlightTuple(sourceNode);
highlightTuple(targetNode);
Edge e = graph.getEdge(sourceNode, targetNode);
if (e != null) {
highlightTuple(e);
}
viz.run("color");
}
public void panToWallet(WalletIdable target) {
panToNodeId(target.getWalletId());
}
}