// $Id: PlainANEPSocketImpl.java,v 1.6 2001/01/08 13:09:31 sdalu Exp $

package java.net;

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

/**
 * Concrete datagram and multicast socket implementation base class.
 * Note: This is not a public class, so that applets cannot call
 * into the implementation directly and hence cannot bypass the
 * security checks present in the DatagramSocket and MulticastSocket
 * classes. <p>
 *
 * Lots of the methods are not synchronized (ie: allowOptionTypes,
 * setOption, ...), so one need to be carefull when using them,
 * especially the way they can interract with send and receive.
 *
 * <code>send</code> is not synchronized, because one should be able
 * to send data in one thread when another is waiting for data,
 * the draw back is that <code>send</code> and <code>receive</code>
 * are anchoring information to the socket, and we need to ensure
 * they don't interact with each others.
 */

class PlainANEPSocketImpl extends ANEPSocketImpl {

    /* timeout value for receive() */
    private int timeout = 0;

    /* flow label */
    private int flowLabel = 0; 

    /* traffic class value */
    private int trafficClass = 0;

    /* a lock */
    private Object sendLock = new Object();

    /**
     * Load net library into runtime.
     */
    static {
	java.security.AccessController.doPrivileged(
		  new sun.security.action.LoadLibraryAction("net6"));
	init();
    }

    /**
     * Options that are allowed to be received
     * @param optionTypes array of option types
     */
    protected native void allowOptionTypes(int [] optionTypes) 
	throws SocketException;

    /**
     * Creates a datagram socket
     */
    protected synchronized void create() throws SocketException {
	fd = new FileDescriptor();
	anepSocketCreate();
    }

    /**
     * Binds a datagram socket to a local port.
     */
    protected synchronized native void bind(int ltype, InetAddress laddr)
        throws IOException;

    /**
     * Sends a datagram packet. The packet contains the data and the
     * destination address to send the packet to.
     * @param packet to be sent.
     */
    protected void send(ANEPPacket p) throws IOException {
	synchronized(sendLock) {
	    _send(p);
	}
    }

    /**
     * Peek at the packet to see who it is from.
     * @param return the address which the packet came from.
     */
    protected synchronized native int peek(InetAddress i) throws IOException;

    /**
     * Receive the datagram packet.
     * @param Packet Received.
     */
    protected synchronized native void receive(ANEPPacket p)
        throws IOException;

    /**
     * Set the TTL (time-to-live) option.
     * @param TTL to be set.
     */
    protected native void setHOPS(int ttl) throws IOException;

    /**
     * Get the TTL (time-to-live) option.
     */
    protected native int getHOPS() throws IOException;

    /**
     * Join the multicast group.
     * @param multicast address to join.
     */
    protected native void join(InetAddress inetaddr) 
	throws IOException;

    /**
     * Leave the multicast group.
     * @param multicast address to leave.
     */
    protected native void leave(InetAddress inetaddr) 
	throws IOException;

    /**
     * Close the socket.
     */
    protected void close() {
	if (fd != null) {
	    anepSocketClose();
	    fd = null;
	}
    }

    /**
     * Finalizer
     */
    protected void finalize() {
	close();
    }

