public class DataController implements ActionListener {
	DataFeeder dataFeeder;
	Timer timer;
	final List<DataControllerListener> listeners;
	final List<PacketFilter> filters;
	final List<Packet> allPackets;
	final List<Packet> filteredPackets;
	private int noUpdated = 0;
	protected int intervalsComplete = 0;
	JPanel filterPanel;

	/**
	 * @param dataFeeder
	 * @param updateInterval
	 *            The time interval at which the data is updating.
	 */
	public DataController(DataFeeder dataFeeder, int updateInterval) {
		this.dataFeeder = dataFeeder;
		listeners = new ArrayList<DataControllerListener>();
		filters = new ArrayList<PacketFilter>();
		filteredPackets = new ArrayList<Packet>();
		allPackets = new ArrayList<Packet>();
		timer = new Timer(updateInterval, this);
		filterPanel = new JPanel();
		NormaliseFactory.INSTANCE.setDataController(this);
		filterPanel.setLayout(new BoxLayout(filterPanel, BoxLayout.Y_AXIS));
		filterPanel.add(Box.createVerticalGlue());
		timer.start();
	}

	public void FinishEverything() {
		this.timer.stop();
		for (DataControllerListener l : listeners)
			l.everythingEnds();
	}

	public void addListener(DataControllerListener listener) {
		listeners.add(listener);
	}

	public void removeListener(DataControllerListener listener) {
		listeners.remove(listener);
	}

	public void addFilter(PacketFilter packetFilter) {
		filters.add(packetFilter);
		if (packetFilter instanceof FixedFilter) {
			JComponent comp = packetFilter.getPanel();
			comp.setAlignmentX(java.awt.Component.LEFT_ALIGNMENT);
			filterPanel.add(comp);
		}
		allDataChanged();
	}

	public void removeFilter(PacketFilter packetFilter) {
		filters.remove(packetFilter);
		if (packetFilter instanceof FixedFilter) {
			filterPanel.remove(packetFilter.getPanel());
		}
		allDataChanged();
	}

	public Iterator<PacketFilter> filterIterator() {
		return filters.iterator();
	}

	/**
	 * Returns all the packets with the filters applied
	 */
	public List<Packet> getPackets() {
		return Collections.unmodifiableList(filteredPackets);
	}

	/**
	 * The action of the timer.
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		List<Packet> newPackets = dataFeeder.getNewPackets();

		if (newPackets != null) {
			allPackets.addAll(newPackets); // First add the new packets to the
											// controller
			intervalsComplete++;

			applyFilters(newPackets); // Then apply the filters to them
			filteredPackets.addAll(newPackets);

			for (DataControllerListener l : listeners)
				l.newPacketsArrived(newPackets);
		}

		// set timer interval to the one suggested by the feeder
		timer.setDelay(dataFeeder.updateInterval());

		// If we've reached the end of the capture, stop the timer
		if (!dataFeeder.hasNext())
			timer.stop();
	}

	// Redraw only when all the filters have applied their changes
	public void filterUpdated() {
		noUpdated++;
		if (noUpdated == filters.size()) {
			allDataChanged();
			noUpdated = 0;
		}
	}

	/**
	 * 
	 * @param list
	 *            List to be filtered
	 */
	private void applyFilters(List<Packet> list) {
		List<Packet> toBeRemoved = new ArrayList<Packet>();
		for (PacketFilter f : filters)
			for (Packet p : list)
				if (!f.filter(p))
					toBeRemoved.add(p);
		list.removeAll(toBeRemoved);
	}

	/**
	 * Informs listeners that all the data has changed.
	 */
	private void allDataChanged() {
		filteredPackets.clear();
		filteredPackets.addAll(allPackets);
		applyFilters(filteredPackets);

		for (DataControllerListener l : listeners)
			l.allDataChanged(filteredPackets, timer.getDelay(), intervalsComplete);

		timer.start();
	}

	/**
	 * Sets a new data feeder and resets the state of the controller and
	 * listeners
	 * 
	 * @param newDataFeeder
	 *            New data feeder
	 */
	public void setDataFeeder(DataFeeder newDataFeeder) {
		this.dataFeeder = newDataFeeder;
		this.allPackets.clear();
		allDataChanged();
	}

