package brn.swans.app.rtcp;

import java.util.ArrayList;
import java.util.List;

import jist.runtime.JistAPI;
import jist.swans.misc.Pickle;

/**
 * The class SDESItem is used for storing a source description item (type of
 * description; description string) for an rtp end system.
 */
public class SDESChunk implements JistAPI.Timeless {

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

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

  /**
   * The ssrc identifier this SDESChunk is for (unsigned).
   */
  protected int ssrc;

  /**
   * The length in bytes of this SDESChunk.
   */
  protected int length;

  /**
   * List of {@link SDESItem}
   */
  protected List items;

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

  /**
   * Default constructor.
   */
  public SDESChunk(int ssrc) {
    this.ssrc = ssrc;
    this.length = 4;
    this.items = new ArrayList();
  }

  public SDESChunk(byte[] msg, int offset) {
    this.items = new ArrayList();

    // Bytes 0-3: ssrc
    ssrc = (int)Pickle.arrayToUInt(msg, offset);
    offset += 4;


    // SDESItem
    while (msg[offset] != 0) {
      SDESItem item = new SDESItem(msg, offset);
      offset += item.getLength();
      items.add(item);
    }
  }


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

  public int getSsrc() {
    return ssrc;
  }

  public void setSsrc(int ssrc) {
    this.ssrc = ssrc;
  }

  /**
   * Returns the length in bytes of this SDESChunk.
   * The SDES chunk is aligned at 32 bit borders.
   */
  public int getLength() {
    // +1 for the null termination
    int padding =  4 + (-length-1) % 4;
    return length + 1 + padding;
  }

  public List getItems() {
    return items;
  }

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

  public String toString() {
    String ret = "SDESChunk [ssrc=" + ssrc + ", [";
    for (int i = 0; i < items.size(); i++) {
      ret += ((SDESItem) items.get(i)).toString();
      if (i + 1 < items.size())
        ret += ", ";
    }
    ret += "]]";
    return ret;
  }

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

  /**
   * Adds an SDESItem to this SDESChunk. If there is already an SDESItem of the
   * same type in this SDESChunk it is replaced by the new one.
   */
  public void addSDESItem(SDESItem item) {
    for (int i = 0; i < items.size(); i++) {
      SDESItem compareItem = (SDESItem) items.get(i);
      if (compareItem.getType() == item.getType()) {
        items.remove(i);
        length -= compareItem.getLength();
      }
    }

    items.add(item);
    length += item.getLength();

  }

  public int getBytes(byte[] msg, int offset) {
    // Bytes 0-3: ssrc
    Pickle.uintToArray(ssrc, msg, offset);
    offset += 4;
    // SDESItem
    for (int i = 0; i < items.size(); i++) {
      SDESItem item = (SDESItem) items.get(i);
      offset = item.getBytes(msg, offset);
    }

    msg[offset++] = 0;

    //  align to 32 bit boundary
    int padding = 4 - offset % 4;

    while (0 != padding) {
      msg[offset] = 0;
      padding--;
      offset++;
    }

    return offset;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + ((items == null) ? 0 : items.hashCode());
    result = PRIME * result + length;
    result = PRIME * result + ssrc;
    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 SDESChunk other = (SDESChunk) obj;
    if (items == null) {
      if (other.items != null)
        return false;
    } else if (!items.equals(other.items))
      return false;
    if (length != other.length)
      return false;
    if (ssrc != other.ssrc)
      return false;
    return true;
  }

}
