/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.rtp;

import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.SocketException;

/**
 * RtpStream represents the base class of streams which send and receive network
 * packets with media payloads over Real-time Transport Protocol (RTP).
 *
 * <p class="note">Using this class requires
 * {@link android.Manifest.permission#INTERNET} permission.</p>
 */
public class RtpStream {
    /**
     * This mode indicates that the stream sends and receives packets at the
     * same time. This is the initial mode for new streams.
     */
    public static final int MODE_NORMAL = 0;

    /**
     * This mode indicates that the stream only sends packets.
     */
    public static final int MODE_SEND_ONLY = 1;

    /**
     * This mode indicates that the stream only receives packets.
     */
    public static final int MODE_RECEIVE_ONLY = 2;

    private static final int MODE_LAST = 2;

    private final InetAddress mLocalAddress;
    private final int mLocalPort;

    private InetAddress mRemoteAddress;
    private int mRemotePort = -1;
    private int mMode = MODE_NORMAL;

    private int mSocket = -1;
    static {
        System.loadLibrary("rtp_jni");
    }

    /**
     * Creates a RtpStream on the given local address. Note that the local
     * port is assigned automatically to conform with RFC 3550.
     *
     * @param address The network address of the local host to bind to.
     * @throws SocketException if the address cannot be bound or a problem
     *     occurs during binding.
     */
    RtpStream(InetAddress address) throws SocketException {
        mLocalPort = create(address.getHostAddress());
        mLocalAddress = address;
    }

    private native int create(String address) throws SocketException;

    /**
     * Returns the network address of the local host.
     */
    public InetAddress getLocalAddress() {
        return mLocalAddress;
    }

    /**
     * Returns the network port of the local host.
     */
    public int getLocalPort() {
        return mLocalPort;
    }

    /**
     * Returns the network address of the remote host or {@code null} if the
     * stream is not associated.
     */
    public InetAddress getRemoteAddress() {
        return mRemoteAddress;
    }

    /**
     * Returns the network port of the remote host or {@code -1} if the stream
     * is not associated.
     */
    public int getRemotePort() {
        return mRemotePort;
    }

    /**
     * Returns {@code true} if the stream is busy. In this case most of the
     * setter methods are disabled. This method is intended to be overridden
     * by subclasses.
     */
    public boolean isBusy() {
        return false;
    }

    /**
     * Returns the current mode.
     */
    public int getMode() {
        return mMode;
    }

    /**
     * Changes the current mode. It must be one of {@link #MODE_NORMAL},
     * {@link #MODE_SEND_ONLY}, and {@link #MODE_RECEIVE_ONLY}.
     *
     * @param mode The mode to change to.
     * @throws IllegalArgumentException if the mode is invalid.
     * @throws IllegalStateException if the stream is busy.
     * @see #isBusy()
     */
    public void setMode(int mode) {
        if (isBusy()) {
            throw new IllegalStateException("Busy");
        }
        if (mode < 0 || mode > MODE_LAST) {
            throw new IllegalArgumentException("Invalid mode");
        }
        mMode = mode;
    }

    /**
     * Associates with a remote host. This defines the destination of the
     * outgoing packets.
     *
     * @param address The network address of the remote host.
     * @param port The network port of the remote host.
     * @throws IllegalArgumentException if the address is not supported or the
     *     port is invalid.
     * @throws IllegalStateException if the stream is busy.
     * @see #isBusy()
     */
    public void associate(InetAddress address, int port) {
        if (isBusy()) {
            throw new IllegalStateException("Busy");
        }
        if (!(address instanceof Inet4Address && mLocalAddress instanceof Inet4Address) &&
                !(address instanceof Inet6Address && mLocalAddress instanceof Inet6Address)) {
            throw new IllegalArgumentException("Unsupported address");
        }
        if (port < 0 || port > 65535) {
            throw new IllegalArgumentException("Invalid port");
        }
        mRemoteAddress = address;
        mRemotePort = port;
    }

    int getSocket() {
        return mSocket;
    }

    /**
     * Releases allocated resources. The stream becomes inoperable after calling
     * this method.
     *
     * @throws IllegalStateException if the stream is busy.
     * @see #isBusy()
     */
    public void release() {
        synchronized (this) {
            if (isBusy()) {
                throw new IllegalStateException("Busy");
            }
            close();
        }
    }

    private native void close();

    @Override
    protected void finalize() throws Throwable {
        close();
        super.finalize();
    }
}
