package brn.swans.app.rtp.profile;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

import jist.runtime.JistAPI;

import brn.swans.app.rtp.RtpPayloadReceiver;
import brn.swans.app.rtp.msg.RtpMessage;
import brn.swans.app.rtp.msg.RtpMpegMessage;

/**
 * This module is used to receive data (mpeg video) of payload 32 for rtp
 * endsystems working under the rtp av profile. It expects data in the format
 * defined in rfc 2250. Its corresponding sender module is
 * RTPAVProfilePayload32Sender. This implementation doesn't work with real mpeg
 * data, so it doesn't write an mpeg file but a sim file, which can be played
 * with a modified mpeg player.
 * 
 * TODO implement a playout buffer. currently the receiver accepts packets only
 * in strict order, all others are discarded.
 */
public class RtpAvProfilePayload32Receiver extends RtpPayloadReceiver {

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

  /**
   * A reordering queue for incoming packets.
   */
  protected List queue;

  /**
   * Size of the playout buffer in ns. 
   * 
   * TODO the receiver needs to know the fps/clockrate etc. to map simulation time
   * to timestamps
   */
  //protected long playoutBufferSize;

  /**
   * Stores the lowest allowed time stamp of rtp data packets. The value is used
   * to throw away packets from mpeg frames already stored in the data file.
   */
  protected int lowestAllowedTimeStamp;

  /**
   * Default constructor.
   */
  public RtpAvProfilePayload32Receiver() {
    super();

    payloadType = 32;
    lowestAllowedTimeStamp = 0;
    queue = new LinkedList();
  }

  /**
   * Writes information about received frames into the output file. The only
   * error correction provided is reordering packets of one frame if needed.
   */
  protected void processPacket(RtpMessage rtpPacket) {
    if (log.isDebugEnabled())
      log.debug(this + " received " + rtpPacket);

    // the first packet sets the lowest allowed time stamp
    if (lowestAllowedTimeStamp == 0) {
      lowestAllowedTimeStamp = rtpPacket.getTimeStamp();
    }

    if (rtpPacket.getTimeStamp() < lowestAllowedTimeStamp) {
      rtpPacket = null;
      if (log.isDebugEnabled())
        log.debug(this + " packet to old, discarded, allowed >= " + lowestAllowedTimeStamp);
      return;
    }

    // is this packet from the next frame ?
    // this can happen when the marked packet has been
    // lost or arrives late
    boolean nextTimeStamp = rtpPacket.getTimeStamp() > lowestAllowedTimeStamp;

    // is this packet marked, which means that it's
    // the last packet of this frame
    boolean marked = rtpPacket.getMarker();

    // test if end of frame reached

    // check if we received the last (= marked)
    // packet of the frame or
    // we received a packet of the next frame

    // we are not at the end of the frame
    // so just insert this packet
    if (!nextTimeStamp && !marked) {
      queue.add(rtpPacket);
      return;
    }

    // a marked packet belongs to this frame
    if (marked && !nextTimeStamp) {
      queue.add(rtpPacket);
    }

    int pictureType = 0;
    int frameSize = 0;
    int timeStamp = 0;
    int sequenceNumber = 0;

    // the queue contains all packets for this frame
    // we have received
    while (0 != queue.size()) {
      RtpMessage readPacket = (RtpMessage) (queue.get(0));
      queue.remove(readPacket);

      RtpMpegMessage mpegPacket = (RtpMpegMessage) (readPacket.getPayload());
      if (pictureType == 0)
        pictureType = mpegPacket.getPictureType();
      frameSize = frameSize + mpegPacket.getPayload().getSize();
      timeStamp = readPacket.getTimeStamp();
      sequenceNumber = (int) (readPacket.getSequenceNumber() & 0xFFFF);

      mpegPacket = null;
      readPacket = null;
    }

    // we start the next frame
    // set the allowed time stamp
    if (nextTimeStamp) {
      lowestAllowedTimeStamp = rtpPacket.getTimeStamp();
      queue.add(rtpPacket);
    }

    // we have calculated a frame
    if (frameSize > 0) {
      // what picture type is it
      char picture = ' ';
      if (pictureType == 1)
        picture = 'I';
      else if (pictureType == 2)
        picture = 'P';
      else if (pictureType == 3)
        picture = 'B';
      else if (pictureType == 4)
        picture = 'D';

      // create sim line
      StringBuffer sb = new StringBuffer();
      sb.append(JistAPI.getTime());
      sb.append("\t");
      sb.append(frameSize * 8);
      sb.append("\t");
      sb.append(timeStamp);
      sb.append("\t");
      sb.append(sequenceNumber);
      sb.append("\t");
      sb.append(picture);
      sb.append("-Frame");
      sb.append("\n");

      // and write it to the file
      try {
        this.output.write(sb.toString().getBytes());
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }

  protected void openOutputFile(String fileName) throws IOException {
    super.openOutputFile(fileName);

    String header = "# arrival\tframe size\ttimestamp\tseqno\tpictype\n";
    output.write(header.getBytes());
  }

}
