public class ActivityVisualisation extends Visualisation {

	int width;
	int height;

	Date oldTime;
	int frameNum;
	JLabel fps;

	Timer animator, cleaner;

	MapActivity map;

	HashMap<String, Candidate> candidates;

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

		super(dataController, joglPanel, visControlsContainer);

		width = joglPanel.getWidth();
		height = joglPanel.getHeight();

		candidates = new HashMap<String, Candidate>();

		map = new MapActivity(width, height);
		new MapController (map, this);

		ActionListener animatum = new ActionListener() {
			long lasttime = (new Date()).getTime();

			public void actionPerformed(ActionEvent evnt) {
				// Animate the entities!
				long newtime = (new Date()).getTime();

				try {
					map.StepAnimation(newtime - lasttime);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				lasttime = newtime;
			}
		};

		animator = new Timer(25, animatum);
		animator.start();

		ActionListener clearing = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent evnt) {
				// If enough time has passed reset the candidates -- > TODO
				candidates.clear();
			}
		};

		cleaner = new Timer(2000, clearing);
		cleaner.start();

		// Add test nodes
		/*
		 * for (int i = 0; i < 0; i++) { currentMap.SuggestNode ("testk" + i,
		 * "test" + i); }
		 * 
		 * for (int i = 0; i < 0; i++) { currentMap.SuggestNode ("testa" + i,
		 * "ServerA"); }
		 */

		/*
		 * List<Packet> listOfPackets = new ArrayList<Packet> (); for (int i =
		 * 0; i < 48; i++) { currentMap.SuggestNode ("testb" + i, "ServerB",
		 * listOfPackets ); }
		 * 
		 * currentMap.SuggestNode("testbTOOMUCH", "ServerB", listOfPackets);
		 */
	}

	private static final long serialVersionUID = 1L;

	@Override
	public void display(GLAutoDrawable drawable) {
		double viewfield = map.GetViewfield ();
		double mx = map.GetMX ();
		double my = map.GetMY();

		// Count the FPS
		if (oldTime == null) {
			frameNum = 0;
			oldTime = new Date();
		} else {
			Date now = new Date();
			long diff = now.getTime() - oldTime.getTime();
			if (diff > 2000) {
				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(mx - this.width * viewfield / 2, mx + this.width
				* viewfield / 2, my - this.height * viewfield / 2,
				my + 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();
			//Painter.StressTestList (currentMap.base, gl);
			map.DrawEverything(gl);
		gl.glPopMatrix();

		// Probably unnecessary
		// 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");
				map.SuggestNode (can.sip, can.dip, 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));
		}

		map.SetSize(width, height, gl);

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

	@Override
	protected JPanel createControls() {

		JPanel mypanel = new JPanel();
		fps = new JLabel("0 FPS");

		// May be useful in future so let's just keep this here
//		mypanel.add(fps);
		return mypanel;
	}

	@Override
	public void everythingEnds() {
		super.everythingEnds();
	}

	@Override
	public void activate() {
		TexturePool.DiscardTextures();

		super.activate();
	}

	@Override
	public String getName() {
		return "Groups of activity";
	}

	@Override
	public String getDescription() {
		return getName()
				+ "\n\n"
				+ "A hexagonal grid displaying clients active in the network.\n"
				+ "They are being grouped around the machine they send packets to.\n"
				+ "Nodes 'heat up' when amount of data goes over specified threshold and 'heat down' in time.";
	}

} public class Comet {
	
	Queue<Position> tail ; public final Collection<Position> getTail  () {return tail;}
	List<Position> trace; public List<Position> getTrace () {return trace;}
	boolean tracefinished = false;
	
	double posx; public int getx() {return (int) posx;};
	double posy; public int gety() {return (int) posy;};
	
	double velocityx;
	double velocityy;

	double colr;
	double colg;
	double colb;
	
	double tilt; public double gettilt() {return tilt;};
	
	double kx = 0.3;
	double ky = 0.3;
	
	public Comet (int amp, double tt)
	{
		super();
		
		tail = new LinkedList<Position> ();
		trace = new ArrayList<Position> ();
		
		Random rand = new Random();
		double randspeed = rand.nextDouble() * 10 + 10;

		tilt = tt;
		
		posx = (int) Math.floor(Math.sin(tilt) * amp);
		posy = (int) Math.floor(Math.cos(tilt) * amp);
		
		velocityx = Math.sin(tilt + Math.PI/2) * randspeed;
		velocityy = Math.cos(tilt + Math.PI/2) * randspeed;
	}
	
	public void Step (long time)
	{
		int x = (int) Math.floor(posx);
		int y = (int) Math.floor(posy);
		
		// Add an entry to keep track of the tail
		tail.add(new Position (x, y));

		// Keep only the last 20
		while (tail.size() > 20) tail.poll();
		
		if (!tracefinished || trace.size() < 100)
		{
			trace.add(new Position (x, y));
			
			// Do the angular sorting
			Collections.sort(trace, new Comparator<Position> () {
				@Override
				public int compare(Position o1, Position o2) {
					
					if (Angle(o1.x, o1.y) < Angle(o2.x, o2.y))
						return 1;
					else
						return -1;
				}
			});
			
			// Check whether the distances are small enough
			if (trace.size() >= 15)
			{
				tracefinished = true;
				Position oldp = trace.get(0);
				for (int i=1; i<trace.size(); i++)
				{
					Position newp = trace.get(i);
					if (Math.abs (oldp.x - newp.x) > 10 || Math.abs (oldp.y - newp.y) > 10)
					{
						// Fail - the trace is not finished
						tracefinished = false;
						break;
					}
					oldp = newp;
				}
				
				// Check the edge ones
				Position first = trace.get(0);
				Position last  = trace.get(trace.size()-1);
				if (Math.abs (first.x - last.x) > 10 || Math.abs (first.y - last.y) > 10)
					tracefinished = false;
			}
		};
		
		//System.out.println("Number of tail  elements : " + tail.size());
		//System.out.println("Number of trace elements : " + trace.size());
		
		// Update the position
		posx += velocityx * (time / 30.0);
		posy += velocityy * (time / 30.0);
		
		ForcesAct(time);
	}
	
	public double Angle (double posx, double posy)
	{
		return Math.atan2(posy, posx);
	}
	
	public void ForcesAct(long time)
	{
		// Harmonic oscillator model
		velocityx -= (posx) * time/3000.0 * kx;
		velocityy -= (posy) * time/3000.0 * ky;
		
		//System.out.println("Time : " + time);
		
		// Gravitational model - maybe a better choice? - There is a lot of escaping though
		// Force is proportional to the 1/distance^2
		//double distance = Math.sqrt(Math.pow(posx, 2) + Math.pow(posy, 2));	
		//velocityx -= posx * 10000 / Math.pow(distance, 3); // * kx;
		//velocityy -= posy * 10000 / Math.pow(distance, 3);
	}
	
	public void SimulateTrace ()
	{

	}
}
 public class CometHeatNode extends HeatNode {

	HashMap<String, Comet> entities;

	public Collection<Comet> getEntities() {
		return entities.values();
	}


	boolean changed = true;

	public CometHeatNode(String texturename, String nodename) {
		super (texturename, nodename);

		entities = new HashMap<String, Comet>();
	}

	public void Draw(int base, NodePainter painter, GL2 gl) {
		// Draw one on top of the other
		painter.DrawNode(base, (HeatNode) this, gl);
		painter.DrawNode(base, (CometHeatNode) this, gl);
	}

	public void UpdateWithData(Packet pp) {
		// Make their tilts nicely shifted
		AddSatelite(pp.sip, 100, entities.size() * Math.PI / 10);
		
		super.UpdateWithData (pp);
	}

	public void UpdateAnimation(long time) {
		for (Comet i : entities.values())
			i.Step(time);
	}

	public void AddSatelite(String sip, int amp, double tilt) {
		// Look for the entity with the same name
		Comet find = entities.get(sip);

		if (find == null) {
			entities.put(sip, new Comet(amp, tilt));
		}
	}

	public void RemoveSatelite(String sip) {
		// Look for the entity
		Comet find = entities.get(sip);

		if (find != null) {
			entities.remove(sip);
			DecreaseWarning();
		}
	}

	@Override
	public void MouseClick (MouseEvent e) {
		super.MouseClick (e);
	}

} public class FlipNode extends Node {

	private int framebufferid;

	public Framebuffer GetFramebuffer() {
		return FramebufferPool.get(framebufferid);
	}

	Node front;
	Node back;

	public Node getFrontNode() {
		return front;
	}

	public Node getBackNode() {
		return back;
	}

	boolean frontSeen = true;

	ValueAnimator rotation;

	public double getRotation() {
		return rotation.toDouble();
	};

	public FlipNode(Node f, Node b) {
		super(f.GetName());

		framebufferid = FramebufferPool.Generate();
		rotation = new ValueAnimator(0.0);
		front = f;
		back = b;
	}
	
	public boolean IsFlat ()
	{
		double rot = rotation.toDouble();
		while (rot > 359.0)
			rot -= 360.0;
		while (rot < -1.0)
			rot += 360.0;
		
		double epsilon = 0.01;
		if (rot < 180.0 + epsilon && rot > 180.0 - epsilon) {
			return true;
		}
		if (rot < 0.0 + epsilon && rot > 0.0 - epsilon) {
			return true;
		}
		
		return false;
	}
	
	public Node GetSide ()
	{
		double rot = rotation.toDouble();
		while (rot > 180.0)
			rot -= 360.0;

		if (rot > -90.0 && rot < 90.0) 
			return front;
		else
			return back;
		
	}

	// Visitor double dispatch
	public void Draw(int base, NodePainter painter, GL2 gl) {


		// If one side is visible flat - just redirect rendering to this face
		// rendering procedures
		if (IsFlat())
		{
			Node visside = GetSide();
			visside.Draw(base, painter, gl);
		} else {
			// Otherwise draw the properly transformed face
			painter.DrawNode(base, this, gl);
		}

	}

	public void UpdateWithData(Packet pp) {
		front.UpdateWithData (pp);
		back.UpdateWithData  (pp);
	}

	public void UpdateAnimation(long time) {
		front.UpdateAnimation(time);
		back.UpdateAnimation(time);
	}

	public int Priority() {
		// Return the bigger one of the two
		return Math.max(front.Priority(), back.Priority());
	}

	@Override
	public void MouseClick (MouseEvent e) {
		if (e.getClickCount() == 2) {
			Flip();
		} else {
			GetSide().MouseClick(e);
		}
	};

	public void Flip() {
		frontSeen = !frontSeen;
		if (frontSeen)
			rotation.MoveTo(rotation.getGoal() + 180.0, 1000);
		else
			rotation.MoveTo(rotation.getGoal() - 180.0, 1000);
	}
}
 public class GraphNode extends Node {

	public HashMap<String, Long> protocollengths;
	public long maxVal = 0;

	public GraphNode(String n) {
		super(n);
	
		protocollengths = new HashMap<String, Long> ();
	}

	public void Draw(int base, NodePainter painter, GL2 gl) {
		painter.DrawNode(base, this, gl);
	}

	public void UpdateWithData(Packet pp) {
		Long val = protocollengths.get (pp.protocol);
		if (val == null)
		{
			val = new Long(0);
			protocollengths.put(pp.protocol, val);
		};
		
		val += pp.length;
		protocollengths.put(pp.protocol, val);
		
		// Compare it with the current most common protocol
		if (val > maxVal)
			maxVal = val;
	}

	public void UpdateAnimation(long time) {
	}

	public int Priority() {
		// Those nodes don't want to be in the middle
		return -1;
	}

	@Override
	public void MouseClick (MouseEvent e) {}

} public class HeatNode extends Node {

	int warning;
	
	public int getWarning() {
		return warning;
	};

	double[] bgColor;
	double opacity;

	public double[] getBGColor() {
		return bgColor;
	}
	
	public double getOpacity () {return opacity;}

	public void setBGColor(double r, double g, double b) {
		bgColor[0] = r;
		bgColor[1] = g;
		bgColor[2] = b;
	}
	
	String tex;
	
	public String getTexture() {
		return tex;
	}
	
	boolean selected;

	public boolean getSelected() {
		return selected;
	};
	
	long cumultime;
	
	// How much data was transferred
	long data;
	// And with what protocols
	HashMap<String, Long> protocollengths;
	long maxVal = 0;
	public String maxProto = "";
	
	public HeatNode (String tname, String nname)
	{
		super (nname);
		
		// Cumulating time
		cumultime = 0;
		
		// How much data was transferred
		data = 0;

		tex = tname;
		
		bgColor = new double[3];
		opacity = 1.0;

		// Set the background color
		bgColor[0] = 0.5;
		bgColor[1] = 1.0;
		bgColor[2] = 0.7;
		warning = 0;
		
		selected = false;
		
		protocollengths = new HashMap<String, Long> ();
	}
	
	@Override
	public void Draw(int base, NodePainter painter, GL2 gl) {
		painter.DrawNode(base, this, gl);
	}
	
	@Override
	public void UpdateWithData(Packet pp) {
		data += pp.length;
		while (warning < data/1024)
			IncreaseWarning();
		
		Long val = protocollengths.get (pp.protocol);
		if (val == null)
		{
			val = new Long(0);
			protocollengths.put(pp.protocol, val);
		};
		
		val += pp.length;
		protocollengths.put(pp.protocol, val);
		
		// Compare it with the current most common protocol
		if (val > maxVal)
		{
			maxVal = val;
			maxProto = pp.protocol;
		};
	}

	@Override
	public void UpdateAnimation(long time) {
		cumultime += time;
		if (cumultime >= 5000)
		{
			// Every second decrease the warning of the node
			if (warning > 0)
				DecreaseWarning();
			else if (warning == 0)
			{
				// Delete the node by calling the parent method
				//this.GetParent().DetachNode (this);
			}
			
			// Reset the counter
			cumultime = 0;
		}

	}

	@Override
	public int Priority() {
		return warning;
	}
	

	public void IncreaseWarning() {
		if (warning == 0)
		{
			// Make it colorful again
			bgColor[0] = 0.5;
			bgColor[1] = 1.0;
			bgColor[2] = 0.7;
			
			opacity = 1.0;
		}
	
		warning += 1;
		bgColor[0] *= 1.3;
		bgColor[1] *= 0.9;
		bgColor[2] *= 0.9;
	}

	public void DecreaseWarning() {
		warning -= 1;
		bgColor[0] /= 1.3;
		bgColor[1] /= 0.9;
		bgColor[2] /= 0.9;
		
		if (warning == 0)
		{
			// Make it grey
			bgColor[0] = 0.7;
			bgColor[1] = 0.7;
			bgColor[2] = 0.7;
			
			opacity = 0.5;
		}
	}

	@Override
	public void MouseClick (MouseEvent e) {
		if (e.getClickCount() == 1) {
			selected = !selected;
		}
	}

}
 public class Framebuffer {

	int textureid;
	int fbufferid;
	int dbufferid;

	int base;

	public Framebuffer(int b) {
		textureid = -1;
		fbufferid = -1;
		dbufferid = -1;

		base = b;
	}

	public void Resize(int nb, GL2 gl) {
		base = nb;

		Delete(gl);
	}

	public int BindFBuffer(GL2 gl) {
		if (fbufferid != -1)
			return fbufferid;
		else
			Create(gl);

		return fbufferid;
	}

	public int BindDBuffer(GL2 gl) {
		if (dbufferid != -1)
			return dbufferid;
		else
			Create(gl);

		return dbufferid;
	}

	public int BindTexture(GL2 gl) {
		if (textureid == -1)
			Create(gl);
		
		gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE);
		return textureid;
	}

	public void Create(GL2 gl) {
		int[] texture = new int[1];
		int[] fbuffer = new int[1];
		int[] dbuffer = new int[1];

		// Generate the texture to render to
		gl.glGenTextures(1, texture, 0);
		gl.glBindTexture(GL.GL_TEXTURE_2D, texture[0]);
		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);

		// Reserve space for the texture - (not) make its dimensions twice as
		// big as necessary
		gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, 2 * base, 2 * base, 0, GL.GL_BGRA,
				GL.GL_UNSIGNED_BYTE, null);
		gl.glGenerateMipmap(GL.GL_TEXTURE_2D);

		// Generate the framebuffer
		gl.glGenFramebuffers(1, fbuffer, 0);
		gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbuffer[0]);

		// Attach the texture to the framebuffer
		gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0, GL.GL_TEXTURE_2D,
				texture[0], 0);

		// Create renderbuffer for the depth testing
		gl.glGenRenderbuffers(1, dbuffer, 0);

		// Bind renderbuffer
		gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, dbuffer[0]);

		// Init as a depth buffer
		gl.glRenderbufferStorage(GL2.GL_RENDERBUFFER, GL2.GL_DEPTH_COMPONENT24, 2 * base, 2 * base);

		// Attach to the FBO for depth
		gl.glFramebufferRenderbuffer(GL2.GL_FRAMEBUFFER, GL2.GL_DEPTH_ATTACHMENT,
				GL2.GL_RENDERBUFFER, dbuffer[0]);

		textureid = texture[0];
		fbufferid = fbuffer[0];
		dbufferid = dbuffer[0];
	}

	public void Delete(GL2 gl) {
		// Cleanup
		gl.glDeleteTextures(1, new int[] { textureid }, 0);
		gl.glDeleteRenderbuffers(1, new int[] { dbufferid }, 0);
		gl.glDeleteFramebuffers(1, new int[] { fbufferid }, 0);

		Discard();
	}

	public void Discard() {
		textureid = -1;
		fbufferid = -1;
		dbufferid = -1;
	}

	public void SetupView(GL2 gl) {
		gl.glViewport(0, 0, 2 * base, 2 * base);
		gl.glOrtho(-base, base, -base, base, -10, 1000);
		gl.glTranslated(0.0, 0.0, 5.0);

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

		gl.glShadeModel(GL2.GL_FLAT);

		// Use the typical blending options
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glEnable(GL.GL_BLEND);
	}
}
 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 interface NodePainter {

	public void DrawNode(int base, HeatNode node, GL2 gl);
	
	public void DrawNode(int base, CometHeatNode node, GL2 gl);

	public void DrawNode(int base, FlipNode node, GL2 gl);

	public void DrawNode(int base, GraphNode node, GL2 gl);

	public void DrawNode(int base, Node node, GL2 gl);
}
 public class Painter {
	
	static {
		
		// Prepare the vertex buffer for the hexagon
		FloatBuffer dbh = FloatBuffer.allocate(3*6);
		for (int i = 0; i < 6; i++) {
			dbh.put ((float) Math.cos(Math.PI / 6 + i * Math.PI / 3));
			dbh.put ((float) Math.sin(Math.PI / 6 + i * Math.PI / 3));
			dbh.put ((float) 0.0);
		}
		
		dbh.rewind();

		VertexBuffer hexagon = new VertexBuffer (dbh);
		
		// Prepare the vertex buffer for the textured hexagon
		FloatBuffer dbht = FloatBuffer.allocate(5*6);
		for (int i = 0; i < 6; i++) {
			dbht.put ((float) Math.cos(Math.PI / 6 + i * Math.PI / 3));
			dbht.put ((float) Math.sin(Math.PI / 6 + i * Math.PI / 3));
			dbht.put ((float) 0.0);
			
			dbht.put (0.5f + (float) (0.5 * Math.cos(Math.PI / 6 + i * Math.PI / 3)));
			dbht.put (0.5f + (float) (0.5 * Math.sin(Math.PI / 6 + i * Math.PI / 3)));
		}
		
		dbht.rewind();

		VertexBuffer hexagont = new VertexBuffer (dbht);
		
		// Prepare the vertex buffer for the hexagon
		FloatBuffer dbs = FloatBuffer.allocate(8*4);
		for (int i = 0; i < 4; i++) {
			// Position
			dbs.put ((float) (Math.sqrt(2) * (Math.cos(Math.PI / 4 + i * Math.PI / 2))));
			dbs.put ((float) (Math.sqrt(2) * (Math.sin(Math.PI / 4 + i * Math.PI / 2))));
			dbs.put ((float) 0.0);
			// Color
			dbs.put ((float) 0.5);
			dbs.put ((float) 0.5);
			dbs.put ((float) 0.5);
			//Texture coords
			dbs.put ((float) (0.5 + 0.5 * Math.sqrt(2) * (Math.cos(Math.PI / 4 + i * Math.PI / 2))));
			dbs.put ((float) (0.5 + 0.5 * Math.sqrt(2) * (Math.sin(Math.PI / 4 + i * Math.PI / 2))));
		}
		
		dbs.rewind();

		VertexBuffer square = new VertexBuffer (dbs);
		
		VertexBufferPool.PutBuffer("hexagon", hexagon);
		VertexBufferPool.PutBuffer("hexagontexture", hexagont);
		VertexBufferPool.PutBuffer("square", square);

	}
	
	
	public static void GenerateGrid (String name, int dim)
	{
		FloatBuffer gb = FloatBuffer.allocate (2*6*3 * Units.DimToCap(dim));
		
		for (int i = 0; i < Units.DimToCap(dim); i++) {
			
			Position rs = Units.FindSpotAround(i);
			Position co = Units.CoordinateByRingAndShift(1, rs.x, rs.y);
			Position xy = Units.PositionByCoordinate(1000, co);
			
			float dx = (float) xy.x / 1000;
			float dy = (float) xy.y / 1000;
	
			for (int k = 0; k < 6; k++) {
				gb.put (dx + (float) Math.cos(Math.PI / 6 + k * Math.PI / 3));
				gb.put (dy + (float) Math.sin(Math.PI / 6 + k * Math.PI / 3));
				gb.put (0.0f);
					
				gb.put (dx + (float) Math.cos(Math.PI / 6 + (k+1) * Math.PI / 3));
				gb.put (dy + (float) Math.sin(Math.PI / 6 + (k+1) * Math.PI / 3));
				gb.put (0.0f);
			}
		}
		
		gb.rewind();
		
		VertexBuffer grid = new VertexBuffer (gb);
		
		VertexBufferPool.PutBuffer (name, grid);
	}
	
	public static void DrawGrid (int base, int dim, String name, GL2 gl) {
		int bid = VertexBufferPool.get(name).Bind(gl);
		
		gl.glBindBuffer (GL2.GL_ARRAY_BUFFER, bid);
		
		gl.glPushMatrix();
		gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
		
			gl.glScaled (base, base, 1.0);
		
			//Define the dataformat
			gl.glVertexPointer (3, GL2.GL_FLOAT, 3*Buffers.SIZEOF_FLOAT, 0);
			
			gl.glLineWidth(1.0f);
			gl.glColor3d(0.8, 0.8, 0.8);
			
			gl.glDrawArrays (GL2.GL_LINES, 0, 12 * Units.DimToCap(dim));
			
		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glPopMatrix();
	}

	/*
	 * Rotation is in degrees
	 */
	public static void DrawImage(Texture tex, double cx, double cy, double scale, double rot, double opacity, GL2 gl) {
		int w = tex.getW();
		int h = tex.getH();

		// Bind to the texture
		int id = tex.Bind(gl);
		gl.glBindTexture(GL.GL_TEXTURE_2D, id);

		// Set the opacity
		gl.glColor4d(1.0, 1.0, 1.0, opacity);
		
		// Draw the image
		DrawSquare(w, h, cx, cy, scale, rot, gl);
	}
	
	public static void DrawImageHex (Texture tex, int mode, double cx, double cy, double scale, double rot, double opacity, GL2 gl) {
		int w = tex.getW();
		@SuppressWarnings("unused")
		int h = tex.getH();

		// Bind to the texture
		int id = tex.Bind(gl);
		gl.glBindTexture(GL.GL_TEXTURE_2D, id);
		gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, mode);
		
		// Draw the image
		Painter.DrawHexagonTextured(GL2.GL_POLYGON, cx, cy, (int) Math.round(scale * w/2.0), rot, gl);
	}
	
	public static void DrawSquare (int w, int h, double cx, double cy, double scale, double rot, GL2 gl)
	{
		int bid = VertexBufferPool.get("square").Bind(gl);
		
		//gl.glBindBuffer (GL2.GL_ARRAY_BUFFER, 0);
		//gl.glVertexPointer (3, GL2.GL_DOUBLE, 0, null);
		gl.glBindBuffer (GL2.GL_ARRAY_BUFFER, bid);
		
		gl.glEnable(GL2.GL_TEXTURE_2D);
		gl.glEnable(GL2.GL_TEXTURE_2D_MULTISAMPLE);
		gl.glPushMatrix();

		gl.glEnableClientState (GL2.GL_VERTEX_ARRAY);
		gl.glEnableClientState (GL2.GL_COLOR_ARRAY);
		gl.glEnableClientState (GL2.GL_TEXTURE_COORD_ARRAY);
		
			gl.glScaled (scale*w/2.0, scale*h/2.0, 1.0);
			gl.glTranslated (cx, cy, 0.0);
			gl.glRotated(rot, 0.0, 0.0, 1.0);
			
			//gl.glVertexPointer (3, GL2.GL_DOUBLE, 6 * Buffers.SIZEOF_DOUBLE, 0);
			gl.glVertexPointer   (3, GL2.GL_FLOAT, 8*Buffers.SIZEOF_FLOAT, 0);
			gl.glColorPointer    (3, GL2.GL_FLOAT, 8*Buffers.SIZEOF_FLOAT, 3*Buffers.SIZEOF_FLOAT);
			gl.glTexCoordPointer (2, GL2.GL_FLOAT, 8*Buffers.SIZEOF_FLOAT, 6*Buffers.SIZEOF_FLOAT);
			
			//gl.glColor3d (0.0, 0.0, 0.0);
			gl.glDrawArrays(GL2.GL_POLYGON, 0, 4);
			
		gl.glDisableClientState (GL2.GL_TEXTURE_COORD_ARRAY);
		gl.glDisableClientState( GL2.GL_COLOR_ARRAY );
		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		
		gl.glPopMatrix();
		gl.glDisable(GL2.GL_TEXTURE_2D_MULTISAMPLE);
		gl.glDisable(GL.GL_TEXTURE_2D);
	}

	public static void DrawSquareImmediate (int w, int h, double cx, double cy, double scale, double rot, GL2 gl) {
		gl.glEnable(GL2.GL_TEXTURE_2D);
		gl.glEnable(GL2.GL_TEXTURE_2D_MULTISAMPLE);
		gl.glPushMatrix();

			// Translate and rotate (rotation in degrees)
			gl.glTranslated(cx, cy, 0.0);
			gl.glRotated(rot, 0.0, 0.0, 1.0);
			
			gl.glBegin(GL2.GL_POLYGON);
				gl.glTexCoord2d(1, 0);
				gl.glVertex2d(-scale * w / 2.0, scale * h / 2.0);
				gl.glTexCoord2d(0, 0);
				gl.glVertex2d(+scale * w / 2.0, scale * h / 2.0);
				gl.glTexCoord2d(0, 1);
				gl.glVertex2d(+scale * w / 2.0, -scale * h / 2.0);
				gl.glTexCoord2d(1, 1);
				gl.glVertex2d(-scale * w / 2.0, -scale * h / 2.0);
			gl.glEnd();

		gl.glPopMatrix();
		gl.glDisable(GL2.GL_TEXTURE_2D_MULTISAMPLE);
		gl.glDisable(GL.GL_TEXTURE_2D);
		
		/*
		 * Draw the lines around gl.glPushMatrix();
		 * 
		 * // Translate and rotate (rotation in degrees) gl.glTranslated(cx, cy,
		 * 0.0); gl.glRotated (rot, 0.0, 0.0, 1.0); gl.glColor3d(0.0, 0.0, 0.0);
		 * 
		 * gl.glBegin (GL2.GL_LINE_LOOP); gl.glVertex2d (-scale*w/2.0,
		 * scale*h/2.0); gl.glVertex2d (+scale*w/2.0, scale*h/2.0);
		 * gl.glVertex2d (+scale*w/2.0, -scale*h/2.0); gl.glVertex2d
		 * (-scale*w/2.0, -scale*h/2.0); gl.glEnd (); gl.glPopMatrix();
		 */
	}
	
	public static void DrawHexagon (int mode, double x, double y, int base, GL2 gl) {
		int bid = VertexBufferPool.get("hexagon").Bind(gl);
		
		gl.glBindBuffer (GL2.GL_ARRAY_BUFFER, bid);
		
		gl.glPushMatrix();
		gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
		
			gl.glTranslated (x, y, 0.0);
			gl.glScaled (base, base, 1.0);
		
			//Define the dataformat
			gl.glVertexPointer (3, GL2.GL_FLOAT, 3*Buffers.SIZEOF_FLOAT, 0);
			
			gl.glDrawArrays (mode, 0, 6);
			
		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glPopMatrix();
	}
	
	public static void DrawHexagonTextured (int mode, double x, double y, int base, double rot, GL2 gl) {
		int bid = VertexBufferPool.get("hexagontexture").Bind(gl);
		
		gl.glBindBuffer (GL2.GL_ARRAY_BUFFER, bid);
		
		gl.glEnable(GL2.GL_TEXTURE_2D);
		gl.glEnable(GL2.GL_TEXTURE_2D_MULTISAMPLE);
		
		gl.glPushMatrix();
		gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glEnableClientState (GL2.GL_TEXTURE_COORD_ARRAY);
		
			gl.glTranslated (x, y, 0.0);
			gl.glScaled (base, base, 1.0);
			gl.glRotated(rot, 0.0, 0.0, 1.0);
		
			//Define the dataformat
			gl.glVertexPointer   (3, GL2.GL_FLOAT, 5*Buffers.SIZEOF_FLOAT, 0);
			gl.glTexCoordPointer (2, GL2.GL_FLOAT, 5*Buffers.SIZEOF_FLOAT, 3*Buffers.SIZEOF_FLOAT);
			
			gl.glDrawArrays (mode, 0, 6);
			
		gl.glDisableClientState (GL2.GL_TEXTURE_COORD_ARRAY);
		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glPopMatrix();
		
		gl.glDisable (GL2.GL_TEXTURE_2D);
		gl.glDisable (GL2.GL_TEXTURE_2D_MULTISAMPLE);
	}
	
	public static void DrawHexagonImmediate (int mode, double x, double y, int base, GL2 gl) {
		gl.glBegin(mode);
		for (int i = 0; i < 6; i++) {
			gl.glVertex2d(x + base * Math.cos(Math.PI / 6 + i * Math.PI / 3),
					y + base * Math.sin(Math.PI / 6 + i * Math.PI / 3));
		}
		;
		gl.glEnd();

	}

	public static void DrawGridUnoptimized(int base, GL2 gl) {
		// Draw the usual hexagon
		
		for (int i = -20; i < 20; i++) {
			for (int j = -20; j < 20; j++) {
				gl.glLineWidth(1.0f);
				gl.glColor3d(0.8, 0.8, 0.8);
				double dx = base / 2 * Math.sqrt(3);
				if (j % 2 == 0)
					dx = 0;
				DrawHexagon(GL2.GL_LINE_LOOP, base * Math.sqrt(3) * i + dx, base * 1.5 * j, 400, gl);
			}
		}
	}

	public static void TraceCurve(double[] arr, int size, GL2 gl) {
		DoubleBuffer ctrlPointBuffer = DoubleBuffer.allocate(3 * size);

		for (int i = 0; i < 3 * size; i++)
			ctrlPointBuffer.put(arr[i]);

		ctrlPointBuffer.rewind();

		for (int i = 0; i < size - 10; i += 9) {
			gl.glMap1d(GL2.GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 10, arr, 3 * i);
			// gl.glMap2d (GL2.GL_MAP2_VERTEX_3, 0.0, 1.0, 3, size, 0.0, 1.0, 3,
			// 1, ctrlPointBuffer);
			gl.glEnable(GL2.GL_MAP1_VERTEX_3);
			gl.glLineWidth(2.0f);
			gl.glPointSize(3.0f);
			gl.glColor3d(0.0, 0.0, 0.0);
			gl.glMapGrid1d(3, 0.0, 1.0);
			gl.glEvalMesh1(GL2.GL_LINE, 0, 3);
			if (i / 2 % 2 == 0)
				gl.glColor3d(1.0, 0.0, 0.0);
			else
				gl.glColor3d(0.0, 1.0, 0.0);
			gl.glEvalMesh1(GL2.GL_POINT, 0, 3);
			gl.glDisable(GL2.GL_MAP1_VERTEX_3);
		}
	}

	public static void DrawCircle (double x, double y, double rad, GL2 gl) {
		gl.glBegin(GL2.GL_TRIANGLE_FAN);
		int parts = 50;
		gl.glColor4d(0.5, 0.2, 0.0, 1.0);
		gl.glVertex2d(x, y);
		gl.glColor4d(0.7, 0.3, 0.1, 0.4);
		for (int i = 0; i <= parts; i++) {
			// Rotate
			double xx = x + rad * Math.cos((2 * Math.PI * i) / parts);
			double yy = y + rad * Math.sin((2 * Math.PI * i) / parts);
			gl.glVertex2d(xx, yy);
		}
		gl.glEnd();
	}
	
	public static void StressTest (int base, GL2 gl) {
	
		for (int i=0; i<10000; i++)
		{
			int x = base * (i % (int) Math.round(Math.sqrt(10000)));
			int y = 2 * base * (i / (int) Math.round(Math.sqrt(10000)));

			gl.glPushMatrix();
				gl.glTranslatef(x, y, 0.0f);

				gl.glColor3d(0.5, 0.5, 0.4);
				Painter.DrawHexagon (GL2.GL_POLYGON, 0.0, 0.0, base, gl);
				gl.glColor3d(0.7, 0.7, 0.6);
				Painter.DrawSquare (100, 100, 0.0, 0.0, 1.0, 0.0, gl);
				
			gl.glPopMatrix();
		}
	}
	
	public static void StressTestList (int base, GL2 gl) {
		
		int	index = gl.glGenLists(1);
		
		gl.glNewList(index, GL2.GL_COMPILE);
			gl.glColor3d(0.5, 0.5, 0.4);
			Painter.DrawHexagon (GL2.GL_POLYGON, 0.0, 0.0, base, gl);
			gl.glColor3d(0.7, 0.7, 0.6);
			Painter.DrawSquare (100, 100, 0.0, 0.0, 1.0, 0.0, gl);
		gl.glEndList();
		
		for (int i=0; i<10000; i++)
		{
			int x = base * (i % (int) Math.round(Math.sqrt(10000)));
			int y = 2 * base * (i / (int) Math.round(Math.sqrt(10000)));

			gl.glPushMatrix();
				gl.glTranslatef(x, y, 0.0f);
				//gl.glTranslated(x, y, 0.0);
				
				gl.glCallList(index);		
			gl.glPopMatrix();
		}
		
		gl.glDeleteLists(index, 1);
	}
}
 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);
		}
	}
}

