// $Id: ANEPPacket.java,v 1.4 2000/02/14 19:01:44 sdalu Exp $

package java.net;

import java.io.*;

/**
 * This class define an ANEP packet, and is used to send or receive data.<p>
 *
 * When receiving data, you need to specified the length of
 * data you expect to receive, if there is less data the length
 * will be changed, if there is more the data will be truncated. <p>
 *
 * There is not fundamental difference between constructors used
 * for sending or receiving packet, except the fact that when
 * receiving packet you need to specify less information. <p>
 *
 * For information the ANEP packet header looks like:
 * <pre>
 *   0                   1                   2                   3
 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |   Version     |     Flags     |            Type ID            |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |      ANEP Header Length       |     ANEP Packet Length        |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  ~                            Options                            ~
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |                               .                               |
 *  ~                            Payload                            ~
 *  |                               .                               |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * </pre>
 *
 * @version $Revision: 1.4 $
 * @author Stephane D'Alu
 */


public class ANEPPacket implements ANEPPacketType {
    //-- Functions ---------------------------------------------------------
    final static void checkType(int type) {
	if (type < 0 || type > MAX_TYPE)
            throw new IllegalArgumentException("Packet type out of range");
    }

    //-- Constants ---------------------------------------------------------

    /**
     * Flag to discard the packet if the receiving node is not able
     * to deal with the packet type, otherwise if able, the node will
     * try to forward it.
     */
    final static public int FLAG_DISCARD = 1;

    /**
     * Maximal TypeId for a packet
     */
    final static public int MAX_TYPE = 0xFFFF;

    /**
     * This is used to indicate to use the system default value for the
     * ANEP packet flag. This <strong>is not</strong> a flag, but a special
     * value.
     */
    final static public int USE_DEFAULT_FLAGS = -1;

    /**
     * Define the value of flags
     */
    final static int FLAGS_MASK = 0x1;

    //-- Variables ---------------------------------------------------------

    int	       offset;
    int        length;
    int        flags;
    int        type;
    byte[]     payload;
    ANEPOption[] options;
    InetAddress address;


    //-- Constructors (for sending) ----------------------------------------

    /**
     * Constructs an ANEP packet for sending packets of length 
     * <code>length</code> to the specified host. 
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * <strong>Due to implementation constraints the only value
     * accepted (for now) for <code>flags</code> is 
     * <code>USE_DEFAULT_FLAGS</code></strong><p>
     *
     * @see ANEPPacketType
     *
     * @param   type     the packet type.
     * @param	flags    the packet flags
     * @param   payload  the packet data.
     * @param   length   the length for the buffer.
     * @param	offset   the offset for the buffer.
     * @param	options  the ANEP options.
     * @param   addr     the destination address.
     * @see     java.net.InetAddress
     */
    public ANEPPacket(int type, int flags, 
		      byte payload[], int offset, int length, 
		      ANEPOption[] options,
		      InetAddress address) {
	setData(payload, offset, length);
	setType(type);
	setFlags(flags);
	setOptions(options);
	setAddress(address);
    }
 
    /**
     * Constructs an ANEP packet for sending packets of length 
     * <code>length</code> to the specified host. 
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * @see ANEPPacketType
     *
     * @param   type     the packet type.
     * @param   payload  the packet data.
     * @param   length   the length for the buffer.
     * @param	offset   the offset for the buffer.
     * @param   addr     the destination address.
     * @see     java.net.InetAddress
     */
    public ANEPPacket(int type,
		      byte payload[], int offset, int length, 
		      InetAddress addr) {
	this(type, USE_DEFAULT_FLAGS, payload, offset, length, null, addr);
    }

    /**
     * Constructs an ANEP packet for sending packets of length 
     * <code>length</code> to the specified host. 
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * @see ANEPPacketType
     *
     * @param   type     the packet type.
     * @param   payload  the packet data.
     * @param   length   the length for the buffer.
     * @param	offset   the offset for the buffer.
     * @param	options  the ANEP options.
     * @param   addr     the destination address.
     * @see     java.net.InetAddress
     */
    public ANEPPacket(int type, 
		      byte payload[], int offset, int length,
		      ANEPOption[] options,
		      InetAddress addr) {
	this(type, USE_DEFAULT_FLAGS, payload, offset, length, options, addr);
    }


    /**
     * Constructs an ANEP packet for receiving packets of length 
     * <code>length</code> to the specified host. 
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * @param   payload  the packet data.
     * @param   length   the length for the buffer.
     * @param	offset   the offset for the buffer.
     * @param	options  the ANEP options.
     * @see     java.net.InetAddress
     */
    public ANEPPacket(byte payload[], int offset, int length, 
		      ANEPOption[] options) {
	setData(payload, offset, length);
	setOptions(options);
    }
 
