content: /*
   * Copyright 2011 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details.
   */
  package routing;
  
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Map;
  
  import routing.util.RoutingInfo;
  
  import core.Connection;
  import core.DTNHost;
  import core.Message;
  import core.Settings;
  import core.SimClock;
  
  /**
   * Epidemic-like message router making waves of messages.
   * Work in progress.
   */
  public class WaveRouter extends ActiveRouter {
  
  	/**
  	 * Immunity time -setting id ({@value}). Defines how long time a node
  	 * will reject incoming messages it has already received
  	 */
  	public static final String IMMUNITY_S = "immunityTime";
  	/**
  	 * Custody fraction -setting id ({@value}). Defines how long (compared to
  	 * immunity time) nodes accept custody for new incoming messages.
  	 */
  	public static final String CUSTODY_S = "custodyFraction";
  	private double immunityTime;
  	private double custodyFraction;
  	/** map of recently received messages and their receive times */
  	private Map<String, Double> recentMessages;
  	/** IDs of the messages this host has custody for */
  	private Map<String, Double> custodyMessages;
  
  	/**
  	 * Constructor. Creates a new message router based on the settings in
  	 * the given Settings object.
  	 * @param s The settings object
  	 */
  	public WaveRouter(Settings s) {
  		super(s);
  		this.immunityTime = s.getDouble(IMMUNITY_S);
  		this.custodyFraction = s.getDouble(CUSTODY_S);
  	}
  
  	/**
  	 * Copy constructor.
  	 * @param r The router prototype where setting values are copied from
  	 */
  	protected WaveRouter(WaveRouter r) {
  		super(r);
  		recentMessages = new HashMap<String, Double>();
  		this.immunityTime = r.immunityTime;
  		this.custodyFraction = r.custodyFraction;
  		this.custodyMessages = new HashMap<String, Double>();
  	}
  
  	@Override
  	protected int checkReceiving(Message m, DTNHost from) {
  		Double lastTime = this.recentMessages.get(m.getId());
  
  		if (lastTime != null) {
  			if (lastTime + this.immunityTime > SimClock.getTime()) {
  				return DENIED_POLICY; /* still immune to the message */
  			} else {
  				/* immunity has passed; remove from recent */
  				this.recentMessages.remove(m.getId());
  			}
  		}
  
  		/* no last time or immunity passed; receive based on other checks */
  		return super.checkReceiving(m, from);
  	}
  
  	/**
  	 * Returns the oldest message that has been already sent forward
  	 */
  	@Override
  	protected Message getNextMessageToRemove(boolean excludeMsgBeingSent) {
  		Collection<Message> messages = this.getMessageCollection();
  		Message oldest = null;
  
  		for (Message m : messages) {
  			Double custodyStartTime = this.custodyMessages.get(m.getId());
  			if (custodyStartTime != null) {
  				if (SimClock.getTime() >
  					custodyStartTime + immunityTime * custodyFraction) {
  					this.custodyMessages.remove(m.getId()); /* time passed */
  				} else {
  					continue; /* skip messages that still have custody */
  				}
  			}
  
  
  			if (excludeMsgBeingSent && isSending(m.getId())) {
  				continue; /* skip the message(s) that router is sending */
  			}
  
  			if (oldest == null ) {
  				oldest = m;
  			}
  			else if (oldest.getReceiveTime() > m.getReceiveTime()) {
  				oldest = m;
  			}
  		}
  
  		return oldest;
  	}
  
  	@Override
  	public void update() {
  		super.update();
  
  		if (isTransferring() || !canStartTransfer()) {
  			return; /* transferring, don't try other connections yet */
  		}
  
  		/* Try first the messages that can be delivered to final recipient */
  		if (exchangeDeliverableMessages() != null) {
  			return;
  		}
  		this.tryAllMessagesToAllConnections();
  	}
  
  	@Override
  	public Message messageTransferred(String id, DTNHost from) {
  		Message m = super.messageTransferred(id, from);
  		/* store received message IDs for immunity */
  		this.recentMessages.put(m.getId(), new Double(SimClock.getTime()));
  		this.custodyMessages.put(id, SimClock.getTime());
  		return m;
  	}
  
  	@Override
  	protected void transferDone(Connection con) {
  		/* remove from custody messages (if it was there) */
  		this.custodyMessages.remove(con.getMessage().getId());
  	}
  
  	@Override
  	public RoutingInfo getRoutingInfo() {
  		RoutingInfo ri = super.getRoutingInfo();
  		RoutingInfo immunity = new RoutingInfo("Immune to " +
  				this.recentMessages.size() + " messages");
  
  		for (String id : recentMessages.keySet()) {
  			RoutingInfo m = new RoutingInfo(id + " until " +
  					String.format("%.2f",
  							recentMessages.get(id) + this.immunityTime));
  			immunity.addMoreInfo(m);
  		}
  		ri.addMoreInfo(immunity);
  
  		return ri;
  	}
  
  	@Override
  	public WaveRouter replicate() {
  		return new WaveRouter(this);
  	}
  
  }
  