public interface Address {
    /** 
    * Called to establish a new outgoing connection.
    * @return A new connection to the peer.
    */
	Connection createConnection();
	/** The cost of an address gives an heuristic about which Address should be used if multiple are available. Lower cost is preferred. An WlanAddress uses a cost of 10. */ 
	int getCost();
} public interface Connection {
	/** 
	* The Connection.Handler interface allows the Connection to inform its delegate about various events.
	*/
	public static interface Handler {
	    /** Called when the connection connected successfully.*/
		void onConnect(Connection connection);
	    /** Called when the connection closes. Has an optional error parameter to indicate issues. (Used to report problems to the user). */
		void onClose(Connection connection);
	    /** Called when data was received. */
		void onDataReceived(Connection connection, ByteBuffer data);
	    /** Called for each writeData call, when it is complete. */
		void onDataSent(Connection connection);
	}
	
    /** Sets the connection's delegate. */
	void setHandler(Handler handler);
    /** The connection's delegate. */
	Handler getHandler();
	
    /** Whether this connection is currently connected. */
	boolean isConnected();
    /** Reto sends packets which may vary in size. This property may return an ideal packet size that should be used if possible. */
	int getRecommendedPacketSize();
	
    /** Connects the connection. */
	void connect();
    /** Closes the connection. */
	void close();
	
    /** Sends data using the connection. */
	void writeData(ByteBuffer data);
} public enum PacketType {		
	UNKNOWN(0),
	
	// Routing Layer
	LINK_HANDHAKE(1),
	ROUTING_HANDSHAKE(2),
	LINK_STATE(3),
	FLOODED_PACKET(4),
	ROUTED_CONNECTION_ESTABLISHED_CONFIRMATION(5),
	
	// Connectivity
	MANAGED_CONNECTION_HANDSHAKE(10),
	CLOSE_REQUEST(11),
	CLOSE_ANNOUNCE(12),
	CLOSE_ACKNOWLEDGE(13),
	
	// Data transmission
	TRANSFER_STARTED(20),
	DATA_PACKET(21),
	CANCELLED_TRANSFER(22),
	PROGRESS_INFORMATION(23);
	
	private static final Map<Integer, PacketType> intToTypeMap = new HashMap<Integer, PacketType>();
	static {
	    for (PacketType type : PacketType.values()) {
	        intToTypeMap.put(type.value, type);
	    }
	}
	
	private final int value;
	
	PacketType(int value) {
		this.value = value;
	}
	public static PacketType fromRaw(int value) {
		PacketType result = intToTypeMap.get(value);
		if (result == null) result = PacketType.UNKNOWN;
		return result;
	}
	public static PacketType fromData(ByteBuffer data) {
		if (data.remaining() < 4) {
			System.err.println("Insufficient data to get packet type.");
			return PacketType.UNKNOWN;
		}
		
		int value = data.getInt();
		data.rewind();
		PacketType type = PacketType.fromRaw(value);
		
		if (type == PacketType.UNKNOWN) {
			System.err.println("Read unknown type, value was: "+value);
		}
		
		return type;
	}
	public int toRaw() {
		return this.value;
	}
} public class LinkStateRoutingTable<T> {
	/**
	 * A Change object contains changes that occurred in the routing table caused by some operation.
	 * */
	public static class Change<T> {
		/**
		 * Contains information about nodes that became reachable.
		 * */
		public static class NowReachableInformation<T> {
			/** The node that became reachable */
			public final T node;
			/** The node that is the next hop for reaching this node. */
			public final T nextHop;
			/** The total cost for reaching this node. */
			public final double cost;
			
			/** Constructs a new NowReachableInformation object. */
			public NowReachableInformation(T node, T nextHop, double cost) {
				this.node = node;
				this.nextHop = nextHop;
				this.cost = cost;
			}
		}
		/**
		 * Contains informatiown about nodes that have changed routes.
		 * */
		public static class RouteChangedInformation<T> {
			/** The node that became reachable */
			public final T node;
			/** The node that is the next hop for reaching this node. */
			public final T nextHop;
			/** The previous total cost for reaching this node. */
			public final double oldCost;
			/** The total cost for reaching this node. */
			public final double cost;
			
			/** Constructs a new RouteChangedInformation object. */
			public RouteChangedInformation(T node, T nextHop, double oldCost, double cost) {
				this.node = node;
				this.nextHop = nextHop;
				this.oldCost = oldCost;
				this.cost = cost;
			}
		}
		
		/** Contains information about all nodes that are now reachable. */
		public final List<NowReachableInformation<T>> nowReachable;
		/** Contains all nodes that are now unreachable. */
		public final List<T> nowUnreachable;
		/** Contains information about all nodes that have changed routes. */
		public final List<RouteChangedInformation<T>> routeChanged;
		
		/** Constructs a new Change object. */
		public Change(List<NowReachableInformation<T>> nowReachable, List<T> nowUnreachable, List<RouteChangedInformation<T>> routeChanged) {
			this.nowReachable = nowReachable;
			this.nowUnreachable = nowUnreachable;
			this.routeChanged = routeChanged;
		}
		
		/** Returns whether this Change object is actually empty. */
		public boolean isEmpty() {
			return nowReachable.isEmpty() && nowUnreachable.isEmpty() && routeChanged.isEmpty();
		}
	}
	
	/**
	 * Stores neighbor information (the neighbor itself and the cost of reaching the neighbor).
	 * */
	public static class NeighborInformation<T> {
		/** The node object representing the neighbor. */
		public final T node;
		/** The cost of reaching this neighbor. */
		public final double cost;
		
		/** Constructs a new neighbor information object. */
		public NeighborInformation(T node, double cost) {
			this.node = node;
			this.cost = cost;
		}
	}

	/** A directed, weighted multigraph used to represent the network of nodes and their link states. */
	private final DirectedWeightedPseudograph<T, DefaultWeightedEdge> graph = new DirectedWeightedPseudograph<>(DefaultWeightedEdge.class);
	/** The local node. In all neighbor related operations, the neighbor is considered a neighbor of this node. */
	private final T localNode;
	
	/** Constructs a new LinkStateRoutingTable. */
	public LinkStateRoutingTable(T localNode) {
		this.localNode = localNode;
		this.graph.addVertex(localNode);
	}
	
	/**
	 * Computes the changes to the routing table when updating or adding a new neighbor.
	 * If the neighbor is not yet known to the routing table, it is added.
	 * 
	 * @param neighbor The neighbor to update or add.
	 * @param cost The cost to reach that neighbor.
	 * @return A LinkStateRoutingTable.Change object representing the changes that occurred in the routing table.
	 * */
	public Change<T> getRoutingTableChangeForNeighborUpdate(final T neighbor, final double cost) {
		return this.trackGraphChanges(new Runnable() {
			@Override
			public void run() {
				LinkStateRoutingTable.this.updateNeighbor(neighbor, cost);
			}
		});
	}
	/**
	 * Computes the changes to the routing table when removing a neighbor.
	 * 
	 * @param neighbor The neighbor to remove
	 * @return A LinkStateRoutingTable.Change object representing the changes that occurred in the routing table.
	 * */
	public Change<T> getRoutingTableChangeForNeighborRemoval(final T neighbor) {
		return this.trackGraphChanges(new Runnable() {
			
			@Override
			public void run() {
				LinkStateRoutingTable.this.removeNeighbor(neighbor);
			}
		});
	}
	/**
	 * Computes the changes to the routing table when link state information is received for a given node.
	 * 
	 * @param node The node for which a list of neighbors (ie. link state information) was received.
	 * @param neighbors The node's neighbors.
	 * @return A LinkStateRoutingTable.Change object representing the changes that occurred in the routing table.
	 * */
	public Change<T> getRoutingTableChangeForLinkStateInformationUpdate(final T node, final List<NeighborInformation<T>> neighbors) {
		return this.trackGraphChanges(new Runnable() {
			
			@Override
			public void run() {
				LinkStateRoutingTable.this.updateLinkStateInformation(node, neighbors);
			}
		});
	}
	
	/** Returns a list of neighbors for the local node (ie. link state information). */
	public List<NeighborInformation<T>> getLinkStateInformation() {
		List<NeighborInformation<T>> linkStateInformation = new ArrayList<>();
		
		for (DefaultWeightedEdge edge : this.graph.outgoingEdgesOf(this.localNode)) {
			linkStateInformation.add(new NeighborInformation<T>(this.graph.getEdgeTarget(edge), this.graph.getEdgeWeight(edge)));
		}
		
		return linkStateInformation;
	}
	
	public Tree<T> getNextHopTree(Set<T> destinations) {
		for (T destination : destinations) {
			if (!this.graph.containsVertex(destination)) {
				System.err.println("You have attempted to connect to a destination that has not yet been added to the routing table. Most likely, it will be discovered soon.");
				return null;
			}
		}
		
		Set<T> participatingNodes = new HashSet<>(destinations);
		participatingNodes.add(this.localNode);
		
		return MinimumSteinerTreeApproximation.approximateSteinerTree(this.graph, localNode, participatingNodes);
	}

	/** Updates or adds a neighbor. */
	private void updateNeighbor(T neighbor, double cost) {
		if (this.graph.edgeSet().contains(neighbor)) this.graph.removeAllEdges(this.localNode, neighbor);
		this.graph.addVertex(neighbor);
		DefaultWeightedEdge edge = this.graph.addEdge(this.localNode, neighbor);
		this.graph.setEdgeWeight(edge, cost);
	}
	/** Removes a neighbor. */
	private void removeNeighbor(T neighbor) {
		this.graph.removeAllEdges(this.localNode, neighbor);
	}
	/** Updates link state information for a given node. */
	private void updateLinkStateInformation(T node, List<NeighborInformation<T>> neighbors) {
		// Remove all edges
		if (this.graph.vertexSet().contains(node)) {
			Set<DefaultWeightedEdge> outgoingEdges = new HashSet<>(this.graph.outgoingEdgesOf(node));
			this.graph.removeAllEdges(outgoingEdges);
		} else {
			this.graph.addVertex(node);
		}

		// Add new edges
		for (NeighborInformation<T> neighbor : neighbors) {
			this.graph.addVertex(neighbor.node);
			DefaultWeightedEdge edge = this.graph.addEdge(node, neighbor.node);
			this.graph.setEdgeWeight(edge, neighbor.cost);
		}
	}
	
	/**
	 * Computes a Change object for arbitrary modifications of the graph.
	 * 
	 * This method first computes the shortest paths to all reachable nodes in the graph, then runs the graph action, and then calculates all shortest paths again.
	 * 
	 * From changes in which nodes are reachable, and changes in the paths, a LinkStateRoutingTable.Change object is created.
	 * 
	 * @param graphAction A Runnable that is expected to perform some changes on the graph.
	 * @return A LinkStateRoutingTable.Change object representing the changes caused by the changes performed by the graphAction.
	 * */
	private Change<T> trackGraphChanges(Runnable graphAction) {
		// Compute shortest paths before and after execting the graph action
		Map<T, DijkstraShortestPath<T, DefaultWeightedEdge>> previousShortestPaths = new HashMap<>();
		
		for (T node : graph.vertexSet()) {
			DijkstraShortestPath<T, DefaultWeightedEdge> shortestPath = new DijkstraShortestPath<T, DefaultWeightedEdge>(this.graph, this.localNode, node);
			if (shortestPath.getPath() != null) previousShortestPaths.put(node, shortestPath);
		}

		graphAction.run();
		
		Map<T, DijkstraShortestPath<T, DefaultWeightedEdge>> updatedShortestPaths = new HashMap<>();
		
		for (T node : graph.vertexSet()) {
			DijkstraShortestPath<T, DefaultWeightedEdge> shortestPath = new DijkstraShortestPath<T, DefaultWeightedEdge>(this.graph, this.localNode, node);
			if (shortestPath.getPath() != null) updatedShortestPaths.put(node, shortestPath);
		}
		
		// Compute routing table changes from shortest paths.
		
		// 1. Nodes that are now reachable but weren't before.
		List<NowReachableInformation<T>> nowReachable = new ArrayList<>();
		Set<T> nowReachableNodes = new HashSet<T>(updatedShortestPaths.keySet());
		nowReachableNodes.removeAll(previousShortestPaths.keySet());
		for (T nowReachableNode : nowReachableNodes) {
			DijkstraShortestPath<T, DefaultWeightedEdge> shortestPath = updatedShortestPaths.get(nowReachableNode);
			DefaultWeightedEdge firstEdge = shortestPath.getPathEdgeList().get(0);
			nowReachable.add(new NowReachableInformation<T>(
				nowReachableNode, 
				this.graph.getEdgeTarget(firstEdge), 
				shortestPath.getPathLength())
			);
		}
		
		// 2. Nodes that were reachable before, but are now unreachable.
		Set<T> nowUnreachableNodes = new HashSet<T>(previousShortestPaths.keySet());
		nowUnreachableNodes.removeAll(updatedShortestPaths.keySet());
		List<T> nowUnreachable = new ArrayList<T>(nowUnreachableNodes);
		
		// 3. Nodes that were and are still reachable, but have a changed route.
		Set<T> stillReachable = new HashSet<T>(previousShortestPaths.keySet());
		stillReachable.retainAll(updatedShortestPaths.keySet());
		
		List<RouteChangedInformation<T>> routeChanged = new ArrayList<>();
		
		for (T node : stillReachable) {
			if (node == this.localNode) continue;
			
			DijkstraShortestPath<T, DefaultWeightedEdge> previousShortestPath = previousShortestPaths.get(node);
			DijkstraShortestPath<T, DefaultWeightedEdge> updatedShortestPath = updatedShortestPaths.get(node);
			
			boolean pathLengthChanged = previousShortestPath.getPathLength() != updatedShortestPath.getPathLength();
			boolean nextHopChanged = this.graph.getEdgeTarget(previousShortestPath.getPathEdgeList().get(0)) != this.graph.getEdgeTarget(updatedShortestPath.getPathEdgeList().get(0));
			
			if (pathLengthChanged || nextHopChanged) {
				routeChanged.add(new RouteChangedInformation<T>(
						node, 
						this.graph.getEdgeTarget(updatedShortestPath.getPathEdgeList().get(0)), 
						previousShortestPath.getPathLength(), 
						updatedShortestPath.getPathLength())
				);
			}
		}
		
		return new Change<>(nowReachable, nowUnreachable, routeChanged);
	}
} public class Tree<T> {
    /** The tree's associated value */
	public final T value;
    /** The tree's subtrees */
	public final Set<Tree<T>> children;
	
    /** Constructs a new tree given a value and a set of subtrees. */
	public Tree(T value, Set<Tree<T>> children) {
		this.value = value;
		this.children = children;
	}
	@SafeVarargs
	public Tree(T value, Tree<T>... children) {
		this.value = value;
		this.children = new HashSet<>(Arrays.asList(children));
	}
	
	public int size() {
		int result = 1;
		
		if (this.children == null) return result;
		
		for (Tree<T> child : this.children) {
			result += child.size();
		}
		
		return result;
	}
    /** Whether the tree is a leaf */
	public boolean isLeaf() {
		return this.children.size() == 0;
	}
	public String toString() {
		return "{value: "+this.value+"; children: "+this.children+"}";
	}
	@Override
	public int hashCode() {
		return this.value.hashCode() ^ this.children.hashCode();
	}
	/** Two trees are equal if their values and subtrees are equal.*/
	@Override
	public boolean equals(Object obj) {
		if (!obj.getClass().equals(this.getClass())) return false;
		
		@SuppressWarnings("unchecked")
		Tree<T> other = (Tree<T>) obj;
		
		if (!this.value.equals(other.value)) return false;
		return this.children.equals(other.children);
	}
} public enum ConnectionPurpose {
	UNKNOWN(0),
    /** Used for connections that are used to transmit routing metadata. */
	ROUTING_DATA_EXCHANGE_CONNECTION(1),
    /** Used for user-requested connections that are routed. */
	ROUTED_CONNECTION(2);
	
	private static final Map<Integer, ConnectionPurpose> intToTypeMap = new HashMap<Integer, ConnectionPurpose>();
	static {
	    for (ConnectionPurpose type : ConnectionPurpose.values()) {
	        intToTypeMap.put(type.value, type);
	    }
	}
	
	private final int value;
	
	ConnectionPurpose(int value) {
		this.value = value;
	}
	public static ConnectionPurpose fromRaw(int value) {
		ConnectionPurpose result = intToTypeMap.get(value);
		if (result == null) result = ConnectionPurpose.UNKNOWN;
		return result;
	}
	public static ConnectionPurpose fromData(ByteBuffer data) {
		if (data.remaining() < 4) return ConnectionPurpose.UNKNOWN;
		
		int value = data.getInt();
		data.rewind();
		return ConnectionPurpose.fromRaw(value);
	}
	public int toRaw() {
		return this.value;
	}
} public class LinkHandshake implements Packet {
	public final static PacketType TYPE = PacketType.LINK_HANDHAKE;
	public final static int LENGTH = Constants.PACKET_TYPE_SIZE + Constants.UUID_SIZE + Constants.INT_SIZE;
	
	public final UUID peerIdentifier;
	public final ConnectionPurpose connectionPurpose;
	
	public LinkHandshake(UUID peerIdentifier, ConnectionPurpose connectionPurpose) {
		this.peerIdentifier = peerIdentifier;
		this.connectionPurpose = connectionPurpose;
	}
	
	public static LinkHandshake deserialize(ByteBuffer data) {
		DataReader reader = new DataReader(data);
		if (!DataChecker.check(reader, TYPE, LENGTH)) return null;
		
		UUID identifier = reader.getUUID();
		ConnectionPurpose purpose = ConnectionPurpose.fromRaw(reader.getInt());
		
		if (purpose == ConnectionPurpose.UNKNOWN) return null;
		
		return new LinkHandshake(identifier, purpose);
	}
	
	public ByteBuffer serialize() {
		DataWriter data = new DataWriter(LENGTH);
		data.add(TYPE);
		data.add(this.peerIdentifier);
		data.add(this.connectionPurpose.toRaw());
		return data.getData();
	}
} public class LinkStatePacket implements Packet {
	public final static PacketType TYPE = PacketType.LINK_STATE;
	public final static int MINIMUM_LENGTH = Constants.PACKET_TYPE_SIZE + Constants.UUID_SIZE + Constants.INT_SIZE;
	
    /** The identifier of the peer that generated the packet. */
	public final UUID peerIdentifier;
    /** A list of identifier/cost pairs for each of the peer's neighbors. */
	public final List<LinkStateRoutingTable.NeighborInformation<UUID>> neighbors;

	public LinkStatePacket(UUID peerIdentifier, List<LinkStateRoutingTable.NeighborInformation<UUID>> neighbors) {
		this.peerIdentifier = peerIdentifier;
		this.neighbors = neighbors;
	}
	
	public static LinkStatePacket deserialize(ByteBuffer data) {
		DataReader reader = new DataReader(data);
		if (!DataChecker.check(reader, TYPE, MINIMUM_LENGTH)) return null;
		
		UUID identifier = reader.getUUID();
		int neighborCount = reader.getInt();
		
		if (!reader.checkRemaining(neighborCount * (Constants.UUID_SIZE + Constants.INT_SIZE))) return null;
		
		ArrayList<LinkStateRoutingTable.NeighborInformation<UUID>> neighbors = new ArrayList<>();
		
		for (int i=0; i<neighborCount; i++) {
			neighbors.add(new LinkStateRoutingTable.NeighborInformation<UUID>(reader.getUUID(), reader.getInt()));
		}
		
		return new LinkStatePacket(identifier, neighbors);
	}
	public ByteBuffer serialize() {
		DataWriter data = new DataWriter(MINIMUM_LENGTH + this.neighbors.size() * (Constants.UUID_SIZE + Constants.INT_SIZE));
		data.add(TYPE);
		data.add(this.peerIdentifier);
		data.add(this.neighbors.size());
		
		for (LinkStateRoutingTable.NeighborInformation<UUID> neighbor : this.neighbors) {
			data.add(neighbor.node);
			data.add((int)neighbor.cost);
		}
		
		return data.getData();
	}
} public class MulticastHandshake implements Packet {
	public final static PacketType TYPE = PacketType.ROUTING_HANDSHAKE;
	public final static int MINIMUM_LENGTH = Constants.PACKET_TYPE_SIZE + Constants.UUID_SIZE;
	
	public final UUID sourcePeerIdentifier;
	public final Set<UUID> destinationIdentifiers;
	public final Tree<UUID> nextHopsTree;
	
	public MulticastHandshake(UUID sourcePeerIdentifier, Set<UUID> destinationIdentifiers, Tree<UUID> nextHopsTree) {
		if (destinationIdentifiers.size() == 0) throw new IllegalArgumentException("At least one destination is required.");
		
		this.sourcePeerIdentifier = sourcePeerIdentifier;
		this.destinationIdentifiers = destinationIdentifiers;
		this.nextHopsTree = nextHopsTree;
	}
	
	public static MulticastHandshake deserialize(ByteBuffer data) {
		DataReader reader = new DataReader(data);
		if (!DataChecker.check(reader, TYPE, MINIMUM_LENGTH)) return null;
		
		UUID sourcePeerIdentifier = reader.getUUID();
		int destinationsCount = reader.getInt();
		
		if (destinationsCount == 0) {
			System.err.println("Invalid MulticastHandshake: No destinations specified.");
			return null;
		}
		if (!reader.checkRemaining(destinationsCount * Constants.UUID_SIZE)) {
			System.err.println("Invalid MulticastHandshake: Not enough data remaining to read destinations.");
			return null;
		}
		
		Set<UUID> destinations = new HashSet<>();
		for (int i=0; i<destinationsCount; i++) {
			destinations.add(reader.getUUID());
		}
		
		Tree<UUID> nextHopsTree = deserializeNextHopTree(reader);
		
		return new MulticastHandshake(sourcePeerIdentifier, destinations, nextHopsTree);
	}
	public ByteBuffer serialize() {
		DataWriter data = new DataWriter(MINIMUM_LENGTH + Constants.INT_SIZE + destinationIdentifiers.size() * Constants.UUID_SIZE + nextHopsTree.size() * (Constants.INT_SIZE + Constants.UUID_SIZE));
		data.add(TYPE);
		data.add(this.sourcePeerIdentifier);
		data.add(destinationIdentifiers.size());
		for (UUID destinationIdentifier : destinationIdentifiers) data.add(destinationIdentifier);
		
		serializeNextHopTree(data, nextHopsTree);
		
		return data.getData();
	}
	
	private static Tree<UUID> deserializeNextHopTree(DataReader reader) {
		if (!reader.checkRemaining(Constants.UUID_SIZE + Constants.INT_SIZE)) {
			System.err.println("Invalid MulticastHandshake: Not enough data remaining to read hop tree.");
			return null;
		}
		
		UUID value = reader.getUUID();
		int childrenCount = reader.getInt();
		Set<Tree<UUID>> children = new HashSet<>();
		
		for (int i=0; i<childrenCount; i++) {
			Tree<UUID> child = deserializeNextHopTree(reader);
			if (child == null) return null;
			
			children.add(child);
		}
		
		return new Tree<>(value, children);
	}
	private void serializeNextHopTree(DataWriter data, Tree<UUID> tree) {
		data.add(tree.value);
		data.add(tree.children.size());
		
		for (Tree<UUID> child : tree.children) {
			serializeNextHopTree(data, child);
		}
	}
} public class RoutedConnectionEstablishedConfirmationPacket implements Packet {
	public final static PacketType TYPE = PacketType.ROUTED_CONNECTION_ESTABLISHED_CONFIRMATION;
	public final static int LENGTH = Constants.PACKET_TYPE_SIZE + Constants.UUID_SIZE;

	public final UUID source;
	
	public RoutedConnectionEstablishedConfirmationPacket(UUID source) {
		this.source = source;
	}

	public static RoutedConnectionEstablishedConfirmationPacket deserialize(ByteBuffer data) {
		DataReader reader = new DataReader(data);
		if (!DataChecker.check(reader, TYPE, LENGTH)) return null;
		
		return new RoutedConnectionEstablishedConfirmationPacket(reader.getUUID());
	}	
	public ByteBuffer serialize() {
		DataWriter data = new DataWriter(LENGTH);
		data.add(TYPE);
		data.add(this.source);
		return data.getData();
	}
} public class RepeatedExecutor {
	private final Runnable action;
	private final double regularDelay;
	private final double shortDelay;
	private final Executor executor;
	
	private boolean isStarted = false;
	private Timer timer;
	
	/**
	 * Constructs a new RepeatableExecutor.
	 * 
	 * @param action The action to execute.
	 * @param regularDelay The delay in which the action is executed by default.
	 * @param shortDelay The delay used when runActionInShortDelay is called.
	 * @param executor The executor to execute the action with.
	 * */
	public RepeatedExecutor(Runnable action, double regularDelay, double shortDelay, Executor executor) {
		this.action = action;
		this.regularDelay = regularDelay;
		this.shortDelay = shortDelay;
		this.executor = executor;
	}
	
	/**
	 * Starts executing the action in regular delays.
	 * */
	public void start() {
		if (this.isStarted) return;
		
		this.isStarted = true;
		this.resume();
	}
	/**
	 * Stops executing the action in regular delays.
	 * */
	public void stop() {
		if (!this.isStarted) return;
		
		this.isStarted = false;
		this.interrupt();
	}
	
	/**
	 * Runs the action immediately. Resets the timer; the next execution of the action will occur after the regular delay.
	 * */
	public void runActionNow() {
		this.resetTimer();
		this.executor.execute(new Runnable() {
			@Override
			public void run() {
				RepeatedExecutor.this.action.run();
			}
		});
	}
	/**
	 * Runs the action after the short delay. After this, actions are executed in regular intervals again.
	 * */
	public void runActionInShortDelay() {
		this.interrupt();
		this.timer = Timer.delay(this.shortDelay, this.executor, new Runnable() {
			@Override
			public void run() {
				RepeatedExecutor.this.action.run();
				RepeatedExecutor.this.resume();
			}
		});
	}

	private void interrupt() {
		if (this.timer != null) this.timer.stop();
		this.timer = null;
	}
	private void resume() {
		if (!this.isStarted) return;

		this.timer = Timer.repeat(this.regularDelay, this.executor, new Timer.Action() {
			@Override
			public void run(Timer timer, int executionCount) {
				RepeatedExecutor.this.action.run();
			}
		});
	}
	private void resetTimer() {
		this.interrupt();
		this.resume();
	}
}
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import de.tum.in.www1.jReto.module.api.Address;
import de.tum.in.www1.jReto.module.api.Connection;
import de.tum.in.www1.jReto.packet.PacketType;
import de.tum.in.www1.jReto.routing.algorithm.LinkStateRoutingTable;
import de.tum.in.www1.jReto.routing.algorithm.Tree;
import de.tum.in.www1.jReto.routing.packets.ConnectionPurpose;
import de.tum.in.www1.jReto.routing.packets.LinkHandshake;
import de.tum.in.www1.jReto.routing.packets.LinkStatePacket;
import de.tum.in.www1.jReto.routing.packets.MulticastHandshake;
import de.tum.in.www1.jReto.routing.packets.RoutedConnectionEstablishedConfirmationPacket;
import de.tum.in.www1.jReto.util.RepeatedExecutor;
package de.tum.in.www1.jReto.routing;



