gt: 		dy = (possibleMovement/distance) * (this.destination.getY() -
context: /* 
   * Copyright 2010 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details. 
   */
  package core;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.List;
  
  import movement.MovementModel;
  import movement.Path;
  import routing.MessageRouter;
  import routing.RoutingInfo;
  
  /**
   * A DTN capable host.
   */
  public class DTNHost implements Comparable<DTNHost> {
  	private static int nextAddress = 0;
  	private int address;
  
  	private Coord location; 	// where is the host
  	private Coord destination;	// where is it going
  
  	private MessageRouter router;
  	private MovementModel movement;
  	private Path path;
  	private double speed;
  	private double nextTimeToMove;
  	private String name;
  	private List<MessageListener> msgListeners;
  	private List<MovementListener> movListeners;
  	private List<NetworkInterface> net;
  	private ModuleCommunicationBus comBus;
  
  	static {
  		DTNSim.registerForReset(DTNHost.class.getCanonicalName());
  		reset();
  	}
  	/**
  	 * Creates a new DTNHost.
  	 * @param msgLs Message listeners
  	 * @param movLs Movement listeners
  	 * @param groupId GroupID of this host
  	 * @param interf List of NetworkInterfaces for the class
  	 * @param comBus Module communication bus object
  	 * @param mmProto Prototype of the movement model of this host
  	 * @param mRouterProto Prototype of the message router of this host
  	 */
  	public DTNHost(List<MessageListener> msgLs,
  			List<MovementListener> movLs,
  			String groupId, List<NetworkInterface> interf,
  			ModuleCommunicationBus comBus, 
  			MovementModel mmProto, MessageRouter mRouterProto) {
  		this.comBus = comBus;
  		this.location = new Coord(0,0);
  		this.address = getNextAddress();
  		this.name = groupId+address;
  		this.net = new ArrayList<NetworkInterface>();
  
  		for (NetworkInterface i : interf) {
  			NetworkInterface ni = i.replicate();
  			ni.setHost(this);
  			net.add(ni);
  		}	
  
  		// TODO - think about the names of the interfaces and the nodes
  		//this.name = groupId + ((NetworkInterface)net.get(1)).getAddress();
  
  		this.msgListeners = msgLs;
  		this.movListeners = movLs;
  
  		// create instances by replicating the prototypes
  		this.movement = mmProto.replicate();
  		this.movement.setComBus(comBus);
  		setRouter(mRouterProto.replicate());
  
  		this.location = movement.getInitialLocation();
  
  		this.nextTimeToMove = movement.nextPathAvailable();
  		this.path = null;
  
  		if (movLs != null) { // inform movement listeners about the location
  			for (MovementListener l : movLs) {
  				l.initialLocation(this, this.location);
  			}
  		}
  	}
  	
  	/**
  	 * Returns a new network interface address and increments the address for
  	 * subsequent calls.
  	 * @return The next address.
  	 */
  	private synchronized static int getNextAddress() {
  		return nextAddress++;	
  	}
  
  	/**
  	 * Reset the host and its interfaces
  	 */
  	public static void reset() {
  		nextAddress = 0;
  	}
  
  	/**
  	 * Returns true if this node is active (false if not)
  	 * @return true if this node is active (false if not)
  	 */
  	public boolean isActive() {
  		return this.movement.isActive();
  	}
  
  	/**
  	 * Set a router for this host
  	 * @param router The router to set
  	 */
  	private void setRouter(MessageRouter router) {
  		router.init(this, msgListeners);
  		this.router = router;
  	}
  
  	/**
  	 * Returns the router of this host
  	 * @return the router of this host
  	 */
  	public MessageRouter getRouter() {
  		return this.router;
  	}
  
  	/**
  	 * Returns the network-layer address of this host.
  	 */
  	public int getAddress() {
  		return this.address;
  	}
  	
  	/**
  	 * Returns this hosts's ModuleCommunicationBus
  	 * @return this hosts's ModuleCommunicationBus
  	 */
  	public ModuleCommunicationBus getComBus() {
  		return this.comBus;
  	}
  	
      /**
  	 * Informs the router of this host about state change in a connection
  	 * object.
  	 * @param con  The connection object whose state changed
  	 */
  	public void connectionUp(Connection con) {
  		this.router.changedConnection(con);
  	}
  
  	public void connectionDown(Connection con) {
  		this.router.changedConnection(con);
  	}
  
  	/**
  	 * Returns a copy of the list of connections this host has with other hosts
  	 * @return a copy of the list of connections this host has with other hosts
  	 */
  	public List<Connection> getConnections() {
  		List<Connection> lc = new ArrayList<Connection>();
  
  		for (NetworkInterface i : net) {
  			lc.addAll(i.getConnections());
  		}
  
  		return lc;
  	}
  
  	/**
  	 * Returns the current location of this host. 
  	 * @return The location
  	 */
  	public Coord getLocation() {
  		return this.location;
  	}
  
  	/**
  	 * Returns the Path this node is currently traveling or null if no
  	 * path is in use at the moment.
  	 * @return The path this node is traveling
  	 */
  	public Path getPath() {
  		return this.path;
  	}
  
  
  	/**
  	 * Sets the Node's location overriding any location set by movement model
  	 * @param location The location to set
  	 */
  	public void setLocation(Coord location) {
  		this.location = location.clone();
  	}
  
  	/**
  	 * Sets the Node's name overriding the default name (groupId + netAddress)
  	 * @param name The name to set
  	 */
  	public void setName(String name) {
  		this.name = name;
  	}
  
  	/**
  	 * Returns the messages in a collection.
  	 * @return Messages in a collection
  	 */
  	public Collection<Message> getMessageCollection() {
  		return this.router.getMessageCollection();
  	}
  
  	/**
  	 * Returns the number of messages this node is carrying.
  	 * @return How many messages the node is carrying currently.
  	 */
  	public int getNrofMessages() {
  		return this.router.getNrofMessages();
  	}
  
  	/**
  	 * Returns the buffer occupancy percentage. Occupancy is 0 for empty
  	 * buffer but can be over 100 if a created message is bigger than buffer 
  	 * space that could be freed.
  	 * @return Buffer occupancy percentage
  	 */
  	public double getBufferOccupancy() {
  		double bSize = router.getBufferSize();
  		double freeBuffer = router.getFreeBufferSize();
  		return 100*((bSize-freeBuffer)/bSize);
  	}
  
  	/**
  	 * Returns routing info of this host's router.
  	 * @return The routing info.
  	 */
  	public RoutingInfo getRoutingInfo() {
  		return this.router.getRoutingInfo();
  	}
  
  	/**
  	 * Returns the interface objects of the node
  	 */
  	public List<NetworkInterface> getInterfaces() {
  		return net;
  	}
  
  	/**
  	 * Find the network interface based on the index
  	 */
  	protected NetworkInterface getInterface(int interfaceNo) {
  		NetworkInterface ni = null;
  		try {
  			ni = net.get(interfaceNo-1);
  		} catch (IndexOutOfBoundsException ex) {
  			System.out.println("No such interface: "+interfaceNo);
  			System.exit(0);
  		}
  		return ni;
  	}
  
  	/**
  	 * Find the network interface based on the interfacetype
  	 */
  	protected NetworkInterface getInterface(String interfacetype) {
  		for (NetworkInterface ni : net) {
  			if (ni.getInterfaceType().equals(interfacetype)) {
  				return ni;
  			}
  		}
  		return null;	
  	}
  
  	/**
  	 * Force a connection event
  	 */
  	public void forceConnection(DTNHost anotherHost, String interfaceId, 
  			boolean up) {
  		NetworkInterface ni;
  		NetworkInterface no;
  
  		if (interfaceId != null) {
  			ni = getInterface(interfaceId);
  			no = anotherHost.getInterface(interfaceId);
  
  			assert (ni != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
  			assert (no != null) : "Tried to use a nonexisting interfacetype "+interfaceId;
  		} else {
  			ni = getInterface(1);
  			no = anotherHost.getInterface(1);
  			
  			assert (ni.getInterfaceType().equals(no.getInterfaceType())) : 
  				"Interface types do not match.  Please specify interface type explicitly";
  		}
  		
  		if (up) {
  			ni.createConnection(no);
  		} else {
  			ni.destroyConnection(no);
  		}
  	}
  
  	/**
  	 * for tests only --- do not use!!!
  	 */
  	public void connect(DTNHost h) {
  		System.err.println(
  				"WARNING: using deprecated DTNHost.connect(DTNHost)" +
  		"\n Use DTNHost.forceConnection(DTNHost,null,true) instead");
  		forceConnection(h,null,true);
  	}
  
  	/**
  	 * Updates node's network layer and router.
  	 * @param simulateConnections Should network layer be updated too
  	 */
  	public void update(boolean simulateConnections) {
  		if (!isActive()) {
  			return;
  		}
  		
  		if (simulateConnections) {
  			for (NetworkInterface i : net) {
  				i.update();
  			}
  		}
  		this.router.update();
  	}
  
  	/**
  	 * Moves the node towards the next waypoint or waits if it is
  	 * not time to move yet
  	 * @param timeIncrement How long time the node moves
  	 */
  	public void move(double timeIncrement) {		
  		double possibleMovement;
  		double distance;
  		double dx, dy;
  
  		if (!isActive() || SimClock.getTime() < this.nextTimeToMove) {
  			return; 
  		}
  		if (this.destination == null) {
  			if (!setNextWaypoint()) {
  				return;
  			}
  		}
  
  		possibleMovement = timeIncrement * speed;
  		distance = this.location.distance(this.destination);
  
  		while (possibleMovement >= distance) {
  			// node can move past its next destination
  			this.location.setLocation(this.destination); // snap to destination
  			possibleMovement -= distance;
  			if (!setNextWaypoint()) { // get a new waypoint
  				return; // no more waypoints left
  			}
  			distance = this.location.distance(this.destination);
  		}
  
  		// move towards the point for possibleMovement amount
  		dx = (possibleMovement/distance) * (this.destination.getX() -
  				this.location.getX());
  