package brn.swans.app.rtp;

import org.apache.log4j.Logger;

import jist.runtime.JistAPI;
import jist.swans.Constants;
import jist.swans.app.AbstractApplication;
import jist.swans.app.AppInterface;
import jist.swans.misc.Util;
import jist.swans.net.NetAddress;
import jist.swans.trans.TransInterface;

/**
 * The class RTPApplication is just a very simple sample for an application
 * which uses RTP. It acts as a sender if the omnet parameter fileName is set,
 * and as a receiver if the parameter is empty.
 *
 * rtp payload types:
 * <ul>
 * <li> sptPCMU ITU-T G.711. mu-law audio 8 Khz (RFC 1890).
 * <li> sptG726_32 ITU-T G.726. ADPCM audio (RFC 1890).
 * <li> sptGSM GSM audio (RFC 1890).
 * <li> sptG723 ITU-T G.723. MP-MLQ ACELP audio (RFC 1890).
 * <li> sptDVI4_8000 Modified IMA ADPCM audio 8Khz (RFC 1890).
 * <li> sptDVI4_16000 Modified IMA ADPCM audio 16Khz (RFC 1890).
 * <li> sptLPC LPC audio (RFC 1890).
 * <li> sptPCMA ITU-T G.711 A-law audio 8 Khz (RFC 1890).
 * <li> sptG722 Audio (RFCs 1890, 3047).
 * <li> 10 sptL16_DUAL Linear uncompressed dual audio (RFC 1890).
 * <li> sptL16_MONO Linear uncompressed mono audio (RFC 1890).
 * <li> sptQCELP Audio at 8000 hz.
 * <li> sptMPA MPEG Audio elem. stream (RFCs 1890, 2250).
 * <li> sptG728 ITU-T G.728. LD-CELP audio.
 * <li> sptDVI4_11025 DVI audio at 11025 hz (by Joseph Di Pol).
 * <li> sptDVI4_22050 DVI audio at 22050 hz (by Joseph Di Pol).
 * <li> sptG729 ITU-T G.729. CS-ACELP audio.
 * <li> sptCELB Sun's propietary video (RFCs 1890, 2029).
 * <li> sptJPEG JPEG (ISO 10918) video (RFCs 1890, 2435).
 * <li> sptNV Ron Frederick's nv audio (RFC 1890).
 * <li> sptH261 ITU-T H.261 video (RFCs 1890, 2032).
 * <li> sptMPV MPEG Video elem. stream (RFCs 1890, 2250).
 * <li> sptMP2T MPEG 2 Transport stream (RFCs 1890, 2250).
 * <li> sptH263 ITU-T H.263 video (RFCs 2190, 2429).
 * </ul>
 */
public class RtpApplication extends AbstractApplication implements AppInterface, JistAPI.Proxiable {

  /**
   * Logger.
   */
  public static final Logger log = Logger.getLogger(RtpApplication.class
      .getName());

  // ////////////////////////////////////////////////
  // constants
  //

  // ////////////////////////////////////////////////
  // locals
  //

  /**
   * The CNAME of this participant.
   */
  private String commonName;

  /**
   * The name of the used profile.
   */
  private String profileName;

  /**
   * The reserved bandwidth for rtp/rtcp in bytes/second.
   */
  private int bandwidth;

  /**
   * The address of the unicast peer or of the multicast group.
   */
  private NetAddress destAddr;

  /**
   * One of the udp port used.
   */
  private short dstPort;

  /**
   * One of the udp port used.
   */
  private short srcPort;

  /**
   * The name of the file to be transmitted.
   */
  private String fileName;

  /**
   * The payload type of the data in the file.
   */
  private int payloadType;

  /**
   * The delay after the application enters the session,
   */
  private long sessionEnterDelay;

  /**
   * The delay after the application starts the transmission.
   */
  private long transmissionStartDelay;

  /**
   * The delay after the application stops the transmission.
   */
  private long transmissionStopDelay;

  /**
   * The delay after the application leaves the session.
   */
  private long sessionLeaveDelay;

