// BOOTPSender.java	(C)  K. J. Turner, K. A. Whyte	08/03/06

// Boot Protocol sender (client)

package protocol;				// protocol package

import java.util.Enumeration;			// enumeration
import java.util.Vector;			// vector (list)
import support.*;				// protocol entity support

/**
  This is the class for a boot protocol sender.

  @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 (27th July 2010, KJT): unchecked warning suppressed
*/
public class BOOTPSender			// protocol sender (client)
 implements ProtocolEntity, Timeouts {		// protocol entity, timeout

  // simulator variables

  private ProtocolEntity peer;			// peer entity (client)
  private Medium medium;			// communications medium
  private String name;				// entity name

  // protocol variables

  private int transId;				// transaction identifier
  private String hwAddress = "hw";		// hardware address
  private String ipAddress = "net.91";		// IP address
  private String bootFile = "boot";		// boot file name
  private PDU pduSent;				// PDU sent
  private PDU pduReceived;			// PDU received
  private boolean timerEnabled;			// whether timer is enabled
  private Vector<ProtocolEvent> entityEvents;	// events from entity

  // protocol state

  int state;					// current protocol state

  final static int idle = 0;			// no request outstanding
  final static int wait = 1;			// waiting for reply

  // protocol messages

  final static String request = "REQUEST";	// boot request message
  final static String reply = "REPLY";		// boot reply message

  // protocol methods

  public BOOTPSender (Medium m, String name) {	// construct sender instance
    this.name = name;				// set protocol entity name
    medium = m;					// set underlying medium
    initialise ();				// initialise protocol
  }

  public String getName () {			// get protocol entity name
    return((name));				// return protocol entity name
  }

  public boolean hasTimer (String type) {	// protocol uses timer?
    return((true));				// report it does
  }

  public void initialise() {			// initialise protocol
    state = idle;				// initialise state
    pduReceived = null;				// initialise no PDU received
    pduSent = null;				// initialise no PDU sent
    timerEnabled = false;			// initialise no timeout
    entityEvents = new Vector<ProtocolEvent>();	// empty medium events
  }

  public void setPeer(ProtocolEntity peer) {	// set protocol peer
    this.peer = peer;				// set this entity's peer
  }

  /**
    Set the timer for a specified PDU.

    @param pdu		PDU
    @param enabled	whether the timer is enabled
  */
  public void setTimer(PDU pdu, boolean enabled) {
    timerEnabled = enabled;			// store timer status
  }

  public Vector<String> getServices() {
    Vector<String> events =			// list of events
      new Vector<String>();
    String pduType;				// received PDU type
    String pduData;				// PDU data
    String pduTrans;				// transaction identifier
    int transPos, hwPos;			// trans/hw address positions
    int trans;					// transaction identifier

    if (pduReceived != null) {			// PDU received?
      pduType = pduReceived.type;		// get received PDU type
      pduData = pduReceived.sdu;		// get received PDU data
      transPos = pduData.indexOf('(') + 1;	// get contents start
      hwPos = pduData.indexOf(',');		// get transaction end
      trans =					// get transaction identifier
	Integer.parseInt(pduData.substring(transPos, hwPos));
      if (pduType.equals(reply) &&		// reply message and ...
	  trans == transId) {			// same transaction identifier?
	timerEnabled = false;
	state = idle;				// no longer waiting
      }
    }
    if (state == idle) {			// not awaiting reply
      trans = 					// make random id 10..99
	10 + ((int) (BOOTPMedium.random() * 90));
      events.addElement(			// send request for IP/file
	request + "(" + trans + "," +
	  hwAddress + ") - send IP address and boot file path request");
      events.addElement(			// send request for file
	request + "(" + trans + "," +
	  hwAddress + "," + ipAddress +
	    ") - send boot file path request");
      events.addElement(			// send request for file path
	request + "(" + trans + "," +
	  hwAddress + "," + ipAddress + "," +
	    bootFile + ") - send path request for given boot file");
    }
    if (timerEnabled)
      events.addElement("Timeout - presume loss of message and resend");
    return(events);				// return list of events
  }

  public Vector<ProtocolEvent> performService(String s) {
    Vector<ProtocolEvent> events = 		// initialise events list
      new Vector<ProtocolEvent>();
    int start, middle, end;			// start/mid/end positions
    String pduData;				// PDU data

    if (s.startsWith(request)) {
      start = s.indexOf('(') + 1;		// get contents start
      middle = s.indexOf(',');			// get trans ident end
      end = s.indexOf(')' );			// get contents end
      transId =					// get transaction ident
	Integer.parseInt(s.substring(start, middle));
      pduData = s.substring(start, end);	// get SDU
      transmitPDU(				// send request
	new PDU(request, pduData), peer);
      events.addElement(			// transmit PDU
	new ProtocolEvent(ProtocolEvent.TRANSMIT, pduSent));
      state = wait;				// awaiting reply
    }
    if (s.startsWith("Timeout")) {		// timeout?
      transmitPDU(pduSent, peer);		// re-send PDU
      events.addElement(			// add timeout event and PDU
	new ProtocolEvent(ProtocolEvent.TIMEOUT, pduSent));
    }
    for (Enumeration e = entityEvents.elements(); // go through medium events
	 e.hasMoreElements(); )
      events.addElement(			// add medium event
	(ProtocolEvent) e.nextElement());
    return((events));				// return list of events
  }

  public Vector<ProtocolEvent> receivePDU(PDU pdu) { // handle received PDU
    pduReceived = pdu;				// store PDU
    return(new Vector<ProtocolEvent>());	// return no events
  }

  public void transmitPDU(			// transmit PDU
   PDU pdu, ProtocolEntity dest) {		// for given PDU, destination
    pdu.setSource(this);			// source is this entity
    pdu.setDestination(dest);			// destination is as given
    pduSent = pdu;				// copy PDU sent
    entityEvents = medium.receivePDU(pdu);	// medium receives PDU
    pduReceived = null;				// note no PDU in response
    timerEnabled = false;			// note no timeout
  }

}
