package org.torproject.jtor.circuits.impl;

import java.util.Date;

import org.torproject.jtor.crypto.TorRandom;

public class CircuitStatus {
	private final static int MAXIMUM_TIMEOUT_COUNT = 3;
	enum CircuitState {
		UNCONNECTED("Unconnected"),
		BUILDING("Building"),
		FAILED("Failed"),
		OPEN("Open"),
		DESTROYED("Destroyed");
		String name;
		CircuitState(String name) { this.name = name; }
		public String toString() { return name; }
	}
	
	private Date timestampCreated;
	private Date timestampDirty;
	private int streamTimeoutErrorCount = 0;
	private int currentStreamId;
	private Object streamIdLock = new Object();
	private CircuitState state = CircuitState.UNCONNECTED;

	CircuitStatus() {
		initializeCurrentStreamId();
	}

	private void initializeCurrentStreamId() {
		final TorRandom random = new TorRandom();
		currentStreamId = random.nextInt(0xFFFF) + 1;
	}

	synchronized boolean countStreamTimeout() {
		streamTimeoutErrorCount++;
		return streamTimeoutErrorCount >= MAXIMUM_TIMEOUT_COUNT;
	}

	synchronized void updateCreatedTimestamp() {
		timestampCreated = new Date();
		timestampDirty = timestampCreated;
	}

	synchronized void updateDirtyTimestamp() {
		timestampDirty = new Date();
	}

	synchronized long getMillisecondsElapsedSinceCreated() {
		return millisecondsElapsedSince(timestampCreated);
	}

	synchronized long getMillisecondsIdle() {
		return millisecondsElapsedSince(timestampDirty);
	}

	private static long millisecondsElapsedSince(Date then) {
		if(then == null)
			return 0;
		final Date now = new Date();
		return now.getTime() - then.getTime();	
	}

	synchronized boolean isDirty() {
		return timestampDirty != timestampCreated;
	}

	void setStateConnected() {
		state = CircuitState.OPEN;
	}

	void setStateBuilding() {
		state = CircuitState.BUILDING;
	}

	void setStateFailed() {
		state = CircuitState.FAILED;
	}

	void setStateOpen() {
		state = CircuitState.OPEN;
	}

	void setStateDestroyed() {
		state = CircuitState.DESTROYED;
	}

	boolean isBuilding() {
		return state == CircuitState.BUILDING;
	}

	boolean isConnected() {
		return state == CircuitState.OPEN;
	}

	boolean isUnconnected() {
		return state == CircuitState.UNCONNECTED;
	}

	String getStateAsString() {
		return state.toString();
	}

	int nextStreamId() {
		synchronized(streamIdLock) {
			currentStreamId++;
			if(currentStreamId > 0xFFFF)
				currentStreamId = 1;
			return currentStreamId;
		}
	}

}

@@ -58,58 +58,197 @@
 til.
-Date;%0A%0Aimport org.torproject.jtor.crypto.TorRandom
+Collections;%0Aimport java.util.Date;%0Aimport java.util.List;%0A%0Aimport org.torproject.jtor.TorException;%0Aimport org.torproject.jtor.crypto.TorRandom;%0Aimport org.torproject.jtor.directory.Router
 ;%0A%0Ap
@@ -577,17 +577,16 @@
 e; %7D%0A%09%7D%0A
-%09
 %0A%09privat
@@ -812,16 +812,77 @@
 NNECTED;
+%0A%09private List%3CRouter%3E circuitPath = Collections.emptyList();
 %0A%0A%09Circu
@@ -1780,17 +1780,16 @@
 tTime();
-%09
 %0A%09%7D%0A%0A%09sy
@@ -1948,24 +1948,48 @@
 ateBuilding(
+List%3CRouter%3E circuitPath
 ) %7B%0A%09%09state 
@@ -2009,24 +2009,439 @@
 e.BUILDING;%0A
+%09%09this.circuitPath = Collections.unmodifiableList(circuitPath);%0A%09%7D%0A%0A%09Router getFinalRouter() %7B%0A%09%09if(state == CircuitState.UNCONNECTED)%0A%09%09%09throw new TorException(%22Cannot retrieve last router from UNCONNECTED circuit%22);%0A%09%09if(circuitPath.size() == 0) %0A%09%09%09throw new TorException(%22No routers on circuit (?!)%22);%0A%09%09return circuitPath.get(circuitPath.size() - 1);%0A%09%7D%0A%0A%09List%3CRouter%3E getCircuitPath() %7B%0A%09%09return circuitPath;%0A
 %09%7D%0A%0A%09void se

Save the requested path in CircuitStatus when initiating build of a circuit.