/*
 * Copyright 2007 Sun Microsystems, Inc.
 *
 * This file is part of jVoiceBridge.
 *
 * jVoiceBridge is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation and distributed hereunder 
 * to you.
 *
 * jVoiceBridge 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 program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Sun designates this particular file as subject to the "Classpath"
 * exception as provided by Sun in the License file that accompanied this 
 * code. 
 */

package com.sun.voip;

/*
 * Packet for sending RTP data
 */
public class RtpSenderPacket extends RtpPacket {
    private static boolean comfortNoise = true;

    private long packetsSent = 0;
    private long comfortPayloadSentCount = 0;
    private long timeComfortPayloadSent;

    /**
     * Initialize an Rtp sender packet.
     */
    public RtpSenderPacket(int encoding, int sampleRate, int channels) {
	super(encoding, sampleRate, channels);

        //
        // Initialize RTP header
        //
        buffer[0]  = (byte)0x80;   		  // version 2
        buffer[1]  = (byte)MARK_BIT | PCMU_PAYLOAD; // MARK bit and payload
        buffer[2]  = (byte)0;      		  // sequence #
        buffer[3]  = (byte)1;
        buffer[4]  = (byte)0;      		  // rtpTimestamp
        buffer[5]  = (byte)0;
        buffer[6]  = (byte)0;
        buffer[7]  = (byte)0;

        long now = System.currentTimeMillis();

	buffer[8]  = (byte)((now >> 24) & 0xff); // Synchronization Source SSRC id
	buffer[9]  = (byte)((now >> 16) & 0xff);
	buffer[10] = (byte)((now >> 8) & 0xff);
	buffer[11] = (byte)(now & 0xff);
    }

    /**
     * set the header fields for a comfort payload change
     *
     * After sending a comfort noise payload, 
     * we keep track of how much time has passed until we start
     * sending data again.  We then update the rtpTimestamp
     * with the number of PACKET_PERIOD samples (rounded up).
     */
    public void setComfortPayload() {
	buffer[1] = COMFORT_PAYLOAD;

	setLength(RtpPacket.HEADER_SIZE + 1);

	comfortPayloadSentCount++;

	timeComfortPayloadSent = System.currentTimeMillis();
    }

    public void setComfortNoiseLevel(byte comfortNoiseLevel) {
	buffer[RtpPacket.DATA] = comfortNoiseLevel;
    }

    /**
     * increment the RTP sequence number
     */
    public void incrementRtpSequenceNumber() {
        rtpSequenceNumber++;
        buffer[2] = (byte)((rtpSequenceNumber >> 8) & 0xff);
        buffer[3] = (byte)(rtpSequenceNumber & 0xff);
    }
    
    /**
     * Update the RTP sequence number and timestamp.  This method is called
     * after sending a packet of data.  The reason this isn't done automatically
     * is because the mixer sends packets with the same sequence # and
     * time stamp to multiple members.
     */
    public void updateRtpHeader(int size) {
	rtpSequenceNumber++;
        buffer[0] = (byte)0x80;           // version 2
        buffer[1] &= ~MARK_BIT;   	  // clear MARK bit
        buffer[2] = (byte)((rtpSequenceNumber >> 8) & 0xff);
        buffer[3] = (byte)(rtpSequenceNumber & 0xff);

        /*
         * adjust RTP header time stamp
         */
	adjustRtpTimestamp(size - HEADER_SIZE);
    }

    /**
     * Adjust the RTP Timestamp.  This method is called when we are about
     * to resume sending data after sending a COMFORT_PAYLOAD.
     *
     * Calulate the number of samples that would have been
     * sent during the time between when the comfort payload
     * was sent and now.
     *
     * The timestamp is adjusted by the amount of time we stopped sending data.
     *
     * @param adjustment long to add to the RTP timestamp.
     */
    public void adjustRtpTimestamp() {
        /*
         * Elapsed time since comfort payload sent multiplied
         * by the number of samples per second gives the
         * number of samples by which the RTP timestamp must
         * be adjusted.
         */
	if (timeComfortPayloadSent == 0) {
	    Logger.error("RtpSenderPacket:  timeComfortPayloadSent is 0!");
	    return;
	}

	adjustRtpTimestamp(System.currentTimeMillis() - timeComfortPayloadSent);
	timeComfortPayloadSent = 0;
    }

    /*
     * Adjust the RTP timestamp by the specified amount.
     * This adjustment is necessary after a comfort payload change
     * or when there is a long pause.
     */
    public void adjustRtpTimestamp(long elapsed) {
        int adjustment = (int)
	    (elapsed * (getDataSize() / RtpPacket.PACKET_PERIOD));

        adjustment = ((adjustment + getDataSize() - 1) / getDataSize()) *
            getDataSize();

        buffer[1] |= MARK_BIT; 
	adjustRtpTimestamp(adjustment);
    }

    private void adjustRtpTimestamp(int adjustment) {
	rtpTimestamp += adjustment;

        buffer[4] = (byte)((rtpTimestamp >> 24) & 0xff);
        buffer[5] = (byte)((rtpTimestamp >> 16) & 0xff);
        buffer[6] = (byte)((rtpTimestamp >> 8) & 0xff);
        buffer[7] = (byte)(rtpTimestamp & 0xff);
    }

    public void incrementPacketsSent() {
	packetsSent++;
    }

    public long getPacketsSent() {
	return packetsSent;
    }

    public long getComfortPayloadSentCount() {
	return comfortPayloadSentCount;
    }

    /*
     * Make sure sequence number wraps properly.
     */
    public static void main(String[] args) {
	RtpSenderPacket packet = new RtpSenderPacket(
	    PCM_ENCODING, 8000, 1);

	Logger.logLevel = 5;

	short expected = packet.rtpSequenceNumber;

	while (true) {
	    if (packet.rtpSequenceNumber != expected) {
		Logger.println("expected " + expected
		    + " got " + packet.rtpSequenceNumber);
	    }

	    packet.rtpSequenceNumber++;
	    expected = packet.rtpSequenceNumber;
	}
    }

}
