/*
 *
 *  * Copyright 2015 AT&T Foundry
 *  *
 *  * 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 org.onlab.packet;

import java.nio.ByteBuffer;

import static org.onlab.packet.PacketUtils.checkHeaderLength;
import static org.onlab.packet.PacketUtils.checkInput;

/**
 * EAP (Extensible Authentication Protocol) packet.
 */
public class EAP extends BasePacket {
    private static final int HEADER_LENGTH = 4;

    public static final short MIN_LEN = 0x4;
    public static final short EAP_HDR_LEN_REQ_RESP = 5;
    public static final short EAP_HDR_LEN_SUC_FAIL = 4;

    // EAP Code
    public static final byte REQUEST  = 0x1;
    public static final byte RESPONSE = 0x2;
    public static final byte SUCCESS  = 0x3;
    public static final byte FAILURE  = 0x4;

    // EAP Attribute Type
    public static final byte ATTR_IDENTITY = 0x1;
    public static final byte ATTR_NOTIFICATION = 0x2;
    public static final byte ATTR_NAK = 0x3;
    public static final byte ATTR_MD5 = 0x4;
    public static final byte ATTR_OTP = 0x5;
    public static final byte ATTR_GTC = 0x6;
    public static final byte ATTR_TLS = 0xd;

    protected byte code;
    protected byte identifier;
    protected short length;
    protected byte type;
    protected byte[] data;


    /**
     * Gets the EAP code.
     *
     * @return EAP code
     */
    public byte getCode() {
        return this.code;
    }


    /**
     * Sets the EAP code.
     *
     * @param code EAP code
     * @return this
     */
    public EAP setCode(final byte code) {
        this.code = code;
        return this;
    }

    /**
     * Gets the EAP identifier.
     *
     * @return EAP identifier
     */
    public byte getIdentifier() {
        return this.identifier;
    }

    /**
     * Sets the EAP identifier.
     *
     * @param identifier
     * @return this
     */
    public EAP setIdentifier(final byte identifier) {
        this.identifier = identifier;
        return this;
    }

    /**
     * Gets the get packet length.
     *
     * @return packet length
     */
    public short getLength() {
        return this.length;
    }

    /**
     * Sets the packet length.
     *
     * @param length packet length
     * @return this
     */
    public EAP setLength(final short length) {
        this.length = length;
        return this;
    }

    /**
     * Gets the data type.
     *
     * @return data type
     */
    public byte getDataType() {
        return this.type;
    }

    /**
     * Sets the data type.
     *
     * @param type data type
     * @return this
     */
    public EAP setDataType(final byte type) {
        this.type = type;
        return this;
    }

    /**
     * Gets the EAP data.
     *
     * @return EAP data
     */
    public byte[] getData() {
        return this.data;
    }

    /**
     * Sets the EAP data.
     *
     * @param data EAP data to be set
     * @return this
     */
    public EAP setData(final byte[] data) {
        this.data = data;
        return this;
    }

    /**
     * Default EAP constructor that sets the EAP code to 0.
     */
    public EAP() {
        this.code = 0;
    }

    /**
     * EAP constructor that initially sets all fields.
     *
     * @param code EAP code
     * @param identifier EAP identifier
     * @param type packet type
     * @param data EAP data
     */
    public EAP(byte code, byte identifier, byte type, byte[] data) {
        this.code = code;
        this.identifier = identifier;
        if (this.code == REQUEST || this.code == RESPONSE) {
            this.length = (short) (5 + (data == null ? 0 : data.length));
            this.type = type;
        } else {
            this.length = (short) (4 + (data == null ? 0 : data.length));
        }
        this.data = data;
    }

    /**
     * Deserializer for EAP packets.
     *
     * @return deserializer
     */
    public static Deserializer<EAP> deserializer() {
        return (data, offset, length) -> {
            checkInput(data, offset, length, HEADER_LENGTH);

            EAP eap = new EAP();
            final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
            eap.code = bb.get();
            eap.identifier = bb.get();
            eap.length = bb.getShort();

            checkHeaderLength(length, HEADER_LENGTH + eap.length);

            int dataLength;
            if (eap.code == REQUEST || eap.code == RESPONSE) {
                eap.type = bb.get();
                dataLength = eap.length - 5;
            } else {
                dataLength = eap.length - 4;
            }

            eap.data = new byte[dataLength];
            bb.get(eap.data);
            return eap;
        };
    }

    @Override
    public byte[] serialize() {
        final byte[] data = new byte[this.length];

        final ByteBuffer bb = ByteBuffer.wrap(data);
        bb.put(this.code);
        bb.put(this.identifier);
        bb.putShort(this.length);
        if (this.code == REQUEST || this.code == RESPONSE) {
            bb.put(this.type);
        }
        if (this.data != null) {
            bb.put(this.data);
        }
        return data;
    }

    @Override
    public IPacket deserialize(final byte[] data, final int offset,
                               final int length) {
        final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
        this.code = bb.get();
        this.identifier = bb.get();
        this.length = bb.getShort();

        int dataLength;
        if (this.code == REQUEST || this.code == RESPONSE) {
            this.type = bb.get();
            dataLength = this.length - 5;
        } else {
            dataLength = this.length - 4;
        }
        this.data = new byte[dataLength];
        bb.get(this.data);
        return this;
    }

    @Override
    public int hashCode() {
        final int prime = 3889;
        int result = super.hashCode();
        result = prime * result + this.code;
        result = prime * result + this.identifier;
        result = prime * result + this.length;
        result = prime * result + this.type;
        return result;
    }
}

@@ -4826,24 +4826,8 @@
 gth,
- HEADER_LENGTH +
  eap

Fix for HEADER_SIZE error in EAP deserialization