/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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 java.net;

import java.io.FileDescriptor;
import java.io.IOException;

import org.apache.harmony.luni.net.NetUtil;
import org.apache.harmony.luni.platform.Platform;

/**
 * The abstract superclass for datagram and multicast socket implementations.
 * 
 * @since Android 1.0
 */
public abstract class DatagramSocketImpl implements SocketOptions {

    /**
     * File descriptor that is used to address this socket.
     * 
     * @since Android 1.0
     */
    protected FileDescriptor fd;

    /**
     * The number of the local port to which this socket is bound.
     * 
     * @since Android 1.0
     */
    protected int localPort;

    /**
     * Constructs an unbound datagram socket implementation.
     * 
     * @since Android 1.0
     */
    public DatagramSocketImpl() {
        localPort = -1;
    }

    /**
     * Binds the datagram socket to the given localhost/port. Sockets must be
     * bound prior to attempting to send or receive data.
     * 
     * @param port
     *            the port on the localhost to bind.
     * @param addr
     *            the address on the multihomed localhost to bind.
     * @exception SocketException
     *                if an error occurs while binding, for example, if the port
     *                has been already bound.
     * @since Android 1.0
     */
    protected abstract void bind(int port, InetAddress addr)
            throws SocketException;

    /**
     * Closes this socket.
     * 
     * @since Android 1.0
     */
    protected abstract void close();

    /**
     * This method allocates the socket descriptor in the underlying operating
     * system.
     * 
     * @throws SocketException
     *             if an error occurs while creating the socket.
     * @since Android 1.0
     */
    protected abstract void create() throws SocketException;

    /**
     * Gets the {@code FileDescriptor} of this datagram socket, which is invalid
     * if the socket is closed or not bound.
     * 
     * @return the current file descriptor of this socket.
     * @since Android 1.0
     */
    protected FileDescriptor getFileDescriptor() {
        return fd;
    }

    /**
     * Gets the local address to which the socket is bound.
     * 
     * @return the local address to which the socket is bound.
     * @since Android 1.0
     */
    InetAddress getLocalAddress() {
        return Platform.getNetworkSystem().getSocketLocalAddress(fd,
                NetUtil.preferIPv6Addresses());
    }

    /**
     * Gets the local port of this socket.
     * 
     * @return the local port to which this socket is bound.
     * @since Android 1.0
     */
    protected int getLocalPort() {
        return localPort;
    }

    /**
     * Gets the value for the specified socket option.
     * 
     * @param optID
     *            the ID of the socket option to be retrieved.
     * @return the requested option value.
     * @exception SocketException
     *                if an error occurs while accessing the option.
     * @since Android 1.0
     */
    public abstract Object getOption(int optID) throws SocketException;

    /**
     * Gets the time-to-live (TTL) for multicast packets sent on this socket.
     * 
     * @return the time-to-live option as a byte value.
     * @throws IOException
     *             if an error occurs while getting the time-to-live option
     *             value.
     * @deprecated Replaced by {@link #getTimeToLive}
     * @see #getTimeToLive()
     * @since Android 1.0
     */
    @Deprecated
    protected abstract byte getTTL() throws IOException;

    /**
     * Gets the time-to-live (TTL) for multicast packets sent on this socket.
     * The TTL option defines how many routers a packet may be pass before it is
     * discarded.
     * 
     * @return the time-to-live option as an integer value.
     * @throws IOException
     *             if an error occurs while getting the time-to-live option
     *             value.
     * @since Android 1.0
     */
    protected abstract int getTimeToLive() throws IOException;

    /**
     * Adds this socket to the multicast group {@code addr}. A socket must join
     * a group before being able to receive data. Further, a socket may be a
     * member of multiple groups but may join any group only once.
     * 
     * @param addr
     *            the multicast group to which this socket has to be joined.
     * @throws IOException
     *             if an error occurs while joining the specified multicast
     *             group.
     * @since Android 1.0
     */
    protected abstract void join(InetAddress addr) throws IOException;

    /**
     * Adds this socket to the multicast group {@code addr}. A socket must join
     * a group before being able to receive data. Further, a socket may be a
     * member of multiple groups but may join any group only once.
     * 
     * @param addr
     *            the multicast group to which this socket has to be joined.
     * @param netInterface
     *            the local network interface which will receive the multicast
     *            datagram packets.
     * @throws IOException
     *             if an error occurs while joining the specified multicast
     *             group.
     * @since Android 1.0
     */
    protected abstract void joinGroup(SocketAddress addr,
            NetworkInterface netInterface) throws IOException;