  // TODO do we need this state data?
  // boolean sessionEntered;
  // boolean transmissionStarted;
  // boolean transmissionFinished;
  // boolean sessionLeft;

  /**
   * The ssrc as reported from rtp module.
   */
  // private int ssrc;
  /**
   * Self-referencing rtp-app entity reference.
   */
  private AppInterface self;

  /**
   * Object below RtpApplication
   */
  private RtpEndsystemModule rtpModuleEntity;

  /** whether to send data */
  private boolean doSend;

  // ////////////////////////////////////////////////
  // initialization
  //

  public RtpApplication(String commonName, String profileName, int bandwidth,
      NetAddress destAddr, short dstPort, short srcPort,
      boolean doSend, String fileName, int payloadType,
      long sessionEnterDelay, long transmissionStartDelay,
      long transmissionStopDelay, long sessionLeaveDelay) {
    super();
    this.commonName = commonName;
    this.profileName = profileName;
    this.bandwidth = bandwidth;
    this.destAddr = destAddr;
    this.dstPort = dstPort;
    this.srcPort = srcPort;
    this.doSend = doSend;
    this.fileName = fileName;
    this.payloadType = payloadType;
    this.sessionEnterDelay = sessionEnterDelay;
    this.transmissionStartDelay = transmissionStartDelay;
    this.transmissionStopDelay = transmissionStopDelay;
    this.sessionLeaveDelay = sessionLeaveDelay;

    // this.sessionEntered = false;
    // this.transmissionFinished = false;
    // this.sessionLeft = false;
    // this.transmissionStarted = false;
    // this.ssrc = 0;

    // hookup
    this.self = (AppInterface) JistAPI.proxy(this, AppInterface.class);
    rtpModuleEntity = new RtpEndsystemModule();
    rtpModuleEntity.setApp(this);
  }

  /**
   * Create a G.711 u-Law audio streaming application (RTP payload type 0).
   * G.711 has a sample rate of 8000 samples/sec. A sample consists of 8 bit.
   * This results in a bandwidth of 8kB/s or 64kbps.
   *
   * @param destAddr
   * @param dstPort
   * @param srcAddr
   * @param srcPort
   * @param fileName
   * @param startTime
   * @param duration
   * @return the newly created application
   */
  public static RtpApplication createAudioPCMU(NetAddress destAddr,
      short dstPort, NetAddress srcAddr, short srcPort,
      boolean doSend, String fileName, long startTime, long duration) {

    long sessionLeaveDelay = (int) 2* Constants.SECOND
        + (!doSend ? duration : 0);

    return new RtpApplication(srcAddr.toString(), "RtpAvProfile",
        44100 * 2 * 2, destAddr, dstPort, srcPort, doSend, fileName, 0, startTime, 0,
        duration, sessionLeaveDelay);
  }

  /**
   * Linear uncompressed dual audio (RFC 1890).
   */
  public static RtpApplication createAudioL16D(NetAddress destAddr,
      short dstPort, NetAddress srcAddr, short srcPort, boolean doSend,
      String fileName, long sessionInterval) {

    long sessionLeaveDelay = (int) Constants.SECOND
        + (!doSend ? sessionInterval : 0);

    return new RtpApplication(srcAddr.toString(), "RtpAvProfile", 44100*2*2,
        destAddr, dstPort, srcPort, doSend, fileName, 10, 0, 0, sessionInterval,
        sessionLeaveDelay);
  }

  /**
   * Linear uncompressed dual audio (RFC 1890).
   */
  public static RtpApplication createApplication(NetAddress destAddr,
      short dstPort, NetAddress srcAddr, short srcPort, boolean doSend, String fileName,
      int bandwidth, int payloadType, long startTime, long duration) {

    long sessionLeaveDelay = (int) Constants.SECOND
        + (!doSend ? duration : 0);

    return new RtpApplication(srcAddr.toString(), "RtpAvProfile", bandwidth,
        destAddr, dstPort, srcPort, doSend, fileName, payloadType, startTime, 0, duration,
        sessionLeaveDelay);
  }

  // ////////////////////////////////////////////////
  // entity hookup
  //

