// MCASTRouterA.java

package protocol;				// protocol package

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

import support.*;				// import Jasper support classes

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

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

public class MCASTRouter implements ProtocolEntity {

  /** Service send offer */
  private final static String SEND = "Forward message for router ";

  /** Current router letter */
  public char currentRouter;

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

  /** Service data unit output */
  private Vector<MCASTSdu> sdus;

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

  /**
    Constructor for sources/sinks.

    @param serviceName	service name
 */
  public MCASTRouter(String serviceName) {
    this.serviceName = serviceName;		// set service name
    currentRouter = lastOf(serviceName);	// set current router letter
    initialise();				// initialise service entity
  }

  /**
    Find the SDU with the given destinations.

    @param destinations		destinations
    @return			the corresponding SDU (or null if none)
  */
  public MCASTSdu findSDU(String destinations) {
    MCASTSdu result = null;			// initialise SDU result
    for (int i = 0; i < sdus.size(); i++) {	// go through SDUs
      MCASTSdu sdu = sdus.get(i);		// get SDU
      if (sdu.destinations.equals(destinations)) { // destinations found?
	result = sdu;				// save result
	break;					// exit loop
      }
    }
    return(result);				// return SDU
  }

  /**
    Get first character of string.

    @param string		string to extract from
    @return			first character of string
  */
  public char firstOf(String string) {
    char letter = '?';				// initialise letter
    if (string != null) {			// non-null string?
      int length = string.length();		// get string length
      if (length > 0)				// non-empty string?
	letter = string.charAt(0);		// get first character
    }
    return(letter);				// return first character
  }

  /**
    Return the service name.

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

  /**
    Return the service data units.

    @return	service data units
  */
  public Vector<MCASTSdu> getSDUs() {
    return(sdus);				// return service data units
  }

  /**
    Return services currently offered.

    @return		list of services
  */
  public Vector<String> getServices() {
    Vector<String> list = new Vector<String>();	// initialise service list
    if (sdus != null && sdus.size() > 0) {	// SDUs from service user?
      for (int i = 0; i < sdus.size(); i++) {	// go through SDUs
	MCASTSdu sdu = sdus.get(i);		// get SDU
	String destinations = sdu.destinations;	// get SDU destinations
	String destination = "";		// initialise destination
	for (int j = 0; j < destinations.length(); j++) { // go through dests.
	  if (j > 0)				// not first destination?
	    destination += ", ";		// append comma
	  destination += destinations.charAt(j);// append destination
	}
	char router = firstOf(destination);	// get router letter
	if (router != currentRouter)		// not current router?
	  list.addElement(SEND + destination);	// add send to list
      }
    }
    return(list);				// return service list
  }

  /**
    Initialise the service entity.
  */
  public void initialise() {
    serviceEvents = new Vector<ProtocolEvent>();// initialise service events
    sdus = new Vector<MCASTSdu>();		// initialise SDUs
  }

  /**
    Get last character of string.

    @param string		string to extract from
    @return			last character of string
  */
  public char lastOf(String string) {
    char letter = '?';				// initialise letter
    if (string != null) {			// non-null string?
      int length = string.length();		// get string length
      if (length > 0)				// non-empty string?
	letter = string.charAt(length - 1);	// get last character
    }
    return(letter);				// return last character
  }

  /**
    Check if the destination router is more than one hop from the current router
    (i.e. is not the next router letter.

    @param destination		entity destination
    @return			true/false if destination is not/is adjacent
  */
  public boolean laterRouter(ProtocolEntity destination) {
    int currentIndex = (int) currentRouter;	// get current router index
    int destinationIndex =			// get destination router index
      (int) ((MCASTRouter) destination).currentRouter;
    return(destinationIndex > currentIndex + 1);// return router letter check
  }

  /**
    Return the next router to reach the destination. This is a very restricted
    routing table for the purposes of this example.

    @param destination		entity destination
    @return			next entity destination
  */
  public ProtocolEntity nextRouter(ProtocolEntity destination) {
    char destinationLetter =			// get destination router letter
      ((MCASTRouter) destination).currentRouter;
    char nextLetter = destinationLetter;	// assume destination by default
    switch (currentRouter) {			// route based on current router
      case 'A':					// at router A?
	switch (destinationLetter) {		// check destination index
	  case 'D':				// to router D?
	    nextLetter = 'C';			// via C
	    break;
	  case 'E':				// to router E?
	    nextLetter = 'C';			// via C
	    break;
	}
	break;
    }
    return(MCAST.getEntity(nextLetter));	// return next router entity
  }

  /**
    Perform service. Note that the current implementation works for a particular
    topology but is not general. When there are multiple destinations, the
    router forward to the first two of these. A more general implementation
    would embody particular knowledge about routing.

    @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 destinations =			// get destinations
	service.substring(SEND.length());
      String destination =			// remove destination commas
	destinations.replaceAll(", ", "");	// get destination(s)
      MCASTSdu sdu = findSDU(destination);	// find corresponding SDU
      char router = firstOf(destination);	// get destination router letter
      serviceEvents.addElement(			// add forward comment
	MCAST.comment(this, "forward"));
      transmitPDU(sdu, router);			// transmit SDU
      sdus.remove(sdu);				// remove SDU from SDUs
    }
    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
    if (sdu != null) {				// non-empty SDU?
      String destinations =			// get SDU destination(s)
	((MCASTSdu) sdu).destinations;
      if (destinations.length() > 1) {		// more than one destination?
	String message = ((MCASTSdu) sdu).sdu;	// get SDU message
	sdu =					// get SDU for first destination
	  new MCASTSdu(destinations.substring(0, 1), message);
	sdus.addElement((MCASTSdu) sdu);	// store SDU
	sdu =					// get SDU for later dests.
	  new MCASTSdu(destinations.substring(1), message);
	sdus.addElement((MCASTSdu) sdu);	// store SDU
      }
      else					// one destination
	sdus.addElement((MCASTSdu) sdu);	// store SDU
    }
    return(serviceEvents);			// return service events
  }

  /**
    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) {
    destination = nextRouter(destination);	// set next router destination
    sdu.setSource(this);			// set source as this entity
    sdu.setDestination(destination);		// set destination
    serviceEvents.addElement(			// add send event
      new ProtocolEvent(ProtocolEvent.SEND, sdu));
    Vector<ProtocolEvent> receiveEvents =	// receive SDU at destination
      destination.receivePDU(sdu);
    if (laterRouter(destination)) {		// later router?
      serviceEvents.addElement(			// add send event
	new ProtocolEvent(ProtocolEvent.TRAVERSE, sdu));
    }
    for (int i = 0; i < receiveEvents.size(); i++) // go through receive events
      serviceEvents.addElement(			// append receive event
	receiveEvents.get(i));
  }

}