    /**
     * set a value - since we only support (setting) binary options
     * here, o must be a Boolean
     */
     public void setOption(int optID, Object o) 
	 throws SocketException {
	 switch (optID) {
	    /* check type safety before going native.  These should never
	     * fail, since only java.Socket* has access to
	     * PlainSocketImpl.setOption().
	     */
	 case SO_TIMEOUT:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for SO_TIMEOUT");
	     }
	     int tmp = ((Integer) o).intValue();
	     if (tmp < 0)
		 throw new IllegalArgumentException("timeout < 0");
	     timeout = tmp;
	     return;
	 case IPV6_FLOW_LABEL:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for FLOW_LABEL");
	     }
	     int tmp_2 = ((Integer) o).intValue();
	     if (tmp_2 < 0)
		 throw new IllegalArgumentException("flow label < 0");
	     if (tmp_2 >= 0xffffff)
		 throw new IllegalArgumentException("flow label < 17377216");
	     flowLabel = tmp_2;
	     return;
	 case IPV6_TRAFFIC_CLASS:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for TRAFFIC_CLASS");
	     }
	     int tmp_3 = ((Integer) o).intValue();
	     if (tmp_3 < 0)
		 throw new IllegalArgumentException("traffic class < 0");
	     if (tmp_3 >= 0xf)
		 throw new IllegalArgumentException("traffic class > 5");
	     trafficClass = tmp_3;
	     return;
	 case SO_BINDADDR:
	     throw new SocketException("Cannot re-bind Socket");
	 case SO_REUSEADDR:
	     if (o == null || !(o instanceof Integer)) {
		 throw new SocketException("bad argument for SO_REUSEADDR");
	     }
	     break;
	case IPV6_AUTH_TRANS_LEVEL: 
	case IPV6_AUTH_NETWORK_LEVEL: 
	case IPV6_ESP_TRANS_LEVEL: 
	case IPV6_ESP_NETWORK_LEVEL: 
	     if (o == null || !(o instanceof Integer) ||
		!(((Integer)o).intValue() > -1) ||
		!(((Integer)o).intValue() < 6))
		 throw new SocketException("bad parameter for IPsec option");
	     
	     break;
	 case SO_RCVBUF:
	 case SO_SNDBUF:
	     if (o == null || !(o instanceof Integer) ||
		 ((Integer)o).intValue() < 0) {
		 throw new SocketException("bad argument for SO_SNDBUF or " +
					   "SO_RCVBUF");
	     }
	     break;
	 case IPV6_MULTICAST_IF:
	     if (o == null || !(o instanceof Integer))
		 throw new SocketException("bad argument for IP_MULTICAST_IF");
	     break;
	 default:
	     throw new SocketException("invalid option: " + optID);
	 }
	 socketSetOption(optID, o);
     }

    /**
     * get option's state - set or not
     */
    public Object getOption(int optID) throws SocketException {
	// cached value
	switch(optID) {
	case SO_TIMEOUT:
	    return new Integer(timeout);
	case IPV6_TRAFFIC_CLASS:
	    return new Integer(trafficClass);
	case IPV6_FLOW_LABEL:
	    return new Integer(flowLabel);
	}

	// get socket option
	byte[] ret = socketGetOption(optID);
	switch(optID) {
	case SO_BINDADDR: {
	    InetAddress in = new InetAddress();
	    for(int i = 0; i < 16; i++)
		in.address[i] = ret[i];	     
	    return in;
	}
	case SO_REUSEADDR:
	case SO_RCVBUF:
	case SO_SNDBUF:
	case IPV6_MULTICAST_IF:
	case IPV6_AUTH_NETWORK_LEVEL:
	case IPV6_AUTH_TRANS_LEVEL:
	case IPV6_ESP_NETWORK_LEVEL:
	case IPV6_ESP_TRANS_LEVEL: {
	    int aux;
	    
	    aux  =   ret[3]        & 0xFF;
	    aux |= ((ret[2] << 8)  & 0xFF00);
	    aux |= ((ret[1] << 16) & 0xFF0000);
	    aux |= ((ret[0] << 24) & 0xFF000000);
	    return (new Integer(aux));	
	}
	}

	// don't know about it
	return null;
    }
    
    private native void anepSocketCreate() throws SocketException;
    private native void anepSocketClose();
    private native void socketSetOption(int opt, Object val)
        throws SocketException;
    private native byte[] socketGetOption(int opt) throws SocketException;
    protected native void _send(ANEPPacket p) throws IOException;


    /**
     * Perform class load-time initializations.
     */
    private native static void init();

   
}

