package com.subgraph.orchid.circuits;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.subgraph.orchid.dashboard.DashboardRenderable;
import com.subgraph.orchid.dashboard.DashboardRenderer;

public class CircuitPredictor implements DashboardRenderable {

	private final static long TIMEOUT_MS = 60 * 60 * 1000; // One hour
	
	private final Map<Integer, Date> portsSeen;
	
	public CircuitPredictor() {
		portsSeen = new HashMap<Integer,Date>();
		portsSeen.put(80, new Date());
	}
	
	void addExitPortRequest(int port) {
		synchronized (portsSeen) {
			portsSeen.put(port, new Date());	
		}
	}
	
	
	private boolean isEntryExpired(Entry<Integer, Date> e, Date now) {
		return (now.getTime() - e.getValue().getTime()) > TIMEOUT_MS;
	}
	
	private void removeExpiredPorts() {
		final Date now = new Date();
		final Iterator<Entry<Integer, Date>> it = portsSeen.entrySet().iterator();
		while(it.hasNext()) {
			if(isEntryExpired(it.next(), now)) {
				it.remove();
			}
		}
	}
	
	Set<Integer> getPredictedPorts() {
		synchronized (portsSeen) {
			removeExpiredPorts();
			return new HashSet<Integer>(portsSeen.keySet());
		}
	}
	
	Date getLastSeen(int port) {
		return portsSeen.get(port);
	}

	List<PredictedPortTarget> getPredictedPortTargets() {
		final List<PredictedPortTarget> targets = new ArrayList<PredictedPortTarget>();
		for(int p: getPredictedPorts()) {
			targets.add(new PredictedPortTarget(p));
		}
		return targets;
	}

	public void dashboardRender(DashboardRenderer renderer, PrintWriter writer, int flags)
			throws IOException {
		
		if((flags & DASHBOARD_PREDICTED_PORTS) == 0) {
			return;
		}
		writer.println("[Predicted Ports] ");
		for(int port : portsSeen.keySet()) {
			writer.write(" "+ port);
			Date lastSeen = portsSeen.get(port);
			if(lastSeen != null) {
				Date now = new Date();
				long ms = now.getTime() - lastSeen.getTime();
				writer.write(" (last seen "+ TimeUnit.MINUTES.convert(ms, TimeUnit.MILLISECONDS) +" minutes ago)");
			}
			writer.println();
		}
		writer.println();
	}
}

@@ -120,31 +120,8 @@
 st;%0A
-import java.util.Date;%0A
 impo
@@ -606,20 +606,20 @@
 nteger, 
-Date
+Long
 %3E portsS
@@ -692,47 +692,40 @@
 ger,
-Date
+Long
 %3E();%0A%09%09
-portsSeen.put(80, new Date()
+addExitPortRequest(80
 );%0A%09
@@ -821,21 +821,36 @@
 rt, 
-new Date
+System.currentTimeMillis
 ());
-%09
 %0A%09%09%7D
@@ -908,21 +908,21 @@
 er, 
-Date
+Long
 %3E e, 
-Date
+long
  now
@@ -938,26 +938,16 @@
 urn (now
-.getTime()
  - e.get
@@ -953,26 +953,16 @@
 tValue()
-.getTime()
 ) %3E TIME
@@ -1019,35 +1019,51 @@
 %09%09final 
-Date
+long
  now = 
-new Date
+System.currentTimeMillis
 ();%0A%09%09fi
@@ -1090,20 +1090,20 @@
 nteger, 
-Date
+Long
 %3E%3E it = 
@@ -1382,73 +1382,8 @@
 %7D%0A%09%7D
-%0A%09%0A%09Date getLastSeen(int port) %7B%0A%09%09return portsSeen.get(port);%0A%09%7D
 %0A%0A%09L
@@ -1912,20 +1912,20 @@
 rt);%0A%09%09%09
-Date
+Long
  lastSee
@@ -1983,27 +1983,43 @@
 %09%09%09%09
-Date
+long
  now = 
-new Date
+System.currentTimeMillis
 ();%0A
@@ -2035,26 +2035,16 @@
 ms = now
-.getTime()
  - lastS
@@ -2050,18 +2050,8 @@
 Seen
-.getTime()
 ;%0A%09%09

Use Long instead of Date for timestamps