import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Map.Entry;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import netvis.visualisations.comets.ActivityVisualisation;
import netvis.visualisations.comets.Comet;
import netvis.visualisations.comets.CometHeatNode;
import netvis.visualisations.comets.FlipNode;
import netvis.visualisations.comets.GraphNode;
import netvis.visualisations.comets.HeatNode;
import netvis.visualisations.gameengine.Framebuffer;
import netvis.visualisations.gameengine.Node;
import netvis.visualisations.gameengine.NodePainter;
import netvis.visualisations.gameengine.Painter;
import netvis.visualisations.gameengine.Position;
import netvis.visualisations.gameengine.TextRendererPool;
import netvis.visualisations.gameengine.TexturePool;
import com.jogamp.opengl.util.awt.TextRenderer;

package netvis.visualisations.maps;





public class MapPainter implements NodePainter {

	static {
		// Load all the necessary textures
		TexturePool.LoadTexture("hexagon1",	ActivityVisualisation.class.getResource("resources/hex1.png"));
		TexturePool.LoadTexture("hexagon2",	ActivityVisualisation.class.getResource("resources/hex2.png"));
		
		TexturePool.LoadTexture("server", ActivityVisualisation.class.getResource("resources/server.png"));
		TexturePool.LoadTexture("basic", ActivityVisualisation.class.getResource("resources/basic.png"));
	}
	
