// TCPMedium.java

package protocol;				// protocol package

import java.util.*;				// import Java utility classes

import support.*;				// import Jasper support classes

/**
  This is the class for a TCP medium.

  @author	Iain A. Robin, Kenneth J. Turner
  @version	1.0 (1st September 1999, IAR): initial version
  <br/>		1.4 (9th March 2006, KJT): updated for JDK 1.5
  <br/>		1.5 (26th July 2010, KJT): minor tidying; a loss rate has been
		  introduced for medium messages
*/
public class TCPMedium extends Medium {

  /** Message loss rate (0.2 means 20% of messages are lost) */
  private float lossRate = 0.2f;

  /** Random number index */
  private int randomIndex;

  /** Random numbers */
  private Vector<Float> randomNumbers;

  /**
    Constructor for a TCP medium.
  */
  public TCPMedium() {
    super();					// construct superclass
    randomNumbers = new Vector<Float>();	// initialise random numbers
  }

  /**
    Initialise the medium.
  */
  public void initialise() {
    super.initialise();
    randomIndex = 0;				// initialise random index
  }

  /**
    Gets the matching PDU.

    @param description		PDU description
    @return			matching PDU
  */
  protected PDU getMatchingPDU(String description) {
    // identify and return PDU currently on channel with
    // type and parameters matching those described in given string
    TCPMessage tcpdu;
    // try to match PDU type and seq with a PDU currently on channel:
    for (Enumeration enumeration = pdus.elements();
	 enumeration.hasMoreElements(); ) {
      tcpdu = (TCPMessage) enumeration.nextElement();
      if (description.indexOf(tcpdu.getID()) > 0)
	return (tcpdu);
    }
    return (null);				// no match found
  }

  /**
    Return a list of strings describing actions (services) that can be carried
    out in the current state.

    @return			service descriptions
  */
  public Vector<String> getServices() {
    Vector<String> list = new Vector<String>();	// initialise service list
    HashSet<String> pdusSent =			// initialise list of sources
      new HashSet<String>();
    for (Enumeration enumeration = pdus.elements(); // go through PDUs
	 enumeration.hasMoreElements(); ) {
      PDU pdu = (PDU) enumeration.nextElement();// get next PDU
      if (pdu != null) {			// non-null PDU?
	String name = pdu.getSource().getName();// get PDU source name
	String description =			// get PDU id and name
	  pdu.getID() + " [" + name + "]";
	if (!pdusSent.contains(name)) {		// no PDU with this source?
	  pdusSent.add(name);			// add source to list
	  list.addElement(			// add delivery service
	    "Deliver " + description + " - no loss");
	}
	if (random() < lossRate)		// loss is possible?
	  list.addElement(			// add loss service
	    "Lose " + description + " - congestion/error");
      }
    }
    return(list);				// return service list
  }

  /**
    Return a random float. If the list of randoms contains an unused value then
    use this, otherwise generate a fresh value and add to the list. This allows
    random numbers to be generated consistently when simulation steps are
    undone/redone.

    @return		random number in the range (0..1]
  */
  private float random() {
    if (randomIndex < randomNumbers.size())	// index within list?
      return(					// return this random
	((Float)randomNumbers.elementAt(randomIndex++)).floatValue());
    else {					// index not within list
      Float random = new Float(Math.random());	// get random number
      randomNumbers.addElement(random);		// add random to list
      randomIndex++;				// increment list index
      return(random.floatValue());		// return rand number
    }
  }

  /**
    Set the medium loss rate.

    @param lossRate	loss rate
  */
  public void setLossRate(float lossRate) {
    this.lossRate = lossRate;			// set loss rate
  }

}

