package brn.swans.app.rtcp;

import java.util.List;

import jist.runtime.JistAPI;
import jist.swans.net.NetAddress;
import brn.swans.app.rtp.msg.RtpMessage;

/**
 * This class is a super class for classes intended for storing information
 * about rtp end systems. It has two subclasses: RTPReceiverInformation which is
 * used for storing information about other system participating in an rtp
 * session. RTPSenderInformation is used by an rtp endsystem for storing
 * information about itself.
 */
public class RtpParticipantInfo implements JistAPI.Timeless {

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

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

  /**
   * Used for storing sdes information about this rtp endsystem. The ssrc
   * identifier is also stored here.
   */
  protected SDESChunk sdesChunk;

  /**
   * Used for storing the ip address of this endsystem.
   */
  protected NetAddress address;

  /**
   * Used for storing the port for rtp by this endsystem.
   */
  protected int rtpPort;

  /**
   * Used for storing the port for rtcp by this endsystem.
   */
  protected int rtcpPort;

  /**
   * Stores the number of rtcp intervals (including the current one) during
   * which this rtp endsystem hasn't sent any rtp data packets. When an rtp data
   * packet is received it is reset to 0.
   */
  protected int silentIntervals;

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

  /**
   * Default constructor.
   */
  public RtpParticipantInfo(int ssrc) {
    this.sdesChunk = new SDESChunk(ssrc);
    // because there haven't been sent any rtp packets
    // by this endsystem at all, the number of silent
    // intervals would be undefined; to calculate with
    // it but not to regard this endsystem as a sender
    // it is set to 3; see isSender() for details
    this.silentIntervals = 3;
    this.address = null;
    this.rtpPort = 0;
    this.rtcpPort = 0;
  }

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

  /**
   * Returns a copy of the sdes chunk used for storing source description items
   * about this system.
   */
  public SDESChunk getSdesChunk() {
    return sdesChunk;
  }

  public NetAddress getAddress() {
    return address;
  }

  public void setAddress(NetAddress address) {
    this.address = address;
  }

  public int getRtpPort() {
    return rtpPort;
  }

  public void setRtpPort(int rtpPort) {
    this.rtpPort = rtpPort;
  }

  public int getRtcpPort() {
    return rtcpPort;
  }

  public void setRtcpPort(int rtcpPort) {
    this.rtcpPort = rtcpPort;
  }

  /**
   * Returns the ssrc identifier of the rtp endsystem.
   */
  public int getSsrc() {
    return sdesChunk.getSsrc();
  }

  /**
   * Sets the ssrc identifier.
   */
  public void setSsrc(int ssrc) {
    sdesChunk.setSsrc(ssrc);
  }

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

  // ////////////////////////////////////////////////
  // Implementation
  //

  /**
   * This method should be extended by a subclass for extracting information
   * about the originating endsystem of an rtp packet. This method sets
   * _silentInterval to 0 so that the sender of this rtp packet is regarded as
   * an active sender.
   */
  public void processRTPPacket(RtpMessage packet, long arrivalTime) {
    silentIntervals = 0;
  }

  /**
   * This method extracts information about an rtp endsystem as provided by the
   * given SenderReport.
   */
  public void processSenderReport(SenderReport report, long arrivalTime) {
    // useful code can be found in subclasses
  }

  /**
   * This method extracts information of the given ReceptionReport.
   */
  protected void processReceptionReport(ReceptionReport report, long arrivalTime) {
    // useful code can be found in subclasses
  }

  /**
   * This method extracts sdes information of the given sdes chunk.and stores
   * it.
   */
  public void processSDESChunk(SDESChunk sdesChunk, long arrivalTime) {
    List items = sdesChunk.getItems();
    for (int i = 0; i < items.size(); i++)
      addSDESItem((SDESItem) items.get(i));
  }

  /**
   * Adds this sdes item to the sdes chunk of this participant.
   */
  public void addSDESItem(SDESItem sdesItem) {
    sdesChunk.addSDESItem(sdesItem);
  }

  /**
   * This method is intended to be overwritten by subclasses. It should return a
   * receiver report if there have been received rtp packets from that endsystem
   * and NULL otherwise. The implementation for this class always returns NULL.
   */
  protected ReceptionReport receptionReport(long now) {
    throw new RuntimeException(
        "Returning NULL pointer results in segmentation fault");
    // return null;
  }

  /**
   * This method is intended to be overwritten by subclasses which are used for
   * storing information about itself. It should return a sender report if there
   * have been sent rtp packets recently or NULL otherwise. The implementation
   * for this class always returns NULL.
   */
  protected SenderReport senderReport(long now) {
    return null;
  }

  /**
   * This method should be called by the rtcp module which uses this class for
   * storing information every time an rtcp packet is sent. Some behaviour of
   * rtp and rtcp (and this class) depend on how many rtcp intervals have
   * passed, for example an rtp end system is marked as inactive if there
   * haven't been received packets from it for a certain number of rtpc
   * intervals. Call senderReport() and receptionReport() before calling this
   * method.
   */
  public void nextInterval(long now) {
    silentIntervals++;
  }

  /**
   * Returns true if the end system does no longer participate in the rtp
   * session. The implementation in this class always returns false.
   */
  public boolean toBeDeleted(long now) {
    return false;
  }

  /**
   * Returns true if this endsystem has sent at least one rtp data packet during
   * the last two rtcp intervals (including the current one).
   */
  public boolean isSender() {
    return (silentIntervals <= 1);
  }

  /**
   * This method returns the given 32 bit ssrc identifier as an 8 character
   * hexadecimal number which is used as name of an RTPParticipantInfo object.
   */
  protected static String ssrcToName(int ssrc) {
    return Integer.toHexString(ssrc);
  }

  /**
   * Creates a new SDESItem and adds it to the SDESChunk stored in this
   * RTPParticipantInfo.
   */
  protected void addSDESItem(int type, String content) {
    sdesChunk.addSDESItem(new SDESItem(type, content));
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + ((address == null) ? 0 : address.hashCode());
    result = PRIME * result + rtcpPort;
    result = PRIME * result + rtpPort;
    result = PRIME * result + ((sdesChunk == null) ? 0 : sdesChunk.hashCode());
    result = PRIME * result + silentIntervals;
    return result;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#equals(java.lang.Object)
   */
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final RtpParticipantInfo other = (RtpParticipantInfo) obj;
    if (address == null) {
      if (other.address != null)
        return false;
    } else if (!address.equals(other.address))
      return false;
    if (rtcpPort != other.rtcpPort)
      return false;
    if (rtpPort != other.rtpPort)
      return false;
    if (sdesChunk == null) {
      if (other.sdesChunk != null)
        return false;
    } else if (!sdesChunk.equals(other.sdesChunk))
      return false;
    if (silentIntervals != other.silentIntervals)
      return false;
    return true;
  }

}
