// Path: core/src/net/jazdw/jnacan/CanId.java
// @Data
// public class CanId {
//     int id;
//     
//     public CanId(int id) {
//         this.id = id;
//     }
//     
//     /**
//      * Gets the ID as standard frame format
//      * @return
//      */
//     public int getSFFid() {
//         return id & CAN_SFF_MASK;
//     }
//     
//     /**
//      * Sets the standard frame format ID
//      * @param id
//      */
//     public void setSFFid(int id) {
//         int newId = this.id & ~CAN_SFF_MASK;
//         this.id = newId | (id & CAN_SFF_MASK);
//     }
//     
//     /**
//      * Gets the ID as extended frame format
//      * @return
//      */
//     public int getEFFid() {
//         return id & CAN_SFF_MASK;
//     }
//     
//     /**
//      * Sets the extended frame format ID
//      * @param id
//      */
//     public void setEFFid(int id) {
//         int newId = this.id & ~CAN_EFF_MASK;
//         this.id = newId | (id & CAN_EFF_MASK);
//     }
//     
//     /**
//      * Sets this ID to extended frame format mode
//      */
//     public void setEFF() {
//         id |= CAN_EFF_FLAG;
//     }
//     
//     /**
//      * Sets this ID back to standard frame format mode
//      */
//     public void clearEFF() {
//         id &= ~CAN_EFF_FLAG;
//     }
//     
//     /**
//      * @return true if extended frame format
//      */
//     public boolean isEFF() {
//         return (id & CAN_EFF_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Sets the RTR (remote response) flag
//      */
//     public void setRTR() {
//         id |= CAN_RTR_FLAG;
//     }
//     
//     /**
//      * Clears the RTR (remote response) flag
//      */
//     public void clearRTR() {
//         id &= ~CAN_RTR_FLAG;
//     }
//     
//     /**
//      * @return true if is remote response
//      */
//     public boolean isRTR() {
//         return (id & CAN_RTR_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Sets the error flag
//      */
//     public void setERR() {
//         id |= CAN_ERR_FLAG;
//     }
//     
//     /**
//      * Clears the error flag
//      */
//     public void clearERR() {
//         id &= ~CAN_ERR_FLAG;
//     }
//     
//     /**
//      * @return true if is an error frame
//      */
//     public boolean isERR() {
//         return (id & CAN_ERR_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Used when specifying inverted CAN ID filters
//      * Do not use for sending CAN frames
//      */
//     public void setInverted() {
//         id |= CLibrary.CAN_INV_FILTER;
//     }
//     
//     /**
//      * Clears the inverted property of a CAN ID filter
//      */
//     public void clearInverted() {
//         id &= ~CLibrary.CAN_INV_FILTER;
//     }
//     
//     /**
//      * Used when specifying inverted CAN ID filters
//      * @return
//      */
//     public boolean isInverted() {
//         return (id & CLibrary.CAN_INV_FILTER) != 0;
//     }
// 
//     @Override
//     public String toString() {
//         String ret;
//         if (isEFF()) {
//             ret = String.format("%08X", getEFFid());
//         }
//         else {
//             ret = String.format("%03X", getSFFid());
//         }
//         if (isRTR()) {
//             ret += " RTR";
//         }
//         return ret;
//     }
// }
// 
// Path: netty/src/net/jazdw/jnacan/netty/tp20/Tp20ApplicationType.java
// public enum Tp20ApplicationType implements ValueEnum<Byte> {
//     DIAGNOSTICS(0x01),
//     INFOTAINMENT(0x20),
//     APPROTOCOL(0x20),
//     WFS_WIV(0x21);
// 
//     private static ReverseEnumMap<Byte, Tp20ApplicationType> map = ReverseEnumMap.create(Tp20ApplicationType.class);
//     private byte value;
//     
//     private Tp20ApplicationType(int value) {
//         this.value = (byte) value;
//     }
// 
//     @Override
//     public Byte value() {
//         return value;
//     }
//     
//     public static Tp20ApplicationType fromValue(byte value) {
//         return map.get(value);
//     }
// }


