package org.limewire.mojito.visual;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.limewire.mojito.KUID;
import org.limewire.mojito.io.MessageDispatcher.MessageDispatcherEvent.EventType;
import org.limewire.mojito.messages.DHTMessage.OpCode;
/**
* This looks a bit like a 2D Plasma Lamp (also known as
* Plasma Ball).
*/
class PlasmaLamp extends Painter {
private static final long ATTACK = 250L;
private static final long RELEASE = 2750L;
private static final long DURATION = ATTACK + RELEASE;
private static final float DOT_SIZE = 6f;
private static final Random GENERATOR = new Random();
private final List<Node> nodes = new LinkedList<Node>();
private final Point2D.Double localhost = new Point2D.Double();
private final Ellipse2D.Double ellipse = new Ellipse2D.Double();
private final Ellipse2D.Double dot = new Ellipse2D.Double();
private final KUID nodeId;
public PlasmaLamp(KUID nodeId) {
this.nodeId = nodeId;
}
@Override
public void paint(Component c, Graphics2D g2) {
double width = c.getWidth();
double height = c.getHeight();
double gap = 50d;
double radius = Math.max(Math.min(width/2d, height/2d) - gap, gap);
double arc_x = width/2d-radius;
double arc_y = height/2d-radius;
double arc_width = 2d*radius;
double arc_height = 2d*radius;
g2.setColor(Color.orange);
g2.setStroke(TWO_PIXEL_STROKE);
ellipse.setFrame(arc_x, arc_y, arc_width, arc_height);
g2.draw(ellipse);
double fi = position(nodeId, 2d*Math.PI) - Math.PI/2d;
double dx = width/2d + radius * Math.cos(fi);
double dy = height/2d + radius * Math.sin(fi);
localhost.setLocation(dx, dy);
dot.setFrame(dx - DOT_SIZE/2d, dy - DOT_SIZE/2d,
DOT_SIZE, DOT_SIZE);
synchronized (nodes) {
for (Iterator<Node> it = nodes.iterator(); it.hasNext(); ) {
if (it.next().paint(localhost, width, height, radius, g2)) {
it.remove();
}
}
}
g2.setColor(Color.orange);
g2.setStroke(DEFAULT_STROKE);
g2.fill(dot);
}
@Override
public void handle(EventType type, KUID nodeId, SocketAddress dst, OpCode opcode, boolean request) {
if (nodeId == null) {
return;
}
synchronized (nodes) {
nodes.add(new Node(dot, type, nodeId, opcode, request));
}
}
@Override
public void clear() {
synchronized (nodes) {
nodes.clear();
}
}
private static class Node {
private final Ellipse2D.Double dot;
private final EventType type;
private final KUID nodeId;
private final boolean request;
private final int noise;
private final long timeStamp = System.currentTimeMillis();
private final Point2D.Double remote = new Point2D.Double();
private final Point2D.Double point = new Point2D.Double();
private final QuadCurve2D.Double curve = new QuadCurve2D.Double();
private final Ellipse2D.Double circle = new Ellipse2D.Double();
private final Ellipse2D.Double prxDot = new Ellipse2D.Double();
private final Stroke stroke;
public Node(Ellipse2D.Double dot, EventType type, KUID nodeId, OpCode opcode, boolean request) {
this.dot = dot;
this.type = type;
this.nodeId = nodeId;
this.request = request;
this.stroke = getStrokeForOpCode(opcode);
int noise = GENERATOR.nextInt(50);
if (GENERATOR.nextBoolean()) {
noise = -noise;
}
this.noise = noise;
}
private int alpha() {
long delta = System.currentTimeMillis() - timeStamp;
if (delta < ATTACK) {
return (int)(255f/ATTACK * delta);
}
return Math.max(255 - (int)(255f/DURATION * delta), 0);
}
private double radius() {
final double r = 20d;
long delta = System.currentTimeMillis() - timeStamp;
if (delta < DURATION) {
return r/DURATION * delta;
}
return r;
}
public boolean paint(Point2D.Double localhost,
double width, double height, double radius, Graphics2D g2) {
double cx = width/2d;
double cy = height/2d;
double fi = position(nodeId, 2d*Math.PI) - Math.PI/2d;
double dx = cx + radius * Math.cos(fi);
double dy = cy + radius * Math.sin(fi);
int red = 0;
int green = 0;
int blue = 0;
if (type.equals(EventType.MESSAGE_SENT)) {
red = 255;
if (!request) {
blue = 255;
}
} else {
green = 255;
if (request) {
blue = 255;
}
}
remote.setLocation(dx, dy);
Point2D.Double corner = new Point2D.Double(
localhost.x + 3 * dot.width, localhost.y + 3 * dot.height);
this.prxDot.setFrameFromCenter(localhost, corner);
Shape shape = null;
if (!prxDot.contains(remote)) {
point.setLocation(cx+noise, cy+noise);
curve.setCurve(localhost, point, remote);
shape = curve;
} else {
double r = radius();
point.setLocation(localhost.x+r, localhost.y+r);
circle.setFrameFromCenter(localhost, point);
shape = circle;
}
if (shape != null) {
g2.setStroke(stroke);
g2.setColor(new Color(red, green, blue, alpha()));
g2.draw(shape);
}
//g2.setStroke(ONE_PIXEL_STROKE);
//g2.setColor(Color.red);
//g2.draw(prxDot);
return System.currentTimeMillis() - timeStamp >= DURATION;
}
}
}