/*
 * Copyright (C) 2009 The Sipdroid Open Source Project
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This file is part of Sipdroid (http://www.sipdroid.org)
 * 
 * Sipdroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.sipdroid.net;

import com.beem.project.beem.utils.Random;

/**
 * RtpPacket implements a RTP packet.
 */
public class RtpPacket {
    /* RTP packet buffer containing both the RTP header and payload */
    byte[] packet;

    /* RTP packet length */
    int packet_len;

    /* RTP header length */
    // int header_len;
    /** Gets the RTP packet */
    public byte[] getPacket() {
	return packet;
    }

    /** Gets the RTP packet length */
    public int getLength() {
	return packet_len;
    }

    /** Gets the RTP header length */
    public int getHeaderLength() {
	if (packet_len >= 12)
	    return 12 + 4 * getCscrCount();
	else
	    return packet_len; // broken packet
    }

    /** Gets the RTP header length */
    public int getPayloadLength() {
	if (packet_len >= 12)
	    return packet_len - getHeaderLength();
	else
	    return 0; // broken packet
    }

    /** Sets the RTP payload length */
    public void setPayloadLength(int len) {
	packet_len = getHeaderLength() + len;
    }

    // version (V): 2 bits
    // padding (P): 1 bit
    // extension (X): 1 bit
    // CSRC count (CC): 4 bits
    // marker (M): 1 bit
    // payload type (PT): 7 bits
    // sequence number: 16 bits
    // timestamp: 32 bits
    // SSRC: 32 bits
    // CSRC list: 0 to 15 items, 32 bits each

    /** Gets the version (V) */
    public int getVersion() {
	if (packet_len >= 12)
	    return (packet[0] >> 6 & 0x03);
	else
	    return 0; // broken packet
    }

    /** Sets the version (V) */
    public void setVersion(int v) {
	if (packet_len >= 12)
	    packet[0] = (byte) ((packet[0] & 0x3F) | ((v & 0x03) << 6));
    }

    /** Whether has padding (P) */
    public boolean hasPadding() {
	if (packet_len >= 12)
	    return getBit(packet[0], 5);
	else
	    return false; // broken packet
    }

    /** Set padding (P) */
    public void setPadding(boolean p) {
	if (packet_len >= 12)
	    packet[0] = setBit(p, packet[0], 5);
    }

    /** Whether has extension (X) */
    public boolean hasExtension() {
	if (packet_len >= 12)
	    return getBit(packet[0], 4);
	else
	    return false; // broken packet
    }

    /** Set extension (X) */
    public void setExtension(boolean x) {
	if (packet_len >= 12)
	    packet[0] = setBit(x, packet[0], 4);
    }

    /** Gets the CSCR count (CC) */
    public int getCscrCount() {
	if (packet_len >= 12)
	    return (packet[0] & 0x0F);
	else
	    return 0; // broken packet
    }

    /** Whether has marker (M) */
    public boolean hasMarker() {
	if (packet_len >= 12)
	    return getBit(packet[1], 7);
	else
	    return false; // broken packet
    }

    /** Set marker (M) */
    public void setMarker(boolean m) {
	if (packet_len >= 12)
	    packet[1] = setBit(m, packet[1], 7);
    }

    /** Gets the payload type (PT) */
    public int getPayloadType() {
	if (packet_len >= 12)
	    return (packet[1] & 0x7F);
	else
	    return -1; // broken packet
    }

    /** Sets the payload type (PT) */
    public void setPayloadType(int pt) {
	if (packet_len >= 12)
	    packet[1] = (byte) ((packet[1] & 0x80) | (pt & 0x7F));
    }

    /** Gets the sequence number */
    public int getSequenceNumber() {
	if (packet_len >= 12)
	    return getInt(packet, 2, 4);
	else
	    return 0; // broken packet
    }

    /** Sets the sequence number */
    public void setSequenceNumber(int sn) {
	if (packet_len >= 12)
	    setInt(sn, packet, 2, 4);
    }

    /** Gets the timestamp */
    public long getTimestamp() {
	if (packet_len >= 12)
	    return getLong(packet, 4, 8);
	else
	    return 0; // broken packet
    }