    /**
     * Constructs an ANEP packet for receiving packets of length 
     * <code>length</code> to the specified host. 
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * @param   payload  the packet data.
     * @param   length   the length for the buffer.
     * @param	offset   the offset for the buffer.
     * @see     java.net.InetAddress
     */
    public ANEPPacket(byte payload[], int offset, int length) {
	this(payload, offset, length, (ANEPOption []) null);
    }



    //-- Modifiers ---------------------------------------------------------

    /** 
     * Set the data buffer for this packet. This sets the
     * payload, length and offset of the packet.
     *
     * The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>). <p>
     *
     * @param payload the buffer to set for this packet
     * @param offset  the offset into the data
     * @param length  the length of the data
     *
     * @exception NullPointerException if the argument is null
     *
     * @see #getData
     * @see #getOffset
     * @see #getLength
     */
    public synchronized void setData(byte[] payload, int offset, int length) {
        if (length < 0 || offset < 0 ||
            ((length + offset) > payload.length))
            throw new IllegalArgumentException("illegal length or offset");

        this.payload = payload;
        this.length  = length;
        this.offset  = offset;
    }

    /**
     * Set the address of the receiver.
     *
     * @param iaddr address of the receiver
     */
    public synchronized void setAddress(InetAddress iaddr) {
        address = iaddr;
    }

    /**
     * Set the ANEP type of the packet.
     *
     * @param type ANEP type Id
     */
    public synchronized void setType(int type) {
	checkType(type);
        this.type = type;
    }

    /**
     * Set the flags for the ANEP packet. Ony the <code>FLAG_DISCARD</code>
     * is supported. <p>
     *
     * For performance, it is better to set the flags on the socket level,
     * and use the default value for the packet than setting the flags for
     * every packet. <p>
     * 
     * <strong>Due to implementation constraints the only value
     * accepted (for now) is <code>USE_DEFAULT_FLAGS</code></strong><p>
     *
     * @see SocketOptions#ANEP_DISCARD
     * @see #USE_DEFAULT_FLAGS
     * @see #FLAG_DISCARD
     *
     * @param flags flags to use
     */
    public synchronized void setFlags(int flags) {
	// ::TODO:: remove limitation
	if (flags != USE_DEFAULT_FLAGS)
            throw new IllegalArgumentException("Packet flags incorrect");

	if ((flags != USE_DEFAULT_FLAGS) && ((flags & ~FLAGS_MASK) != 0))
            throw new IllegalArgumentException("Packet flags incorrect");
	this.flags = flags;
    }

    /**
     * Specify the options to be send with the main data.
     * 
     * The same option type <strong>should not</strong> appear more
     * than once in the list, otherwise the behaviour is unspecified.
     * (this could in a futur version throw an exception)
     *
     * @param options an array of options to send.
     */
    public synchronized void setOptions(ANEPOption[] options) {
	this.options = options;
    }

    //-- Observers ---------------------------------------------------------

    /**
     * Get the ANEP packet flags which are defined here as FLAG_*.
     * This information is not available when a packet has been
     * received, so calling this method will return a 
     * meaningless information.
     *
     * @return the ANEP packet flags or the <code>USE_DEFAULT_FLAGS</code>
     *         value.
     *
     * @see ANEPSocket#receive
     */
    public synchronized int getFlags() { 
	return flags; 
    }

    /**
     * Get the ANEP packet options.
     *
     * @return the ANEP options
     */
    public synchronized ANEPOption[] getOptions() { 
	return options; 
    }

    /**
     * Get the packet type Id.
     *
     * @see ANEPPacketType
     *
     * @return packet type Id
     */
    public synchronized int getType() { 
	return type;
    }

    /**
     * Get the address of the destination.
     *
     * @return address of the packet destination
     */
    public synchronized InetAddress getAddress() {
	return address;
    }

    /**
     * Get the packet payload.
     *
     * @return packet payload
     */
    public synchronized byte[] getData() { 
	return payload; 
    }

    /**
     * Returns the length of the data to be sent or the length of the
     * data received.
     *
     * @return  the length of the data to be sent or the length of the
     *          data received.
     */
    public synchronized int getLength() {
        return length;
    }

    /**
     * Returns the offset of the data to be sent or the offset of the
     * data received.
     *
     * @return  the offset of the data to be sent or the offset of the
     *          data received.
     */
    public synchronized int getOffset() {
        return offset;
    }
}