/**
* The Router class is responsible for discovering remote peers (represented via the Node class) in the network (both directly and indirectly reachable ones), 
* and establish connections to those peers.
*
* In this implementation, this involves the following tasks:
* 
* - Using of Reto Modules to discover direct neighbors and advertise the local peer (this is implemented in the DefaultRouter subclass).
* - Distribution of routing information using a flooding algorithm.
* - Building a link-state routing table based on that information.
* - Computing reachability information about other nodes when new routing information is received and informing the delegate about changes in reachabilty
* - Establishing connections to other peers, including routed connections
* - Acting as a router for other nodes, i.e. forwarding data from one peer to another, without handling the data on the local peer
* - Supporting multicast connections (computing routes, handling incoming underlying connections accordingly)
*/
public class Router {
	public static class BroadcastDelaySettings {
		public final double regularDelay;
		public final double shortDelay;
		
		public BroadcastDelaySettings(double regularDelay, double shortDelay) {
			this.regularDelay = regularDelay;
			this.shortDelay = shortDelay;
		}
	}
	
	public static interface RouterHandler {
		void onNodeFound(Router router, Node node);
		void onRouteImproved(Router router, Node node);
		void onNodeLost(Router router, Node node);
		void onConnection(Router router, Node node, Connection connection);
	}
	public static interface OnConnectionHandler {
		public void onConnect(Connection connection);
	}
	public static interface OnFailHandler {
		public void onFail();
	}
	
    /** The local peer's identifier */
	private final UUID identifier;
    /** The Executor used for networking related tasks. Delegate methods are also called on this Executor. */
	private final Executor executor;
    /** The Router's delegate. */
	private RouterHandler handler;

    /** A map from a node's UUID to the node for all Nodes known to the Router */
	private Map<UUID, Node> nodes = new HashMap<UUID, Node>();
    /** The set of Nodes that are neighbors of the local peer. */
	private Set<Node> neighbors = new HashSet<Node>();
    /** 
    * Forking connections act as a normal underling connection for the local peer, but forward received data to another peer in the background. 
    * This type of connection is used in multicast connections.
    * As the local peer may not hold a reference to the connection, it must be retained here.
    */
	private final Set<ForkingConnection> forwardingConnections = new HashSet<>();

    /** The linkStatePacketManager floods LinkStatePackets (i.e. routing information) through the network */
	private FloodingPacketManager linkStatePacketManager;
    /** Link state information packets are flooded periodically. The delayedLinkStateBroadcaster calls the appropriate methods in specified intervals. */
	private RepeatedExecutor delayedLinkStateBroadcaster;