// MCASTSource.java

package protocol;				// protocol package

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

import support.*;				// import Jasper support classes

/**
  This is the class that supports the source for broad/uni/multicast.

  @author	Kenneth J. Turner
  @version	1.0 (24th July 2010, KJT): initial version
*/
public class MCASTSource implements ProtocolEntity {

  /** Service broadcast send offer */
  private final static String SEND_BROADCAST =
    "Broadcast message for routers B, D, E, F";

  /** Service multicast send offer */
  private final static String SEND_MULTICAST =
    "Multicast message for routers B, D, E";

  /** Service unicast send offer */
  private final static String SEND_UNICAST =
    "Unicast message for routers B, D, E";

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

  /** Service data unit output */
  private PDU sdu;

  /** Service data unit destination letters */
  private String serviceDestinations;

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

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

  /**
    Constructor for sources/sinks.

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

  /**
    Check if the service destinations have received an SDU. If so, re-initialise
    these destinations.

    @return		true/false if all service destinations have/have not
			received an SDU
  */
  public boolean checkDestinations() {
    boolean result = false;			// initialise result
    int sduCount = 0;				// initialise SDU count
    int length = serviceDestinations.length();	// get number of destinations
    for (int i = 0; i < length; i++) {		// go through destinations
      Vector<MCASTSdu> sdus =			// get SDUs for destination
	MCAST.getSDUs(serviceDestinations.charAt(i));
      if (sdus != null && sdus.size() > 0)	// destination has SDUs?
	sduCount++;				// increment SDU count
    }
    if (sduCount == length) {			// all destinations have SDUs?
      result = true;				// note true result
      for (int i = 0; i < length; i++) {	// go through destinations
	ProtocolEntity entity =			// get destination entity
	  MCAST.getEntity(serviceDestinations.charAt(i));
	entity.initialise();			// re-initialise entity
      }
   }
    return(result);				// return check result
  }

  /**
    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
    if (sdu == null || checkDestinations()) {	// no SDU sent or all received?
      if (MCAST.serviceMode == MCAST.BROADCAST)	// broadcast?
	list.addElement(SEND_BROADCAST);	// add broadcast send to list
      else if (MCAST.serviceMode == MCAST.MULTICAST) // multicast?
	list.addElement(SEND_MULTICAST);	// add multicast send to list
      else if (MCAST.serviceMode == MCAST.UNICAST) // multicast?
	list.addElement(SEND_UNICAST);		// add unicast send to list
    }
    return(list);				// return service list
  }

  /**
    Initialise the service entity.
  */
  public void initialise() {
    messageCount = 0;				// initialise message count
    sdu = null;					// initialise SDU output
    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
    String message = "M" + messageCount;	// create message
    messageCount++;				// increment message count
    if (service.equals(SEND_BROADCAST)) {	// send broadcast request?
      MCAST.serviceMode = MCAST.BROADCAST;	// set broadcast mode
      serviceDestinations = "BDEF";		// set service destinations
      sdu = new MCASTSdu("B", message);		// create SDU to B
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
      sdu = new MCASTSdu("D", message);		// create SDU to D
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
      sdu = new MCASTSdu("E", message);		// create SDU to E
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
      sdu = new MCASTSdu("F", message);		// create SDU to F
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
    }
    else if (service.equals(SEND_MULTICAST)) {	// send multicast request?
      MCAST.serviceMode = MCAST.MULTICAST;	// set multicast mode
      serviceDestinations = "BDE";		// set service destinations
      sdu = new MCASTSdu("BDE", message);	// create SDU to B, D, E
      transmitPDU(sdu, 'A');			// send message to router A
    }
    else if (service.equals(SEND_UNICAST)) {	// send unicast request?
      MCAST.serviceMode = MCAST.UNICAST;	// set unicast mode
      serviceDestinations = "BDE";		// set service destinations
      sdu = new MCASTSdu("B", message);		// create SDU to B
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
      sdu = new MCASTSdu("D", message);		// create SDU to D
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
      sdu = new MCASTSdu("E", message);		// create SDU to E
      serviceEvents.addElement(			// add copy comment
	MCAST.comment(this, "copy"));
      transmitPDU(sdu, 'A');			// send message to router A
    }
    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
    return(serviceEvents);			// return service events
  }

  /**
    Send an SDU.

    @param sdu		SDU
  */
  public void transmitPDU(PDU sdu) {
    String destinations =			// get destinations
      ((MCASTSdu) sdu).getDestinations();
    ProtocolEntity entityDestination =		// get protocol entity dest.
      MCAST.getEntity(destinations.charAt(0));
    transmitPDU(sdu, entityDestination);	// transmit PDU
  }

  /**
    Send an SDU to the given destination letter.

    @param sdu		SDU
    @param destination	destination letter
  */
  public void transmitPDU(PDU sdu, char destination) {
    ProtocolEntity entityDestination =		// get protocol entity dest.
      MCAST.getEntity(destination);
    transmitPDU(sdu, entityDestination);	// transmit PDU
  }

  /**
    Send an SDU to the given destination entity.

    @param sdu		SDU
    @param destination	destination 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);
    serviceEvents.addElement(			// add send event
      new ProtocolEvent(ProtocolEvent.SEND, sdu));
    for (int i = 0; i < receiveEvents.size(); i++) // go through receive events
      serviceEvents.addElement(			// append receive event
	receiveEvents.get(i));
  }

}