import lombok.Data;
import net.jazdw.jnacan.CanId;
import net.jazdw.jnacan.netty.tp20.Tp20ApplicationType;
        // TODO EFF supported?
        int txId = this.txId.getSFFid();
        int rxId = this.rxId.getSFFid();
        
        data[2] = (byte) txId;
        data[3] = (byte) ((1 << 4) | (txId >> 8));
        data[4] = (byte) rxId;
        data[5] = (byte) (rxId >> 8);
        data[6] = applicationType.value();
        return data;
    }
    
    @Override
    public void decode(byte[] data) {
        if (data.length != 7) {
            throw new IllegalArgumentException("Channel connect data length should be 7 bytes");
        }
        
        opCode = Tp20OpCode.fromValue(data[1]);
        switch (opCode) {
        case CHANNEL_REPLY_APPLICATION_TYPE_UNAVAILABLE:
        case CHANNEL_REPLY_APPLICATION_TYPE_UNSUPPORTED:
        case CHANNEL_REPLY_OUT_OUT_RESOURCES:
        case CHANNEL_REPLY_POSITIVE:
        case CHANNEL_REQUEST:
            break;
        default:
            throw new IllegalArgumentException("Wrong op code for channel connect");
        }
        
// Path: core/src/net/jazdw/jnacan/CanId.java
// @Data
// public class CanId {
//     int id;
//     
//     public CanId(int id) {
//         this.id = id;
//     }
//     
//     /**
//      * Gets the ID as standard frame format
//      * @return
//      */
//     public int getSFFid() {
//         return id & CAN_SFF_MASK;
//     }
//     
//     /**
//      * Sets the standard frame format ID
//      * @param id
//      */
//     public void setSFFid(int id) {
//         int newId = this.id & ~CAN_SFF_MASK;
//         this.id = newId | (id & CAN_SFF_MASK);
//     }
//     
//     /**
//      * Gets the ID as extended frame format
//      * @return
//      */
//     public int getEFFid() {
//         return id & CAN_SFF_MASK;
//     }
//     
//     /**
//      * Sets the extended frame format ID
//      * @param id
//      */
//     public void setEFFid(int id) {
//         int newId = this.id & ~CAN_EFF_MASK;
//         this.id = newId | (id & CAN_EFF_MASK);
//     }
//     
//     /**
//      * Sets this ID to extended frame format mode
//      */
//     public void setEFF() {
//         id |= CAN_EFF_FLAG;
//     }
//     
//     /**
//      * Sets this ID back to standard frame format mode
//      */
//     public void clearEFF() {
//         id &= ~CAN_EFF_FLAG;
//     }
//     
//     /**
//      * @return true if extended frame format
//      */
//     public boolean isEFF() {
//         return (id & CAN_EFF_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Sets the RTR (remote response) flag
//      */
//     public void setRTR() {
//         id |= CAN_RTR_FLAG;
//     }
//     
//     /**
//      * Clears the RTR (remote response) flag
//      */
//     public void clearRTR() {
//         id &= ~CAN_RTR_FLAG;
//     }
//     
//     /**
//      * @return true if is remote response
//      */
//     public boolean isRTR() {
//         return (id & CAN_RTR_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Sets the error flag
//      */
//     public void setERR() {
//         id |= CAN_ERR_FLAG;
//     }
//     
//     /**
//      * Clears the error flag
//      */
//     public void clearERR() {
//         id &= ~CAN_ERR_FLAG;
//     }
//     
//     /**
//      * @return true if is an error frame
//      */
//     public boolean isERR() {
//         return (id & CAN_ERR_FLAG) != 0 ? true : false;
//     }
//     
//     /**
//      * Used when specifying inverted CAN ID filters
//      * Do not use for sending CAN frames
//      */
//     public void setInverted() {
//         id |= CLibrary.CAN_INV_FILTER;
//     }
//     
//     /**
//      * Clears the inverted property of a CAN ID filter
//      */
//     public void clearInverted() {
//         id &= ~CLibrary.CAN_INV_FILTER;
//     }
//     
//     /**
//      * Used when specifying inverted CAN ID filters
//      * @return
//      */
//     public boolean isInverted() {
//         return (id & CLibrary.CAN_INV_FILTER) != 0;
//     }
// 
//     @Override
//     public String toString() {
//         String ret;
//         if (isEFF()) {
//             ret = String.format("%08X", getEFFid());
//         }
//         else {
//             ret = String.format("%03X", getSFFid());
//         }
//         if (isRTR()) {
//             ret += " RTR";
//         }
//         return ret;
//     }
// }
// 
// Path: netty/src/net/jazdw/jnacan/netty/tp20/Tp20ApplicationType.java
// public enum Tp20ApplicationType implements ValueEnum<Byte> {
//     DIAGNOSTICS(0x01),
//     INFOTAINMENT(0x20),
//     APPROTOCOL(0x20),
//     WFS_WIV(0x21);
// 
//     private static ReverseEnumMap<Byte, Tp20ApplicationType> map = ReverseEnumMap.create(Tp20ApplicationType.class);
//     private byte value;
//     
//     private Tp20ApplicationType(int value) {
//         this.value = (byte) value;
//     }
// 
//     @Override
//     public Byte value() {
//         return value;
//     }
//     
//     public static Tp20ApplicationType fromValue(byte value) {
//         return map.get(value);
//     }
// }


// Path: netty/src/net/jazdw/jnacan/netty/tp20/messages/Tp20ChannelConnect.java
import lombok.Data;
import net.jazdw.jnacan.CanId;
import net.jazdw.jnacan.netty.tp20.Tp20ApplicationType;

        // TODO EFF supported?
        int txId = this.txId.getSFFid();
        int rxId = this.rxId.getSFFid();
        
        data[2] = (byte) txId;
        data[3] = (byte) ((1 << 4) | (txId >> 8));
        data[4] = (byte) rxId;
        data[5] = (byte) (rxId >> 8);
        data[6] = applicationType.value();
        return data;
    }
    
    @Override
    public void decode(byte[] data) {
        if (data.length != 7) {
            throw new IllegalArgumentException("Channel connect data length should be 7 bytes");
        }
        
        opCode = Tp20OpCode.fromValue(data[1]);
        switch (opCode) {
        case CHANNEL_REPLY_APPLICATION_TYPE_UNAVAILABLE:
        case CHANNEL_REPLY_APPLICATION_TYPE_UNSUPPORTED:
        case CHANNEL_REPLY_OUT_OUT_RESOURCES:
        case CHANNEL_REPLY_POSITIVE:
        case CHANNEL_REQUEST:
            break;
        default:
            throw new IllegalArgumentException("Wrong op code for channel connect");
        }
        
        destination = data[0];