// TCPService.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 service entity.

  @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
*/
public class TCPService implements ProtocolEntity {

  /** Debug flag */
  private final static boolean DEBUG = false;

  // Service primitives (based on Halsall Figs 11.6 - 11.8):

  /** Active open message */
  public final static String ACTIVE_OPEN = "Active Open";

  /** Passive open message */
  public final static String PASSIVE_OPEN = "Passive Open";

  /** Open failure message */
  public final static String OPEN_FAILURE = "Open Failure";

  /** Open received message */
  public final static String OPEN_RECEIVED = "Open Received";

  /** Open success message */
  public final static String OPEN_SUCCESS = "Open Success";

  /** Send message */
  public final static String SEND = "Send";

  /** Deliver message */
  public final static String DELIVER = "Deliver";

  /** Close message */
  public final static String CLOSE = "Close";

  /** Closing message */
  public final static String CLOSING = "Closing";

  /** Closed message */
  public final static String CLOSED = "Closed";

  /** Abort message */
  public final static String ABORT = "Abort";

  /** Push message */
  public final static String PUSH = "(Push)";

  /** Open confirmed message (internal use) */
  public final static String OPEN_CONFIRMED = "Open Confirmed";

  // State constants

  /** Connected state */
  private final static int CONNECTED = 0;

  /** Calling state */
  private final static int CALLING = 1;

  /** Listening state */
  private final static int LISTENING = 2;

  /** Close sent state */
  private final static int CLOSE_SENT = 3;

  /** Disconnected state */
  private final static int DISCONNECTED = 4;

  /** Protocol for service */
  private ProtocolEntity provider;


  /** Events from provider */
  private Vector<ProtocolEvent> providerEvents;

  /** Service name */
  private String name;

  /** Service entity role */
  private int role;

  /** Protocol state */
  private int state;

  /** Request Push flag */
  private boolean push;

  /** Message size */
  private int messageSize;

  /**
    Constructor for a TCP service entity.

    @param name		service name
    @param role		service role
  */
  public TCPService(String name, int role) {
    this.name = name;
    this.role = role;
    initialise();
  }

  /**
    Return the TCP service entity name.

    @return		service name
  */
  public String getName() {
    return (name);
  }

  /**
    Return the services offered by the entity.

    @return		services offered
  */
  public Vector<String> getServices() {
    Vector<String> services = new Vector<String>();
    if (state == CONNECTED) {			// connected?
      if (!TCP.isSlowStart() ||			// not slow start and
	  name.equals("User A")) {		// user A?
	String service =			// set send service
	  SEND + " " + messageSize + " octets";
	if (push)				// push flag set?
	  service += " " + PUSH;		// append push
	services.addElement(service);		// add send service
      }
      if (!TCP.isSlowStart())			// not slow start?
	services.addElement(CLOSE);		// add close service
    }
    else if (state == DISCONNECTED) {
      if (role == TCP.SERVER)
	services.addElement(PASSIVE_OPEN);
      else
	services.addElement(ACTIVE_OPEN);
    }
    return (services);
  }

  /**
    Initialise service entity.
  */
  public void initialise() {
    providerEvents = new Vector<ProtocolEvent>();
    push = false;
    if (TCP.isSlowStart())			// slow start?
      setState(CONNECTED);			// start directly as connected
    else					// client-server/peer-peer
      setState(DISCONNECTED);			// start as disconnected
  }

  /**
    Perform entity service.

    @param service	service requested
    @return		resulting service events
  */
  public Vector<ProtocolEvent> performService(String service) {
    Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
    PDU pdu;
    switch (state) {
      case CONNECTED:
	if (service.startsWith(SEND)) {
	  String type = SEND + " (" + messageSize + ")";
	  if (service.indexOf(PUSH) > 0)
	    type += " " + PUSH;
	  pdu = new PDU(type);
	  pdu.size = messageSize;
	  transmitPDU(pdu, provider);
	  events.addElement(
	    new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu));
	}
	if (service.startsWith(CLOSE)) {
	  pdu = new PDU(CLOSE);
	  transmitPDU(pdu, provider);
	  events.addElement(
	    new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu));
	  setState(CLOSE_SENT);
	}
	break;
      case CALLING:
	break;
      case DISCONNECTED:
	if (service.equals(ACTIVE_OPEN)) {
	  pdu = new PDU(service);
	  transmitPDU(pdu, provider);
	  events.addElement(
	    new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu));
	  setState(CALLING);
	}
	if (service.equals(PASSIVE_OPEN)) {
	  pdu = new PDU(service);
	  transmitPDU(pdu, provider);
	  events.addElement(
	    new ProtocolEvent(ProtocolEvent.TRANSMIT, pdu));
	  setState(LISTENING);
	}
	break;
    }
    for (Enumeration enumeration = providerEvents.elements();
	 enumeration.hasMoreElements(); )
      events.addElement((ProtocolEvent) enumeration.nextElement());
    return (events);
  }

  /**
    Receive SDU.

    @param sdu		SDU
    @return		resulting service events
  */
  public Vector<ProtocolEvent> receivePDU(PDU sdu) {
    // respond to PDU received from underlying protocol
    Vector<ProtocolEvent> events = new Vector<ProtocolEvent>();
    String sduType = sdu.type;
    if (sduType.equals(OPEN_SUCCESS) || sduType.equals(OPEN_CONFIRMED))
      setState(CONNECTED);
    else if (sduType.equals(OPEN_FAILURE) || sduType.equals(CLOSED))
      setState(DISCONNECTED);
    return (events);
  }

  /**
    Sets the messageSize attribute of the TCPService object
   *
    @param size  The new messageSize value
   */
  public void setMessageSize(int size) {
    messageSize = size;
  }

  /**
    Set entity service provider

    @param provider	service provider
  */
  public void setProvider(ProtocolEntity provider) {
    this.provider = provider;
  }

  /**
    Set the Push attribute of a service entity.

    @param push		Push value
  */
  public void setPush(boolean push) {
    this.push = push;
  }

  /**
    Set the state attribute of a service entity.

    @param state	state
  */
  public void setState(int state) {
    if (DEBUG)
      System.err.println("state (" + name + "): " + state);
    this.state = state;
  }

  /**
    Transmit an SDU.

    @param sdu		SDU
    @param destination	destination
  */
  public void transmitPDU(PDU sdu, ProtocolEntity destination) {
    sdu.setSource(this);
    sdu.setDestination(destination);
    providerEvents = destination.receivePDU(sdu);
  }

}

