// $Id: ANEPOption.java,v 1.9 2001/02/28 11:54:10 sdalu Exp $ 

package java.net;

/**
 * This class allow to create an ANEP options, this class is used 
 * to send options as well as to receive them. <p>
 *
 * When receiving option, 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>
 *
 * Due to constraint inside the anep option, the length of the payload
 * could be greater that the number of bytes effecively put in it due
 * to 32 bits alignment, so <strong>you can seem to receive MORE
 * DATA that what you have send</strong>. <p>
 *
 * For information here is the structure of an ANEP option as it is
 * send in the ANEP Packet (the length is expressed in 32bit words):
 * <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
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |FLG|       Option Type         |         Option Length         |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |                               .                               |
 *  ~               Option Payload (Option Value)                   ~
 *  |                               .                               |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * </pre>
 *
 * @version $Revision: 1.9 $
 * @author  Stephane D'Alu 
 */


public class ANEPOption implements ANEPOptionType {
    //-- Constants ---------------------------------------------------------

    /**
     * Flag to mark an option has private to the packet type.
     * (ie: this option will only be interpreted by the application)
     */
    final static public int FLAG_PRIVATE = 0x8000;

    /**
     * Flag to say if an unknown option should result in ignoring 
     * the option or discarding the whole packet.
     */
    final static public int FLAG_DISCARD = 0x4000;
	
    /**
     * This is the default flags used when not specified
     */
    final static public int USE_DEFAULT_FLAGS = 0x0000;

    /**
     * Maximal value that an ANEP option type can have.
     */
    final static public int MAX_TYPE = 0x3FFF;

    /**
     * Maximal payload size for the option. <strong>But</strong> this
     * doesn't imply you will be able to send a packet if the option
     * has such a size, due to constraint on the lower layer transport.
     */
    final static public int MAX_LENGTH = (0xFFFF-1)<<2;

    /**
     * Define acceptables values for the flag field
     */
    final static int FLAG_MASK = 0xC000;


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

    int		type;
    int		flags;
    byte[]	payload;
    int		offset;
    int		length;
    boolean	received;

    //-- Constructors ------------------------------------------------------

    /**
     * Create an ANEP option which could be used when
     * sending/receiving ANEP Packet. The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>).
     *
     * @see #FLAG_PRIVATE
     * @see #FLAG_DISCARD
     * @see ANEPOptionType
     *
     * @param type    type of the option
     * @param flags   option flags
     * @param payload option payload
     * @param offset  offset for the payload
     * @param length length for the payload */
    public ANEPOption(int type, int flags, byte[] payload,
		      int offset, int length) {
	setType(type);
	setFlags(flags);
	setData(payload, offset, length);
    }


    /**
     * Create an ANEP option which could be used when
     * sending/receiving ANEP Packet. The <code>offset</code> combined
     * with the <code>length</code> should not exced the size of the
     * payload buffer (<code>payload.length</code>).
     *
     * @see #FLAG_PRIVATE
     * @see #FLAG_DISCARD
     * @see ANEPOptionType
     *
     * @param type    type of the option
     * @param payload option payload
     * @param offset  offset for the payload
     * @param length  length for the payload */
    public ANEPOption(int type, byte[] payload,
		      int offset, int length) {
	this(type, USE_DEFAULT_FLAGS, payload, offset, length);
    }


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

    /**
     * Set the type of the option.
     *
     * @see ANEPOptionType 
     *
     * @param type type of the option
     */
    public synchronized void setType(int type) {
	if (type < 0 || type > MAX_TYPE)
            throw new IllegalArgumentException("type out of range");
	this.type = type;
    }

    /**
     * Set the flag for this option. (the two supported flags
     * are <code>FLAG_DISCARD</code> and <code>FLAG_PRIVATE</code>).
     *
     * @see #FLAG_PRIVATE
     * @see #FLAG_DISCARD
     * 
     * @param flags flags of the option
     */
    public synchronized void setFlags(int flags) {
	if ((flags & ~FLAG_MASK) != 0)
	    throw new IllegalArgumentException("bad flags");
	this.flags = flags;
    }

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


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

    /**
     * Get the type of this option.
     *
     * The value <strong>is not</strong> necessarily the one set with
     * <code>setType</code>, has it could have changed after having
     * received an ANEP packet.
     *
     * @return the option type 
     */
    public synchronized int getType() { 
	return type; 
    }

    /**
     * Get the flags of this option.
     *
     * The value <strong>is not</strong> necessarily the one set with
     * <code>setFlags</code>, has it could have changed after having
     * received an ANEP packet.
     *
     * @return flags value 
     */
    public synchronized int getFlags() {
	return flags;
    }

    /**
     * Get the payload of this option.
     *
     * The value <strong>is not</strong> necessarily the one set with
     * <code>setData</code>, has it could have changed after having
     * received an ANEP packet.
     *
     * @return the option payload 
     */
    public synchronized byte[] getData() { 
	return payload; 
    }

    /**
     * Get the length of the payload.
     *
     * The value <strong> is not</strong> necessarily the one set with
     * <code>setData</code>, has it could have changed after having
     * received an ANEP packet.
     *
     * @return payload length 
     */
    public synchronized int getLength() {
	return length;
    }
    /**
     * Set the length of the payload to send.
     *
     * @param length length of the payload to send.
     *
     * @exception IllegalArgumentException if the length is out of range
     *             or if there was no payload buffer previously associated.
     */
    public synchronized void setLength(int length) {
        if ((length < 0)				|| 
	    (length > MAX_LENGTH)			||
	    (offset < 0)				||
	    (payload == null)				||
            ((length + offset) > payload.length))
            throw new IllegalArgumentException("illegal length or offset");
	this.length = 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;
    }

    /**
     * Returns a boolean to indicate if this option has been received whithin
     * the ANEP packet. It's return value is only meaningful after having 
     * succesfully received an ANEP packet.
     *
     * @return the state of the option (received or not) 
     */
    public boolean isReceived() {
	return received;
    }
}
