gt: 				return false; /* not time for the first scan */
context: /* 
   * Copyright 2010 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details. 
   */
  package core;
  
  import interfaces.ConnectivityGrid;
  import interfaces.ConnectivityOptimizer;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Random;
  
  import movement.DangerMovement;
  import routing.util.EnergyModel;
  import util.ActivenessHandler;
  
  /**
   * Network interface of a DTNHost. Takes care of connectivity among hosts.
   */
  abstract public class NetworkInterface implements ModuleCommunicationListener {
  	/** transmit range -setting id ({@value})*/
  	public static final String TRANSMIT_RANGE_S = "transmitRange";
  	/** transmit speed -setting id ({@value})*/
  	public static final String TRANSMIT_SPEED_S = "transmitSpeed";
  	/** scanning interval -setting id ({@value})*/
  	public static final String SCAN_INTERVAL_S = "scanInterval";
  	
  	/** 
  	 * Sub-namespace for the network related settings in the Group namespace
  	 * ({@value}) 
  	 */
  	public static final String NET_SUB_NS = "net";
  	
  	/** Activeness offset jitter -setting id ({@value})
  	 * The maximum amount of random offset for the offset */
  	public static final String ACT_JITTER_S = "activenessOffsetJitter";
  	
  	/** {@link ModuleCommunicationBus} identifier for the "scanning interval" 
      variable. */
  	public static final String SCAN_INTERVAL_ID = "Network.scanInterval";
  	/** {@link ModuleCommunicationBus} identifier for the "radio range" 
  	variable. Value type: double */
  	public static final String RANGE_ID = "Network.radioRange";
  	/** {@link ModuleCommunicationBus} identifier for the "transmission speed" 
      variable. Value type: integer */
  	public static final String SPEED_ID = "Network.speed";
  	
  	private static final int CON_UP = 1;
  	private static final int CON_DOWN = 2;
  
  	private static Random rng;
  	protected DTNHost host = null;
  
  	protected String interfacetype;
  	protected List<Connection> connections; // connected hosts
  	private List<ConnectionListener> cListeners = null; // list of listeners
  	private int address; // network interface address
  	protected double transmitRange;
  	protected double oldTransmitRange;
  	protected int transmitSpeed;
  	protected ConnectivityOptimizer optimizer = null;
  	/** scanning interval, or 0.0 if n/a */
  	private double scanInterval;
  	private double lastScanTime;
  	
  	/** activeness handler for the node group */
  	private ActivenessHandler ah;
  	/** maximum activeness jitter value for the node group */
  	private int activenessJitterMax;
  	/** this interface's activeness jitter value */
  	private int activenessJitterValue;
  
  	static {
  		DTNSim.registerForReset(NetworkInterface.class.getCanonicalName());
  		reset();
  	}
  	
  	/**
  	 * Resets the static fields of the class
  	 */
  	public static void reset() {
  		rng = new Random(0);
  	}
  	
  	/**
  	 * For creating an empty class of a specific type
  	 */
  	public NetworkInterface(Settings s) {
  		this.interfacetype = s.getNameSpace();
  		this.connections = new ArrayList<Connection>();
  
  		this.transmitRange = s.getDouble(TRANSMIT_RANGE_S);
  		this.transmitSpeed = s.getInt(TRANSMIT_SPEED_S);
  		ensurePositiveValue(transmitRange, TRANSMIT_RANGE_S);
  		ensurePositiveValue(transmitSpeed, TRANSMIT_SPEED_S);
  	}
  	
  	/**
  	 * For creating an empty class of a specific type
  	 */
  	public NetworkInterface() {
  		this.interfacetype = "Default";
  		this.connections = new ArrayList<Connection>();
  	}
  	
  	/**
  	 * copy constructor
  	 */
  	public NetworkInterface(NetworkInterface ni) {
  		this.connections = new ArrayList<Connection>();
  		this.host = ni.host;
  		this.cListeners = ni.cListeners;
  		this.interfacetype = ni.interfacetype;
  		this.transmitRange = ni.transmitRange;
  		this.transmitSpeed = ni.transmitSpeed;
  		this.scanInterval = ni.scanInterval;
  		this.ah = ni.ah;
  		
  		if (ni.activenessJitterMax > 0) {
  			this.activenessJitterValue = rng.nextInt(ni.activenessJitterMax);
  		} else {
  			this.activenessJitterValue = 0;
  		}
  		
  		this.scanInterval = ni.scanInterval;
  		/* draw lastScanTime of [0 -- scanInterval] */
  		this.lastScanTime = rng.nextDouble() * this.scanInterval;
  	}
  
  	/**
  	 * Replication function
  	 */
  	abstract public NetworkInterface replicate();
  
  	/**
  	 * For setting the host - needed when a prototype is copied for several
  	 * hosts
  	 * @param host The host where the network interface is
  	 */
  	public void setHost(DTNHost host) {
  		this.host = host;
  		ModuleCommunicationBus comBus = host.getComBus();
  		
  		if (!comBus.containsProperty(SCAN_INTERVAL_ID)) {
  			/* add properties and subscriptions only for the 1st interface */
  			/* TODO: support for multiple interfaces */
  			comBus.addProperty(SCAN_INTERVAL_ID, this.scanInterval);
  			comBus.addProperty(RANGE_ID, this.transmitRange);
  			comBus.addProperty(SPEED_ID, this.transmitSpeed);
  			comBus.subscribe(SCAN_INTERVAL_ID, this);
  			comBus.subscribe(RANGE_ID, this);
  			comBus.subscribe(SPEED_ID, this);
  		}
  		
  		if (transmitRange > 0) {
  			optimizer = ConnectivityGrid.ConnectivityGridFactory(
  					this.interfacetype.hashCode(), transmitRange);
  			optimizer.addInterface(this);
  		} else {
  			optimizer = null;
  		}
  	}
  	
  	/**
  	 * Sets group-based settings for the network interface
  	 * @param s The settings object using the right group namespace
  	 */
  	public void setGroupSettings(Settings s) {
  		s.setSubNameSpace(NET_SUB_NS);	
  		ah = new ActivenessHandler(s);
  		
  		if (s.contains(SCAN_INTERVAL_S)) {
  			this.scanInterval =  s.getDouble(SCAN_INTERVAL_S);
  		} else {
  			this.scanInterval = 0;
  		}
  		if (s.contains(ACT_JITTER_S)) {
  			this.activenessJitterMax = s.getInt(ACT_JITTER_S);
  		}
  		
  		s.restoreSubNameSpace();
  	}
  
  	/**
  	 * For checking what interface type this interface is
  	 */
  	public String getInterfaceType() {
  		return interfacetype;
  	}
  	
  	/**
  	 * For setting the connectionListeners
  	 * @param cListeners List of connection listeners
  	 */
  	public void setClisteners(List<ConnectionListener> cListeners) {
  		this.cListeners = cListeners;
  	}
  
  	/**
  	 * Returns the transmit range of this network layer
  	 * @return the transmit range
  	 */
  	public double getTransmitRange() {
  		return this.transmitRange;
  	}
  
  	/**
  	 * Returns the transmit speed of this network layer
  	 * @return the transmit speed
  	 */
  	public int getTransmitSpeed() {
  		return this.transmitSpeed;
  	}
  
  	/**
  	 * Returns a list of currently connected connections
  	 * @return a list of currently connected connections
  	 */
  	public List<Connection> getConnections() {
  		return this.connections;
  	}
  	
  	/**
  	 * Returns true if the interface is on at the moment (false if not)
  	 * @return true if the interface is on at the moment (false if not)
  	 */
  	public boolean isActive() {
  		boolean active = ah.isActive(this.activenessJitterValue);
  		if (host.getDangerMode() == DangerMovement.EVAC_MODE) {
  			active = false;
  		}
  		if (active && host.getComBus().getDouble(EnergyModel.ENERGY_VALUE_ID,
  					1) <= 0) {
  			/* TODO: better way to check battery level */
  			/* no battery -> inactive */
  			active = false;
  		}
  		
  		if (active == false && this.transmitRange > 0) {
  			/* not active -> make range 0 */
  			this.oldTransmitRange = this.transmitRange;
  			host.getComBus().updateProperty(RANGE_ID, 0.0);
  		} else if (active == true && this.transmitRange == 0.0) {
  			/* active, but range == 0 -> restore range  */
  			host.getComBus().updateProperty(RANGE_ID, 
  					this.oldTransmitRange);
  		}		
  		return active;
  	}
  	
  	/**
  	 * Checks if this interface is currently in the scanning mode
  	 * @return True if the interface is scanning; false if not
  	 */
  	public boolean isScanning() {
  		double simTime = SimClock.getTime();
  		
  		if (!isActive()) {
  			return false;
  		}
  		
  		if (scanInterval > 0.0) {
  			if (simTime < lastScanTime) {
  