// ABPSender.java

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 the alternating bit 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): minor tidying
*/
public class ABPSender implements ProtocolEntity, Timeouts {

  private PDU pduBeingSent;
  private PDU pduReceived;
  private ProtocolEntity peer;
  private Medium medium;
  private String name;
  private boolean timerEnabled;
  private Vector<ProtocolEvent> entityEvents;		// events from entity

  public ABPSender(Medium m, String name) {
    this.name = name;
    medium = m;
    initialise();
  }

  public String getName() {
    return(name);
  }

  public Vector<String> getServices() {
    Vector<String> list = new Vector<String>();
    if (pduReceived != null && pduReceived.type.equals("ACK")) {
      list.addElement("Send DATA(" + pduReceived.seq + ")");
      timerEnabled = false;
    }
    if (timerEnabled)
      list.addElement("Timeout - presume loss of message and resend");
    return(list);
  }

  public boolean hasTimer(String type) {
    return(true);
  }

 /** Increment PDU sequence number (0 or 1 only in ABP protocol) */

  private int inc(int seq) {
    return(1 - seq);
  }

  public void initialise() {
    // dummy initial data PDU
    pduBeingSent = new PDU("DATA", 1);
    pduBeingSent.setSource(this);
    // dummy initial ack PDU
    pduReceived = new PDU("ACK", 0);
    timerEnabled = false;
    entityEvents = new Vector<ProtocolEvent>();		// empty medium events
  }

  public Vector<ProtocolEvent> performService(String s) {
    Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
    if (s.startsWith("Send DATA")) {
      // PDU seq number delimited by brackets:
      int startIndex = s.indexOf( '(' ) + 1;
      int endIndex = s.indexOf( ')' );
      int seq = Integer.parseInt(s.substring(startIndex, endIndex));
      transmitPDU(new PDU("DATA", seq), peer);
      events.addElement(
	new ProtocolEvent(ProtocolEvent.TRANSMIT, pduBeingSent));
    }
    if (s.startsWith("Timeout")) {
      transmitPDU(pduBeingSent, peer);
      events.addElement(
	new ProtocolEvent(ProtocolEvent.TIMEOUT, pduBeingSent));
    }
    for (Enumeration e = entityEvents.elements();	// get medium events
	 e.hasMoreElements(); )
      events.addElement(				// add medium event
	(ProtocolEvent) e.nextElement());
    return(events);
  }

  public Vector<ProtocolEvent> receivePDU(PDU pdu) {
    pduReceived = pdu;
    return(new Vector<ProtocolEvent>());
  }

  public void setPeer(ProtocolEntity peer) {
    this.peer = 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;
  }

  public void transmitPDU(PDU pdu, ProtocolEntity dest) {
    pdu.setSource(this);
    pdu.setDestination(dest);
    pduBeingSent = pdu;
    entityEvents = medium.receivePDU(pdu);	// medium receives PDU
    pduReceived = null;
    timerEnabled = false;
  }

}