  /**
   * Return proxy entity of this mac.
   *
   * @return self-referencing proxy entity.
   */
  public AppInterface getProxy() {
    return this.self;
  }

  /**
   * Hook up with the transport entity.
   *
   * @param udpEntity udp entity
   */
  public void setUdpEntity(TransInterface.TransUdpInterface udpEntity) {
    if (!JistAPI.isEntity(udpEntity))
      throw new IllegalArgumentException("expected entity");
    this.rtpModuleEntity.setUdpEntity(udpEntity);
  }

  public void setFlowId(int flowId) {
    this.rtpModuleEntity.setFlowId(flowId);
  }

  // ////////////////////////////////////////////////
  // accessors
  //

  public int getBandwidth() {
    return bandwidth;
  }

  public String getCommonName() {
    return commonName;
  }

  public NetAddress getDestAddr() {
    return destAddr;
  }

  public String getFileName() {
    return fileName;
  }

  public int getPayloadType() {
    return payloadType;
  }

  public short getDstPort() {
    return dstPort;
  }

  public short getSrcPort() {
    return srcPort;
  }

  public String getProfileName() {
    return profileName;
  }

  public long getSessionEnterDelay() {
    return sessionEnterDelay;
  }

  public long getSessionLeaveDelay() {
    return sessionLeaveDelay;
  }

  public long getTransmissionStartDelay() {
    return transmissionStartDelay;
  }

  public long getTransmissionStopDelay() {
    return transmissionStopDelay;
  }

  // ////////////////////////////////////////////////
  // overwrites
  //

  public String toString() {
    return //"RtpApplication:" +
      commonName + " " + Util.timeSeconds();
  }

  // ////////////////////////////////////////////////
  // AppInterface interface impl
  //

  public void run() {
    if (log.isDebugEnabled())
      log.debug(this + " starting RtpApplication");
    JistAPI.sleepBlock(this.sessionEnterDelay);

    // enter the session at the rtp layer
    if (log.isDebugEnabled())
      log.debug(this + " enter session");
    rtpModuleEntity.enterSession(commonName, profileName, bandwidth, destAddr,
        dstPort, srcPort);
  }

  public void run(String[] args) {
    // TODO Auto-generated method stub
    throw new RuntimeException("Not supported");
  }

  // ////////////////////////////////////////////////
  // Self-messages
  //

  // ////////////////////////////////////////////////
  // Called by the rtp layer
  //

  /**
   * Asynchronous information about session entering.
   */
  public void sessionEntered(int ssrc) {
    // sessionEntered = true;
    // this.ssrc = ssrc;
    if (log.isDebugEnabled())
      log.debug(this + " session entered");

    if (true == this.doSend) {
      if (log.isDebugEnabled())
        log.debug(this + " creating sender module");

      rtpModuleEntity.createSenderModule(ssrc, payloadType, fileName);
      if (log.isDebugEnabled())
        log.debug(this + " sender module created");

      JistAPI.sleepBlock(transmissionStartDelay);
      if (log.isDebugEnabled())
        log.debug(this + " starting sender");
      rtpModuleEntity.senderModuleControl(ssrc, RtpPayloadSender.CMD_PLAY, 0);

      JistAPI.sleepBlock(transmissionStopDelay);
      if (log.isDebugEnabled())
        log.debug(this + " stopping sender");
      rtpModuleEntity.senderModuleControl(ssrc, RtpPayloadSender.CMD_STOP, 0);
    } else {
      JistAPI.sleepBlock(sessionLeaveDelay);
      rtpModuleEntity.leaveSession();
    }
  }

  public void senderModuleStatus(int ssrc, int status, int timeStamp) {
    switch (status) {
    case RtpPayloadSender.STATE_PLAYING:
      // skip
      break;
    case RtpPayloadSender.STATE_FINISHED:
    case RtpPayloadSender.STATE_STOPPED:
      // transmissionFinished = true;
      JistAPI.sleepBlock(sessionLeaveDelay);
      rtpModuleEntity.leaveSession();
      break;
    default:
      throw new RuntimeException("got unknown status " + status);
    }
  }

  public void sessionLeft() {
    // sessionLeft = true;
  }


}
