// CSMAService.java

package protocol;				// protocol package

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

import support.*;				// import Jasper support classes

/**
  This is the class that supports sources/sinks for (de)multiplexing.

  @author	Kenneth J. Turner
  @version	1.0 (24th July 2010, KJT): initial version
*/

public class CSMAService implements ProtocolEntity {

  /** Service send offer */
  private final static String SEND = "Send data to ";

  /** Service message count */
  private static int messageCount;

  /** PDU sent by service */
  private PDU sduSent;

  /** Service entity name */
  private String serviceName;

  /** Service provider */
  private CSMAProtocol serviceProvider;

  /** Service provider events */
  private Vector<ProtocolEvent> serviceEvents;

  /**
    Constructor for sources/sinks.

    @param serviceName	service name
  */
  public CSMAService(String serviceName) {
    this.serviceName = serviceName;		// set service name
    initialise();				// initialise service entity
  }

  /**
    Return a protocol event with the given comment.

    @param comment	protocol comment
    @return		protocol event
  */
  public ProtocolEvent comment(String comment) {
    return(new ProtocolEvent(ProtocolEvent.COMMENT, this, comment));
  }

  /**
    Return a protocol event with an empty comment as a gap.

    @return		protocol event
  */
  public ProtocolEvent gap() {
    return(new ProtocolEvent(ProtocolEvent.COMMENT, this, ""));
  }

  /**
    Return the service name.

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

 /**
    Return services currently offered.

    @return		list of services
  */
  public Vector<String> getServices() {
    Vector<String> list = new Vector<String>();	// initialise service list
    CSMAProtocol remoteProvider =		// get remote service provider
      serviceProvider.protocolPeer;
    CSMAMedium protocolMedium =			// get protocol medium
      serviceProvider.protocolMedium;
    String providerName = 			// get provider name
      serviceProvider.protocolName;
    // System.err.println("CSMAService.getServices - " + serviceName +
    //   ": in <" + serviceProvider.protocolIn + "> out <" +
    //   serviceProvider.protocolOut + ">");
    boolean localIdle =				// check if local protocol idle
      serviceProvider.protocolIn == null &&	// no incoming data pending?
      serviceProvider.protocolOut == null &&	// no outgoing data pending?
      !serviceProvider.protocolReceiving &&	// not receiving data?
      protocolMedium.isEmpty(providerName);	// no outgoing PDUs in transit?
    boolean remoteIdle =			// check if remote protocol idle
      remoteProvider.protocolIn == null &&	// no incoming data pending and
      remoteProvider.protocolState == CSMAProtocol.NORMAL; // no collision?
      // System.err.println("CSMAService.getServices - localIdle <" +
      //   localIdle + "> remoteIdle <" + remoteIdle + ">\n");
    if (localIdle && remoteIdle) {		// local and remote idle?
      String name =				// get peer system name
	serviceProvider.protocolPeer.protocolUser.getName();
      list.addElement(SEND + name);		// add send to list
    }
    return(list);				// return service list
  }

  /**
    Initialise the service entity.
  */
  public void initialise() {
    messageCount = 0;				// initialise message count
    serviceEvents = new Vector<ProtocolEvent>();// initialise service events
  }

  /**
    Perform service.

    @param service	service request
    @return		resulting service events
  */
  public Vector<ProtocolEvent> performService(String service) {
    serviceEvents = new Vector<ProtocolEvent>();// initialise service events
    if (service.startsWith(SEND)) {		// send request?
      String message = "D" + messageCount;	// create data message
      PDU sdu =					// create SDU
	new CSMASdu(CSMASdu.DATA, message);
      serviceEvents.addElement(			// add send event
	new ProtocolEvent(ProtocolEvent.SEND, sdu));
      transmitPDU(sdu, serviceProvider);	// send service message
      messageCount++;				// increment message count
    }
    return(serviceEvents);			// return service events
  }

  /**
    Receive an SDU.

    @param sdu		SDU
    @return		resulting service events
  */
  public Vector<ProtocolEvent> receivePDU(PDU sdu) {
    serviceEvents = new Vector<ProtocolEvent>();// initialise service events
    serviceEvents.addElement(			// deliver SDU
      new ProtocolEvent(ProtocolEvent.DELIVER, sdu));
    serviceEvents.addElement(gap());		// add a vertical gap

    return(serviceEvents);			// return service events
  }

  /**
    Set the local service provider.

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

  /**
    Send an SDU.

    @param sdu		SDU
    @param dest		destination protocol entity
  */
  public void transmitPDU(PDU sdu, ProtocolEntity destination) {
    sdu.setSource(this);			// set source as this entity
    sdu.setDestination(destination);		// set destination
    Vector<ProtocolEvent> receiveEvents =	// receive SDU at destination
      destination.receivePDU(sdu);
    for (int i = 0; i < receiveEvents.size(); i++) // go through receive events
      serviceEvents.addElement(			// append receive event
	receiveEvents.get(i));
    sduSent = sdu;				// stored the sent SDU
  }

}