    /** Sets the timestamp */
    public void setTimestamp(long timestamp) {
	if (packet_len >= 12)
	    setLong(timestamp, packet, 4, 8);
    }

    /** Gets the SSCR */
    public long getSscr() {
	if (packet_len >= 12)
	    return getLong(packet, 8, 12);
	else
	    return 0; // broken packet
    }

    /** Sets the SSCR */
    public void setSscr(long ssrc) {
	if (packet_len >= 12)
	    setLong(ssrc, packet, 8, 12);
    }

    /** Gets the CSCR list */
    public long[] getCscrList() {
	int cc = getCscrCount();
	long[] cscr = new long[cc];
	for (int i = 0; i < cc; i++)
	    cscr[i] = getLong(packet, 12 + 4 * i, 16 + 4 * i);
	return cscr;
    }

    /** Sets the CSCR list */
    public void setCscrList(long[] cscr) {
	if (packet_len >= 12) {
	    int cc = cscr.length;
	    if (cc > 15)
		cc = 15;
	    packet[0] = (byte) (((packet[0] >> 4) << 4) + cc);
	    cscr = new long[cc];
	    for (int i = 0; i < cc; i++)
		setLong(cscr[i], packet, 12 + 4 * i, 16 + 4 * i);
	    // header_len=12+4*cc;
	}
    }

    /** Sets the payload */
    public void setPayload(byte[] payload, int len) {
	if (packet_len >= 12) {
	    int header_len = getHeaderLength();
	    for (int i = 0; i < len; i++)
		packet[header_len + i] = payload[i];
	    packet_len = header_len + len;
	}
    }

    /** Gets the payload */
    public byte[] getPayload() {
	int header_len = getHeaderLength();
	int len = packet_len - header_len;
	byte[] payload = new byte[len];
	for (int i = 0; i < len; i++)
	    payload[i] = packet[header_len + i];
	return payload;
    }

    /** Creates a new RTP packet */
    public RtpPacket(byte[] buffer, int packet_length) {
	packet = buffer;
	packet_len = packet_length;
	if (packet_len < 12)
	    packet_len = 12;
	init(0x0F);
    }

    /** init the RTP packet header (only PT) */
    public void init(int ptype) {
	init(ptype, Random.nextLong());
    }

    /** init the RTP packet header (PT and SSCR) */
    public void init(int ptype, long sscr) {
	init(ptype, Random.nextInt(), Random.nextLong(), sscr);
    }

    /** init the RTP packet header (PT, SQN, TimeStamp, SSCR) */
    public void init(int ptype, int seqn, long timestamp, long sscr) {
	setVersion(2);
	setPayloadType(ptype);
	setSequenceNumber(seqn);
	setTimestamp(timestamp);
	setSscr(sscr);
    }

    // *********************** Private and Static ***********************

    /** Gets int value */
    private static int getInt(byte b) {
	return ((int) b + 256) % 256;
    }

    /** Gets long value */
    private static long getLong(byte[] data, int begin, int end) {
	long n = 0;
	for (; begin < end; begin++) {
	    n <<= 8;
	    n += data[begin];
	}
	return n;
    }

    /** Sets long value */
    private static void setLong(long n, byte[] data, int begin, int end) {
	for (end--; end >= begin; end--) {
	    data[end] = (byte) (n % 256);
	    n >>= 8;
	}
    }

    /** Gets Int value */
    private static int getInt(byte[] data, int begin, int end) {
	return (int) getLong(data, begin, end);
    }

    /** Sets Int value */
    private static void setInt(int n, byte[] data, int begin, int end) {
	setLong(n, data, begin, end);
    }

    /** Gets bit value */
    private static boolean getBit(byte b, int bit) {
	return (b >> bit) == 1;
    }

    /** Sets bit value */
    private static byte setBit(boolean value, byte b, int bit) {
	if (value)
	    return (byte) (b | (1 << bit));
	else
	    return (byte) ((b | (1 << bit)) ^ (1 << bit));
    }
}