    /**
     * Removes this socket from the multicast group {@code addr}.
     * 
     * @param addr
     *            the multicast group to be left.
     * @throws IOException
     *             if an error occurs while leaving the group or no multicast
     *             address was assigned.
     * @since Android 1.0
     */
    protected abstract void leave(InetAddress addr) throws IOException;

    /**
     * Removes this socket from the multicast group {@code addr}.
     * 
     * @param addr
     *            the multicast group to be left.
     * @param netInterface
     *            the local network interface on which this socket has to be
     *            removed.
     * @throws IOException
     *             if an error occurs while leaving the group.
     * @since Android 1.0
     */
    protected abstract void leaveGroup(SocketAddress addr,
            NetworkInterface netInterface) throws IOException;

    /**
     * Peeks at the incoming packet to this socket and returns the address of
     * the {@code sender}. The method will block until a packet is received or
     * timeout expires.
     * 
     * @param sender
     *            the origin address of a packet.
     * @return the address of {@code sender} as an integer value.
     * @exception IOException
     *                if an error or a timeout occurs while reading the address.
     * @since Android 1.0
     */
    protected abstract int peek(InetAddress sender) throws IOException;

    /**
     * Receives data and stores it in the supplied datagram packet {@code pack}.
     * This call will block until either data has been received or, if a timeout
     * is set, the timeout has expired. If the timeout expires an {@code
     * InterruptedIOException} is thrown.
     * 
     * @param pack
     *            the datagram packet container to fill in the received data.
     * @exception IOException
     *                if an error or timeout occurs while receiving data.
     * @since Android 1.0
     */
    protected abstract void receive(DatagramPacket pack) throws IOException;

    /**
     * Sends the given datagram packet {@code pack}. The packet contains the
     * data and the address and port information of the target host as well.
     * 
     * @param pack
     *            the datagram packet to be sent.
     * @exception IOException
     *                if an error occurs while sending the packet.
     * @since Android 1.0
     */
    protected abstract void send(DatagramPacket pack) throws IOException;

    /**
     * Sets the value for the specified socket option.
     * 
     * @param optID
     *            the ID of the socket option to be set.
     * @param val
     *            the value of the option.
     * @exception SocketException
     *                if an error occurs while setting the option.
     * @since Android 1.0
     */
    public abstract void setOption(int optID, Object val)
            throws SocketException;

    /**
     * Sets the time-to-live (TTL) option for multicast packets sent on this
     * socket.
     * 
     * @param ttl
     *            the time-to-live option value. Valid values are 0 &lt; ttl
     *            &lt;= 255.
     * @throws IOException
     *             if an error occurs while setting the option.
     * @since Android 1.0
     */
    protected abstract void setTimeToLive(int ttl) throws IOException;

    /**
     * Sets the time-to-live (TTL) option for multicast packets sent on this
     * socket.
     * 
     * @param ttl
     *            the time-to-live option value. Valid values are 0 &lt; ttl
     *            &lt;= 255.
     * @throws IOException
     *             if an error occurs while setting the option.
     * @deprecated Replaced by {@link #setTimeToLive}
     * @see #setTimeToLive(int)
     * @since Android 1.0
     */
    @Deprecated
    protected abstract void setTTL(byte ttl) throws IOException;

    /**
     * Connects this socket to the specified remote address and port.
     * 
     * @param inetAddr
     *            the address of the target host which has to be connected.
     * @param port
     *            the port on the target host which has to be connected.
     * @exception SocketException
     *                if the datagram socket cannot be connected to the
     *                specified remote address and port.
     * @since Android 1.0
     */
    protected void connect(InetAddress inetAddr, int port)
            throws SocketException {
        // do nothing
    }

    /**
     * Disconnects this socket from the remote host.
     * 
     * @since Android 1.0
     */
    protected void disconnect() {
        // do nothing
    }

    /**
     * Receives data into the supplied datagram packet by peeking. The data is
     * not removed from socket buffer and can be received again by another
     * {@code peekData()} or {@code receive()} call. This call blocks until
     * either data has been received or, if a timeout is set, the timeout has
     * been expired.
     * 
     * @param pack
     *            the datagram packet used to store the data.
     * @return the port the packet was received from.
     * @exception IOException
     *                if an error occurs while peeking at the data.
     * @since Android 1.0
     */
    protected abstract int peekData(DatagramPacket pack) throws IOException;
}