	public void DrawNode(int base, HeatNode lum, GL2 gl) {
		
		double [] color = lum.getBGColor();
		double opacity = lum.getOpacity();

		// Draw the background
		gl.glColor4d (color[0], color[1], color[2], opacity);
		//Painter.DrawHexagon(GL2.GL_POLYGON, 0, 0, base, gl);

		// Draw the graphical hexagon
		if (lum.getSelected() == true)
			Painter.DrawImageHex(TexturePool.get("hexagon2"), GL2.GL_DECAL, 0.0, 0.0, 2 * base / 512.0, 0, opacity, gl);
		else
			Painter.DrawImageHex(TexturePool.get("hexagon1"), GL2.GL_DECAL, 0.0, 0.0, 2 * base / 512.0, 0, opacity, gl);

		// Draw the server image
		int imageSize = 200;
		gl.glColor4d (0.0, 0.0, 0.0, opacity);
		if (lum.getSelected() == true)
			Painter.DrawImageHex (TexturePool.get(lum.getTexture()), GL2.GL_BLEND, 0.0, 0.0, imageSize / 512.0, 90.0, opacity, gl);
		else
			Painter.DrawImageHex (TexturePool.get(lum.getTexture()), GL2.GL_BLEND, 0.0, 0.0, imageSize / 512.0, 0.0, opacity, gl);

		// Write the name of the node

		TextRenderer renderer = TextRendererPool.get("basic");
