/*
 * @(#)RTCPReport.java
 * Created: 02-Dec-2005
 * Version: 1-1-alpha3
 * Copyright (c) 2005-2006, University of Manchester All rights reserved. 
 * Andrew G D Rowley
 * Christian Vincenot <sipcom@cyberspace7.net>
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials 
 * provided with the distribution. Neither the name of the University of 
 * Manchester nor the names of its contributors may be used to endorse or 
 * promote products derived from this software without specific prior written
 * permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package net.sf.fmj.media.rtp;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Vector;

import javax.media.rtp.Participant;
import javax.media.rtp.rtcp.Report;
import javax.media.rtp.rtcp.SourceDescription;

/**
 * Represents an RTCP Report
 * @author Andrew G D Rowley
 * @author Christian Vincenot
 * @version 1-1-alpha3
 */
public abstract class RTCPReport implements Report {
    
    // The participant that sent the report
    protected Participant participant;

    // The header of the report
    protected RTCPHeader header;
    
    // The vector of feedback reports in this report
    protected Vector feedbackReports = new Vector();
    
    // The source descriptions in the report
    protected Vector sourceDescriptions = new Vector();
    
    // The number of bytes of SDES packets read
    protected int sdesBytes = 0;
    
    // The CNAME of the source
    private String cName = null;
    
    // True if this is a bye event
    private boolean isBye = false;
    
    // The reason for leaving if this is a bye packet
    private String byeReason = "";
    
    // The ssrc of the report
    private long ssrc = 0;
    
    /**
     * Creates a new RTCPReport
     * @param data The data of the report
     * @param offset The offset in the data where the report starts
     * @param length The length of the report in the data
     * @throws IOException I/O Exception
     */
    public RTCPReport(byte data[], int offset, int length) throws IOException {
        header = new RTCPHeader(data, offset, length);
        this.ssrc = header.getSsrc();
        if (header.getPadding() == 1) {
            throw new IOException("First packet has padding");
        } else if (((header.getLength() + 1) * 4) >= length) {
            throw new IOException("Invalid Length");
        }
    }
    
    /**
     * Reads feedback reports from the data
     * @param data The data to read the feedback reports from
     * @param offset The offset into the data where the reports start
     * @param length The length of the data
     * @throws IOException I/O Exception
     */
    protected void readFeedbackReports(byte data[], int offset, int length) 
            throws IOException {
        for (int i = 0; i < header.getReceptionCount(); i++) {
            RTCPFeedback feedback = new RTCPFeedback(data, offset, length);
            feedbackReports.add(feedback);
            offset += RTCPFeedback.SIZE;
        }
    }
    
    /**
     * Reads the source description from the data
     * @param data The data to read the source description from
     * @param offset The offset into the data where the SDES packet starts
     * @param length The length of the data
     * @throws IOException I/O Exception
     */
    protected void readSourceDescription(byte data[], int offset, int length) 
            throws IOException {
        if (length > 0) {
            // Only do this if there is an SDES header
            RTCPHeader sdesHeader = new RTCPHeader(data, offset, length);

            if (sdesHeader.getPacketType() == RTCPPacket.PT_SDES) {
                ssrc = sdesHeader.getSsrc();
                sdesBytes = (sdesHeader.getLength() + 1) * 4;
                DataInputStream stream = 
                    new DataInputStream(new ByteArrayInputStream(
                        data, offset + RTCPHeader.SIZE, length));
                int type = SourceDescription.SOURCE_DESC_CNAME;
                while (type != 0) {
                    type = stream.readUnsignedByte();
                    if (type != 0) {
                        int len = stream.readUnsignedByte();
                        byte[] desc = new byte[len];
                        stream.readFully(desc);
                        String descStr = new String(desc, "UTF-8");
                        SourceDescription description = 
                            new SourceDescription(type, descStr, 0, false);
                        sourceDescriptions.add(description);
                        if (type == SourceDescription.SOURCE_DESC_CNAME) {
                            cName = descStr;
                        }
                    }
                }
            }
        }
    }
    
    /**
     * Reads and handles the BYE part of an RTCP report.
     * @param data the raw data in which the packet is contained
     * @param offset the offset where the BYE starts
     * @param length the length of the report
     * @throws java.io.IOException I/O Exception
     */
    protected void readBye(byte data[], int offset, int length) 
            throws IOException {
        if (length > 0) {
            RTCPHeader sdesHeader = new RTCPHeader(data, offset, length);
            if (sdesHeader.getPacketType() == RTCPPacket.PT_BYE) {
                isBye = true;
                if (((length + 1) * 4) > RTCPHeader.SIZE) {
                    int len = data[offset + RTCPHeader.SIZE] & 0xFF;
                    if ((len < (length - RTCPHeader.SIZE)) && (len > 0)) {
                        byeReason = new String(data, offset + RTCPHeader.SIZE + 
                                1, len);
                    }
                }
            }
        }
    }
    
    /**
     * Sets the participant linked with this RTCP report.
     * @param participant the participant identified as linked with this RTCP report
     */
    protected void setParticipant(RTPParticipant participant) {
        this.participant = participant;
        Vector streams = participant.getStreams();
        if (streams.size() == 0) {
            Vector sdes = participant.getSourceDescription();
            for (int i = 0; i < sdes.size(); i++) {
                SourceDescription sdesItem = (SourceDescription) sdes.get(i);
                participant.addSourceDescription(sdesItem);
            }
        }
    }
    
    /**
     * Returns the participant linked with this RTCP report
     * @return the participant identified previously as being linked with this RTCP report
     */
    public Participant getParticipant() {
        return participant;
    }
    
    /**
     * Returns the SSRC announced in this report.
     * @return SSRC in this report
     */
    public long getSSRC() {
        return ssrc;
    }
    
    /**
     * Returns the feedback reports for this RTCP report.
     * @return the feedback reports for this RTCP report
     */
    public Vector getFeedbackReports() {
        return feedbackReports;
    }
    
    /**
     * Returns the sources descriptions (SDES) in this RTCP report.
     * @return the sources descriptions (SDES) in this RTCP report
     */
    public Vector getSourceDescription() {
        return sourceDescriptions;
    }
    
    /**
     * Gets the cName of the source of the report
     * @return the cName, or null if none sent
     */
    public String getCName() {
        return cName;
    }
    
    /**
     * Returns true if a bye packet was added to the report
     * @return true if a BYE packet was added to the report
     */
    public boolean isByePacket() {
        return isBye;
    }
    
    /**
     * Returns the reason for the bye
     * @return the reason announced for this BYE packet
     */
    public String getByeReason() {
        return byeReason;
    }

}