	public DataFeeder getDataFeeder() {
		return dataFeeder;
	}

	public JPanel fixedFiltersPanel() {
		return filterPanel;
	}

} public class Packet {
	public final int no, sport, dport, length;
	public final double time;
	public final String smac, dmac, info, protocol, sip, dip;

	/**
	 * 
	 * @param no
	 *            Packet number
	 * @param time
	 *            Time elapsed since first packet (seconds)
	 * @param sip
	 *            Source IPv4/6 address
	 * @param smac
	 *            Source hardware (MAC) address
	 * @param sport
	 *            Source Port
	 * @param dip
	 *            Destination IPv4/6 address
	 * @param dmac
	 *            Destination hardware (MAC) address
	 * @param dport
	 *            Destination Port
	 * @param protocol
	 *            Communication protocol
	 * @param length
	 *            Packet Length(Bytes)
	 * @param info
	 *            Detected description of packet purpose
	 */
	public Packet(int no, double time, String sip, String smac, int sport, String dip, String dmac,
			int dport, String protocol, int length, String info) {
		this.no = no;
		this.time = time;
		this.sip = sip;
		this.smac = smac;
		this.sport = sport;
		this.dip = dip;
		this.dmac = dmac;
		this.dport = dport;
		this.protocol = protocol;
		this.length = length;
		this.info = info;
	}

}
 public class OpenGLPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	private Visualisation currentVis;

	public OpenGLPanel() {
		currentVis = null;
	}

	public void redraw() {
		currentVis.display();
	}

	public void setVis(Visualisation vis) {
		if (currentVis != null)
			this.remove(currentVis);
		currentVis = vis;
		this.add(vis);
		resizeVisualisation();
	}

	public Visualisation getVis() {
		return currentVis;
	}

	public void resizeVisualisation() {
		currentVis.setPreferredSize(getSize());
		currentVis.setSize(getSize());
	}
} public class VisControlsContainer extends JPanel {
	JPanel lastCtrl;

	public VisControlsContainer() {
		super();
		lastCtrl = null;
	}

	private static final long serialVersionUID = 1L;

	public void setVisControl(JPanel newPanel) {
		if (lastCtrl != null)
			this.remove(lastCtrl);

		if (newPanel != null)
			this.add(newPanel);
		lastCtrl = newPanel;
		this.repaint();
	}

}
 public abstract class Visualisation extends GLJPanel implements DataControllerListener,
		GLEventListener {

	private static final long serialVersionUID = 1L;
	final List<Packet> listOfPackets;
	List<Packet> newPackets;
	final OpenGLPanel joglPanel;
	final DataController dataController;

	FPSAnimator fpskeep;

	boolean allDataChanged;
	JPanel visControls;
	final VisControlsContainer visContainer;

	public Visualisation(DataController dataController, OpenGLPanel joglPanel,
			VisControlsContainer visControlsContainer) {

		super(CreateCapabilities());

		this.setPreferredSize(new Dimension(800, 500));
		this.setSize(800, 500);
		this.setFocusable(true);

		this.addGLEventListener(this);

		dataController.addListener(this);
		this.listOfPackets = dataController.getPackets();
		this.newPackets = new ArrayList<Packet>();
		this.dataController = dataController;
		this.joglPanel = joglPanel;
		this.visControls = this.createControls();
		this.visContainer = visControlsContainer;

		// Create the timer that will keep the FPS - don't start it yet
		this.fpskeep = new FPSAnimator(this, 600);
	}

	private static GLCapabilitiesImmutable CreateCapabilities() {
		GLCapabilities caps = new GLCapabilities(GLProfile.getDefault());
		//caps.setSampleBuffers (true);
		//caps.setNumSamples (2);

		return caps;
	}

	protected abstract JPanel createControls();

	@Override
	public void allDataChanged(List<Packet> allPackets, int updateInterval, int intervalsComplete) {
		if (joglPanel.getVis() == this) {
			this.newPackets = allPackets;
			this.allDataChanged = true;
			//this.display();
		}
	}

	@Override
	public void newPacketsArrived(List<Packet> newPackets) {
		if (joglPanel.getVis() == this) {
			this.newPackets = newPackets;
		}
	}

	public void activate() {
		joglPanel.setVis(this);
		visContainer.setVisControl(visControls);
		this.newPackets = this.listOfPackets;
		allDataChanged = true;

		// Start the FPSAnimator to keep the framerate around 120
		System.setProperty("sun.awt.noerasebackground", "true");

		// this.fpskeep.add(this);
		this.fpskeep.start();
	}

	public void deactivate() {
		this.fpskeep.stop();
	}

	protected void render() {
		this.display();
		allDataChanged = false;
	}

	public abstract String getName();

	public abstract String getDescription();

	public void everythingEnds() {
		this.destroy();
		this.fpskeep.stop();
	}

	public void setState(int insideI) {}
} public class FramebufferPool {
	// Singleton pattern version II - everything is static
	private FramebufferPool() {
	};

	protected static List<Framebuffer> buffers;

	static {
		buffers = new ArrayList<Framebuffer>();
	}

	public static int Generate() {
		Framebuffer fb = new Framebuffer(400);

		buffers.add(fb);
		return buffers.size() - 1;
	}

	public static Framebuffer get(int fbid) {
		if (fbid < buffers.size())
			return buffers.get(fbid);

		return null;
	}

	public static void DiscardAll() {
		for (Framebuffer i : buffers) {
			i.Discard();
		}
	}

	public static void RegenerateAll(GL2 gl) {
		for (Framebuffer i : buffers) {
			i.Create(gl);
		}
	}
}
 public abstract class Node {

	private String name = null;
	public String GetName() {return name;}
	public void   SetName(String n) {name = n;}
	
	public Node(String nam) {
		name = nam;
	}

	public void Draw(int base, NodePainter painter, GL2 gl) {
		painter.DrawNode(base, this, gl);
	}
	
	Node parent;
	public Node GetParent() {
		return parent;
	}
	public void SetParent (Node par) {
		parent = par;
	}
	
	// Defaultly a node is a singular hexagon
	public int getDimenstion () {return 1;}
	public int getCapacity   () {return 1;}
	
	public Node GetNode (String name) {return null;}
	public void DetachNode (Node n) {};
	
	
	public Node GetClickedNode (int base, Position deltacoord) {
		if (deltacoord.x == 0 && deltacoord.y == 0)
			return this;
		else
			return null;
	}
	
	public boolean AddNode (String name, Node n) {return false;}

	public abstract void UpdateWithData (Packet pp);

	public abstract void UpdateAnimation(long time);

	public abstract int Priority();

	public abstract void MouseClick (MouseEvent e);
}
 public class Position {
	public Position(int posx, int posy) {
		x = posx;
		y = posy;
	}

	public Position(double posx, double posy) {
		x = (int) Math.round(posx);
		y = (int) Math.round(posy);
	}

	public int x;
	public int y;

	@Override
	public int hashCode() {
		return 1024 * 1024 * x + y;
	}

	@Override
	public boolean equals(Object t) {
		if (t == null)
			return false;

		Position that = (Position) t;
		return (this.x == that.x) && (this.y == that.y);
	}
}
 public class TextRendererPool {
	// Singleton pattern version II - everything is static
	private TextRendererPool() {
	};

	protected static HashMap<String, Font> fonts;
	protected static HashMap<String, TextRenderer> renderers;

	static {
		renderers = new HashMap<String, TextRenderer>();
		fonts = new HashMap<String, Font>();

		LoadFont("default", 15, Painter.class.getResource("Ubuntu-L.ttf"));
		LoadFont("basic", 70, Painter.class.getResource("cmr17.ttf"));
	}

	public static void LoadFont(String name, int size, URL resource) {

		Font font;

		try {
			font = Font.createFont(Font.TRUETYPE_FONT, resource.openStream());

			// Scale it up
			font = font.deriveFont((float) size);
		} catch (FontFormatException | IOException e) {

			// Failsafe font
			font = new Font("SansSerif", Font.BOLD, size);
		}

		TextRenderer renderer = new TextRenderer(font, true, true,
				new TextRenderer.DefaultRenderDelegate(), true);

		fonts.put(name, font);
		renderers.put(name, renderer);
	}

	public static void Recreate() {
		for (String name : renderers.keySet()) {
			TextRenderer renderer = new TextRenderer(fonts.get(name), true, true,
					new TextRenderer.DefaultRenderDelegate(), true);
			renderers.put(name, renderer);
		}
	}

	public static TextRenderer get(String fontName) {
		return renderers.get(fontName);
	}

	public static Font getFont(String fontName) {
		return fonts.get(fontName);
	}

	public static void StartRend(String name, GL2 gl) {
		TextRenderer renderer = renderers.get(name);
		renderer.begin3DRendering();
		renderer.setSmoothing(true);
		renderer.setUseVertexArrays(true);
	}

	public static void FinishRend(String name, GL2 gl) {
		TextRenderer renderer = renderers.get(name);
		renderer.end3DRendering();
		renderer.flush();
	}

}
 public class TexturePool {

	// Singleton pattern version II - everything is static
	private TexturePool() {
	};

	protected static HashMap<String, Texture> textures;

	static {
		textures = new HashMap<String, Texture>();
	}

	public static void LoadTexture(String name, URL resource) {
		textures.put(name, new Texture(resource));
	}

	public static void DiscardTextures() {
		for (Texture t : textures.values()) {
			t.Discard();
		}
	}

	public static Texture get(String textureName) {
		return textures.get(textureName);
	}

	public static void Rebind(GL2 gl) {

		// Rebind the textures
		for (Texture t : textures.values()) {
			t.Rebind(gl);
		}
	}
}
 public class ValueAnimator {

	final int LINEAR = 1;
	final int CUBIC = 2;
	final int SINE = 3;

	// Animation style
	protected int style;

	protected double value;

	protected double start;
	protected double goal;
	protected double finalGoal;

	public double getGoal() {
		return finalGoal;
	};

	Queue<Double> pendingGoals;
	Queue<Long> pendingDurations;

	protected long duration;
	protected long timeleft;
	protected long laststamp;

	Timer anim;

	public ValueAnimator(double v) {
		style = SINE;

		value = v;
		start = v;
		goal = v;
		finalGoal = v;

		pendingGoals = new ConcurrentLinkedQueue<Double>();
		pendingDurations = new ConcurrentLinkedQueue<Long>();

		ActionListener inner = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evnt) {
				// Find current stamp
				Date d = new Date();

				long timepassed = d.getTime() - laststamp;
				if (timepassed > timeleft)
					timepassed = timeleft;

				timeleft -= timepassed;
				laststamp = d.getTime();

				if (style == CUBIC)
					ValueAnimator.this.CubicProg();
				if (style == LINEAR)
					ValueAnimator.this.LinearProg();
				if (style == SINE)
					ValueAnimator.this.SineProg();

				if (timeleft <= 0) {
					value = goal;

					anim.stop();
					ValueAnimator.this.ContinueAnimation();
				}
			}
		};

		anim = new Timer(10, inner);
	}

	public void SetStyle(int st) {
		style = st;
	}

	public void MoveTo(double g, long d) {
		if (d == 0) {
			anim.stop();

			pendingGoals.clear();
			pendingDurations.clear();

			goal = g;
			finalGoal = g;
			value = g;
			return;
		}

		finalGoal = g;
		pendingGoals.add(g);
		pendingDurations.add(d);

		if (!anim.isRunning()) {
			ContinueAnimation();
		}
		;
	}

	private void ContinueAnimation() {
		if (pendingGoals.size() != 0) {
			double newgoal = pendingGoals.remove();
			long newduration = pendingDurations.remove();

			// Start is the current value
			start = value;
			goal = newgoal;

			duration = newduration;
			timeleft = newduration;
			laststamp = (new Date()).getTime();

			anim.start();
		}
	}

	public void LinearProg() {
		value = (goal * (duration - timeleft) + start * timeleft) / duration;
	}

	public void CubicProg() {
		// Slope at 0
		double slopeb = 3.0 / 2.0;
		double slopea = (-slopeb / 3.0);

		double x = 2.0 * (duration - timeleft) / duration - 1.0;
		double y = slopea * Math.pow(x, 3) + slopeb * x;

		value = start + (goal - start) * (y + 1.0) / 2;
	}

	public void SineProg() {
		double x = 2.0 * (duration - timeleft) / duration - 1.0;
		double y = Math.sin(Math.PI / 2.0 * x);

		value = start + (goal - start) * (y + 1.0) / 2;
	}

	public double toDouble() {
		return value;
	}
}
 public class VertexBufferPool {

	// Singleton pattern version II - everything is static
	private VertexBufferPool() {
	};

	protected static HashMap<String, VertexBuffer> vertexbuffers;

	static {
		vertexbuffers = new HashMap<String, VertexBuffer>();
	}

	public static void PutBuffer (String name, VertexBuffer resource) {
		vertexbuffers.put (name, resource);
	}

	public static void DiscardAll() {
		for (VertexBuffer t : vertexbuffers.values()) {
			t.Discard();
		}
	}

	public static VertexBuffer get(String buffername) {
		return vertexbuffers.get (buffername);
	}

	public static void Rebind(GL2 gl) {

		// Rebind the textures
		for (VertexBuffer t : vertexbuffers.values()) {
			t.Rebind(gl);
		}
	}
}
 public class MapHeat extends Map {

	public class NodeWithPosition {
		public NodeWithPosition(Node a, Position c, Position p) {
			node = a;
			coo = c;
			pos = p;
		}

		public Node node;
		public Position pos;
		public Position coo;
	}

	// IPs mapped to nodesByName
	HashMap<String, NodeWithPosition> nodesByName;

	HashMap<Position, NodeWithPosition> nodesByPosition;

	List<NodeWithPosition> nodesl;
	
	MapPainter painter;

	// Connections to be drawn
	HashMap<String, Connection> connections;

	Random rand;
	
	int gridsize;

	class NamedThreadFactory implements ThreadFactory {
		int i = 0;

		public Thread newThread(Runnable r) {
			return new Thread(r, "Node animating thread #" + (i++));
		}
	}

	// Animation of the nodesByName can be parallelised
	ExecutorService exe = new ThreadPoolExecutor(4, 8, 5000, TimeUnit.MILLISECONDS,
			new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory());

	public MapHeat(int w, int h) {
		width = w;
		height = h;

		rand = new Random();

		nodesByName = new HashMap<String, NodeWithPosition>();
		nodesByPosition = new HashMap<Position, NodeWithPosition>();
		nodesl = new ArrayList<NodeWithPosition>();
		
		painter = new MapPainter();
		
		gridsize = 160;
		Painter.GenerateGrid("secondgrid", gridsize);
	}

	public void DrawEverything(GL2 gl) {

		Painter.DrawGrid (base, gridsize, "secondgrid", gl);
		for (NodeWithPosition i : nodesByName.values()) {
			int x = i.pos.x;
			int y = i.pos.y;

			gl.glPushMatrix();
			// Transpose it to the right spot
			gl.glTranslated(x, y, 0.0);

			// Draw it
			i.node.Draw(base, painter, gl);
			gl.glPopMatrix();
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void StepAnimation(final long time) throws InterruptedException, ExecutionException {
		ArrayList<Future<?>> list = new ArrayList<Future<?>>();
		for (final NodeWithPosition i : nodesByName.values())
			list.add(exe.submit(new Callable() {
				@Override
				public Object call() throws Exception {
					i.node.UpdateAnimation(time);
					return null;
				}
			}));

		for (Future<?> i : list)
			i.get();
	}

	public void SetSize(int w, int h, GL2 gl) {
		width = w;
		height = h;
	}

	public void SuggestNode (String sip, String dip, List<Packet> packets) {
		// Suggests the existence of the node in the network to be displayed

		// Look whether the node already exists
		NodeWithPosition find = nodesByName.get(dip);

		if (find == null) {
			find = AddNode(sip, dip, "basic");
		}

		for (Packet pp : packets)
			find.node.UpdateWithData(pp);
	}

	public void SortNodes() {
		if (true)
			return;

		/*
		 * TODO This code is unreachable -- Is it still here so it can be used
		 * later? Yes, it most certainly is
		 * 
		 * Collections.sort(nodesl, new Comparator<NodeWithPosition>() {
		 * 
		 * @Override public int compare(NodeWithPosition n1, NodeWithPosition
		 * n2) { return n2.node.Priority() - n1.node.Priority(); }
		 * 
		 * });
		 * 
		 * // Now display them in the order for (int i = 0; i < nodesl.size();
		 * i++) { NodeWithPosition n = nodesl.get(i);
		 * 
		 * Position posit = FindPosition(i); Position coord =
		 * CoordinateByPosition(posit);
		 * 
		 * n.pos = posit; n.coo = coord; }
		 */
	}

	private Position FindPosition(int num) {
		double x = 0.0;
		double y = 0.0;

		int s = num + 1;
		int innerring = (int) Math.floor(Math.sqrt((s - 1 / 4.0) / 3.0) - 0.5);
		int outerring = innerring + 1;
		int k = innerring;
		int shift = s - 3 * (k * k + k) - 1;

		// Move to the desired ring
		if (outerring % 2 == 1)
			x += Math.sqrt(3) * base / 2.0;
		y += base * outerring * 1.5;

		// Move the shift times
		double angle = 0;

		for (int i = 0 - (outerring / 2); i < shift - (outerring / 2); i++) {
			if (i % outerring == 0) {
				angle -= Math.PI / 3;
			}
			x += Math.sqrt(3) * base * Math.cos(angle);
			y += Math.sqrt(3) * base * Math.sin(angle);
		}

		// System.out.println("Node #" + num + " is on the ring : " + outerring
		// + " with shift " + shift);

		return new Position(x, y);
	}

	private Position PositionByCoordinate(Position coor) {
		int coorx = coor.x;
		int coory = coor.y;

		double r3 = Math.sqrt(3.0);
		Position p = new Position(r3 * coorx * base, 1.5 * coory * base);

		// Odd rows are shifted
		if (coory % 2 != 0)
			p.x += r3 * base / 2.0;

		return p;
	}

	private Position CoordinateByPosition(Position pos) {
		int posx = pos.x;
		int posy = pos.y;

		double r3 = Math.sqrt(3.0);
		Position p = new Position(0, 0);

		p.y = (int) Math.round(posy / (base * 1.5));
		if (p.y % 2 != 0)
			p.x = (int) Math.round((posx - base * r3 / 2.0) / (base * r3));
		else
			p.x = (int) Math.round(posx / (base * r3));

		return p;
	}

	private NodeWithPosition AddNode(String near, String name, String textureName) {
		HeatNode front = new HeatNode  (textureName, name);
		GraphNode back = new GraphNode (name);

		// Make it into the flip node - the node that has two sides
		FlipNode lemur = new FlipNode (front, back);

		Position posit;
		Position coord;
		NodeWithPosition nearnode = nodesByName.get(near);
		
		// Look for the position around the specified coordinate
		int current = 0;
		while (true) {
			posit = FindPosition(current);
			coord = CoordinateByPosition(posit);
			if (nearnode != null)
			{
				coord.x += nearnode.coo.x;
				coord.y += nearnode.coo.y;
			}
			posit = PositionByCoordinate(coord);

			if (nodesByPosition.get(coord) != null)
				current++;
			else
				break;
		}
		
		coord = CoordinateByPosition(posit);	
		NodeWithPosition k = new NodeWithPosition(lemur, coord, posit);

		// System.out.println("Node " + name + " placed in coords : " + coord.x + ", " + coord.y);

		nodesByName.put(name, k);
		nodesl.add(k);
		nodesByPosition.put(coord, k);

		return k;
	}

	public Node FindClickedNode(int x, int y) {
		// Optimised version
		Position p = new Position(x, y);
		Position c = CoordinateByPosition(p);

		NodeWithPosition node = nodesByPosition.get(c);
		return node.node;
	}
	
	public Position FindClickedNodePos (int x, int y) {
		// Optimised version
		Position p = new Position(x, y);
		Position c = CoordinateByPosition(p);

		NodeWithPosition node = nodesByPosition.get(c);
		return node.pos;
	}
}

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import netvis.data.DataController;
import netvis.data.model.Packet;
import netvis.ui.OpenGLPanel;
import netvis.ui.VisControlsContainer;
import netvis.visualisations.Visualisation;
import netvis.visualisations.gameengine.FramebufferPool;
import netvis.visualisations.gameengine.Node;
import netvis.visualisations.gameengine.Position;
import netvis.visualisations.gameengine.TextRendererPool;
import netvis.visualisations.gameengine.TexturePool;
import netvis.visualisations.gameengine.ValueAnimator;
import netvis.visualisations.gameengine.VertexBufferPool;
import netvis.visualisations.maps.MapHeat;
				double fpsnum = Math.round(10000.0 * frameNum / (diff)) / 10.0;
				fps.setText(fpsnum + " FPS");

				oldTime = null;
			}
		}
		frameNum++;

		// if (true)
		// return;

		GL2 gl = drawable.getGL().getGL2();

		// Set the width and height to the actuall width and height in pixels,
		// (0, 0) is in the middle
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();

		gl.glOrtho(middlex.toDouble() - this.width * viewfield / 2, middlex.toDouble() + this.width
				* viewfield / 2, middley.toDouble() - this.height * viewfield / 2,
				middley.toDouble() + this.height * viewfield / 2, -1000, 2000);

		// Clear the board
		gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		gl.glClearDepth(3000.0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		gl.glShadeModel(GL2.GL_FLAT);

		// Depth things - probably unnecessary
		// gl.glEnable(GL.GL_DEPTH_TEST);
		// gl.glDepthFunc(GL2.GL_GEQUAL);

		// Use the typical blending options
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glEnable(GL.GL_BLEND);

		// glut.glutInitDisplayMode(GLUT. GLUT_DOUBLE | GLUT.GLUT_RGBA |
		// GLUT.GLUT_DEPTH | GLUT_MULTISAMPLE);

		// Try making rendering as nice as possible
		gl.glEnable(GL2.GL_MULTISAMPLE);
		gl.glShadeModel(GL2.GL_SMOOTH);
		gl.glEnable(GL2.GL_POLYGON_SMOOTH);
		gl.glEnable(GL2.GL_LINE_SMOOTH);
		gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
		gl.glHint(GL2.GL_POLYGON_SMOOTH_HINT, GL2.GL_NICEST);

		gl.glPushMatrix();
			//gl.glTranslated(0.0, 0.0, -1.0);
			// Make the map draw all of the elements
			currentMap.DrawEverything(gl);
		gl.glPopMatrix();
		
		//this.swapBuffers();
	}

	@Override
	public void newPacketsArrived(List<Packet> newPackets) {
		// Dispatch the data to the specific points in the Map

		for (Packet i : newPackets) {
			Candidate dri = candidates.get(i.sip);
			if (dri == null) {
				// Create the candidate to be displayed
				dri = new Candidate(0, i.length, i.sip, i.dip);
				candidates.put(i.sip, dri);
			}
			
			dri.RegisterPacket (i);
		}

		// Decide on which candidates should be displayed
		for (String ip : candidates.keySet()) {
			Candidate can = candidates.get(ip);

			// System.out.println("I'm considering IP: " + ip +
			// " which dataflow: " + can.datasize);
			if (can.datasize >= 2000) {
				// System.out.println("IP: " + ip + " which dataflow: " +
				// can.datasize + " added to the simulation");
				currentMap.SuggestNode(can.sip, can.dip, can.GetWaitingPackets());
				currentMap.SuggestNode(can.dip, can.sip, can.GetWaitingPackets());
				
				can.ResetWaitingPackets();
			}
		}
	}

	@Override
	public void dispose(GLAutoDrawable arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void init(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();

		gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		gl.glClearDepth(0.0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

	}

	@Override
	public void reshape(GLAutoDrawable drawable, int arg1, int arg2, int wi, int he) {
		GL2 gl = drawable.getGL().getGL2();

		if (wi != width || he != height) {
			width = wi;
			height = he;
			this.setSize(wi, he);
			this.setPreferredSize(new Dimension(wi, he));
		}

		currentMap.SetSize(width, height, gl);

		// Mark all the graphic card side object as broken
		TexturePool.DiscardTextures();
		TextRendererPool.Recreate();
		FramebufferPool.DiscardAll();