package brn.swans.app.rtcp.msg;

import jist.swans.misc.Message;
import jist.swans.misc.Pickle;

/**
 * This is a base class for all types (except RTCPCompoundPacket) of rtcp
 * packets. It isn't intended to be used directly.
 *
 * See http://www.scit.wlv.ac.uk/rfc/rfc18xx/RFC1889.html
 */
public abstract class RtcpMessage implements Message {

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

  /**
   * size of the common header in octets.
   */
  public static final int COMMON_HEADER_SIZE = 4;

  /**
   * The values for the packet type field in the rtcp header as defined in the
   * rfc.
   */

  /**
   * default value undefined
   */
  public static final byte RTCP_PT_UNDEF = (byte)0;

  /**
   * sender report
   */
  public static final byte RTCP_PT_SR = (byte)200;

  /**
   * receiver report
   */
  public static final byte RTCP_PT_RR = (byte)201;

  /**
   * source description
   */
  public static final byte RTCP_PT_SDES = (byte)202;

  /**
   * bye
   */
  public static final byte RTCP_PT_BYE = (byte)203;

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

  /**
   * The rtp version used. Always 2.
   */
  protected byte version;

  /**
   * Set to 1 if padding (bytes at the end of the packet to assure that the
   * packet length in bytes is a multiple of a certain number; possibly needed
   * for encryption) is used. In the simulation padding isn't needed, so it is
   * always 0.
   */
  protected boolean padding;

  /**
   * Depending on the packet type, here is stored how many receiver reports or
   * sdes chunks are contained in the packet. Values from 0 to 31 are allowed.
   */
  protected byte count;

  /**
   * The packet type of the rtcp packet.
   */
  protected byte packetType;

  /**
   * Length of the rtcp header
   */
  protected int length;

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

  /**
   * Default constructor.
   */
  public RtcpMessage() {
    // initialize variables
    version = 2;
    padding = false;
    count = 0;
    packetType = RTCP_PT_UNDEF;
    // rtcpLength can be calculated with cPacket::length()

    // RTCP header length size is 4 bytes
    // not all rtcp packets (in particular RTCPSDESPacket) have
    // the ssrc identifier stored in the header
    length = COMMON_HEADER_SIZE;
  }

  public RtcpMessage(byte[] msg, int offset) {
    // Byte 0, bits 0,1: version
    version = (byte)((msg[offset + 0] >> 6) & 0x3);
    // Byte 0, bit 2: padding
    padding = ((msg[offset + 0] & 0x20) == 0x20) ? true : false;
    // Byte 0, bit 3-7: reception report count (RC)
    count = (byte)(msg[offset + 0] & 0x1F);

    // Byte 1, packet type (PT)
    packetType = msg[offset + 1];
    // Byte 2-3: sequence number
    length = (Pickle.arrayToUShort(msg, offset + 2) + 1) * 4;
  }


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

  public byte getCount() {
    return count;
  }

  public byte getPacketType() {
    return packetType;
  }

  public boolean getPadding() {
    return padding;
  }

  public byte getVersion() {
    return version;
  }

  public int getLength() {
    return length;
  }

  /**
   * rtcpLength is the header field length of an rtcp packet in 32 bit words
   * minus one
   */
  public short getRtcpLength() {
    return (short) (length / 4 - 1);
  }

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

  public String toString() {
    return "RTCPPacket:" + ", version=" + version + ", padding=" + padding
        + ", count=" + count + ", packetType=" + packetType + ", rtcpLength="
        + getRtcpLength() + "]";
  }

  // ////////////////////////////////////////////////
  // Message interface implementation
  //

  public int getCommonHeaderBytes(byte[] msg, int offset) {

    byte b = 0;
    // Byte 0, bits 0,1: version
    b |= (version & 0x3) << 6;
    // Byte 0, bit 2: padding
    b |= (padding ? 0x20 : 0x00 );
    // Byte 0, bit 3-7: reception report count (RC)
    b |= count & 0x1F;
    Pickle.ubyteToArray(b, msg, offset);

    // Byte 1, packet type (PT)
    Pickle.ubyteToArray(packetType, msg, offset + 1);
    // Byte 2-3: sequence number
    Pickle.ushortToArray(getRtcpLength() & 0xFFFF, msg, offset + 2);

    return offset + COMMON_HEADER_SIZE;
  }

  public int getSize() {
    return length;
  }

  public static RtcpMessage fromBytes(byte[] msg, int offset) {
    RtcpMessage ret = null;

    byte packetType = msg[offset + 1];
    switch (packetType) {
    case RTCP_PT_BYE:
      ret = new RtcpByeMessage(msg, offset);
      break;
    case RTCP_PT_RR:
      ret = new RtcpReceiverReportMessage(msg, offset);
      break;
    case RTCP_PT_SDES:
      ret = new RtcpSDESMessage(msg, offset);
      break;
    case RTCP_PT_SR:
      ret = new RtcpSenderReportMessage(msg, offset);
      break;
    case RTCP_PT_UNDEF:
    default:
      throw new RuntimeException("Unknown rtcp type " + packetType);
    }

    return ret;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + length;
    result = PRIME * result + (padding ? 1231 : 1237);
    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 RtcpMessage other = (RtcpMessage) obj;
    if (count != other.count)
      return false;
    if (length != other.length)
      return false;
    if (packetType != other.packetType)
      return false;
    if (padding != other.padding)
      return false;
    if (version != other.version)
      return false;
    return true;
  }

}
