/*
 * Copyright 2014 Inductive Automation
 *
 * 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 com.inductiveautomation.opcua.sdk.core;

import java.util.UUID;

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.inductiveautomation.opcua.stack.core.types.builtin.ByteString;
import com.inductiveautomation.opcua.stack.core.types.builtin.DataValue;
import com.inductiveautomation.opcua.stack.core.types.builtin.DateTime;
import com.inductiveautomation.opcua.stack.core.types.builtin.DiagnosticInfo;
import com.inductiveautomation.opcua.stack.core.types.builtin.ExpandedNodeId;
import com.inductiveautomation.opcua.stack.core.types.builtin.ExtensionObject;
import com.inductiveautomation.opcua.stack.core.types.builtin.LocalizedText;
import com.inductiveautomation.opcua.stack.core.types.builtin.NodeId;
import com.inductiveautomation.opcua.stack.core.types.builtin.QualifiedName;
import com.inductiveautomation.opcua.stack.core.types.builtin.StatusCode;
import com.inductiveautomation.opcua.stack.core.types.builtin.Variant;
import com.inductiveautomation.opcua.stack.core.types.builtin.XmlElement;
import com.inductiveautomation.opcua.stack.core.types.builtin.unsigned.UByte;
import com.inductiveautomation.opcua.stack.core.types.builtin.unsigned.UInteger;
import com.inductiveautomation.opcua.stack.core.types.builtin.unsigned.ULong;
import com.inductiveautomation.opcua.stack.core.types.builtin.unsigned.UShort;
import com.inductiveautomation.opcua.stack.core.types.enumerated.IdType;

public enum DataType {
    Boolean(1, Boolean.class),
    SByte(2, Byte.class),
    Byte(3, UByte.class),
    Int16(4, Short.class),
    UInt16(5, UShort.class),
    Int32(6, Integer.class),
    UInt32(7, UInteger.class),
    Int64(8, Long.class),
    UInt64(9, ULong.class),
    Float(10, Float.class),
    Double(11, Double.class),
    String(12, String.class),
    DateTime(13, DateTime.class),
    Guid(14, UUID.class),
    ByteString(15, ByteString.class),
    XmlElement(16, XmlElement.class),
    NodeId(17, NodeId.class),
    ExpandedNodeId(18, ExpandedNodeId.class),
    StatusCode(19, StatusCode.class),
    QualifiedName(20, QualifiedName.class),
    LocalizedText(21, LocalizedText.class),
    ExtensionObject(22, ExtensionObject.class),
    DataValue(23, DataValue.class),
    Variant(24, Variant.class),
    DiagnosticInfo(25, DiagnosticInfo.class);

    private final int typeId;
    private final Class<?> backingClass;

    DataType(int typeId, Class<?> backingClass) {
        this.typeId = typeId;
        this.backingClass = backingClass;
    }

    public int getTypeId() {
        return typeId;
    }

    public Class<?> getBackingClass() {
        return backingClass;
    }

    private static final BiMap<Integer, Class<?>> BackingClassesById;
    private static final BiMap<NodeId, Class<?>> BackingClassesByNodeId;

    static {
        ImmutableBiMap.Builder<Integer, Class<?>> builder = ImmutableBiMap.<Integer, Class<?>>builder();
        ImmutableBiMap.Builder<NodeId, Class<?>> builder2 = ImmutableBiMap.<NodeId, Class<?>>builder();

        for (DataType dataType : values()) {
            builder.put(dataType.getTypeId(), dataType.getBackingClass());
            builder2.put(new NodeId(0, dataType.getTypeId()), dataType.getBackingClass());
        }

        BackingClassesById = builder.build();
        BackingClassesByNodeId = builder2.build();
    }

    /**
     * @param backingClass the backing {@link Class} of the builtin type.
     * @return the id of the builtin type backed by {@code backingClass}.
     */
    public static int getBuiltinTypeId(Class<?> backingClass) {
        if (backingClass.isPrimitive()) {
            if (backingClass == boolean.class) backingClass = Boolean.class;
            else if (backingClass == byte.class) backingClass = Byte.class;
            else if (backingClass == short.class) backingClass = Short.class;
            else if (backingClass == int.class) backingClass = Integer.class;
            else if (backingClass == long.class) backingClass = Long.class;
            else if (backingClass == float.class) backingClass = Float.class;
            else if (backingClass == double.class) backingClass = Double.class;
        }

        return BackingClassesById.inverse().get(backingClass);
    }

    /**
     * @param typeId the id of the builtin type.
     * @return the {@link Class} backing the builtin type.
     */
    public static Class<?> getBackingClass(int typeId) {
        return BackingClassesById.get(typeId);
    }

    public static Class<?> getBackingClass(NodeId typeId) {
        return BackingClassesByNodeId.get(typeId);
    }

    public static Class<?> getBackingClass(ExpandedNodeId typeId) {
        if (typeId.getNamespaceIndex().intValue() == 0 && typeId.getType() == IdType.Numeric) {
            Number id = (Number) typeId.getIdentifier();
            return BackingClassesById.get(id.intValue());
        }

        return null;
    }

    public static boolean isBuiltin(int typeId) {
        return BackingClassesById.containsKey(typeId);
    }

    public static boolean isBuiltin(NodeId typeId) {
        return BackingClassesByNodeId.containsKey(typeId);
    }

    public static boolean isBuiltin(ExpandedNodeId typeId) {
        if (typeId.getNamespaceIndex().intValue() == 0 && typeId.getType() == IdType.Numeric) {
            Number id = (Number) typeId.getIdentifier();
            return BackingClassesById.containsKey(id.intValue());
        }

        return false;
    }

}

@@ -5810,172 +5810,34 @@
     
-if (typeId.getNamespaceIndex().intValue() == 0 && typeId.getType() == IdType.Numeric) %7B%0A            Number id = (Number) typeId.getIdentifier();%0A            return 
+return typeId.local().map(
 Back
@@ -5840,35 +5840,40 @@
 BackingClassesBy
-Id.
+NodeId::
 containsKey(id.i
@@ -5871,56 +5871,23 @@
 sKey
-(id.intValue());%0A        %7D%0A%0A        return 
+).orElse(
 false
+)
 ;%0A  

Simplify isBuiltin() implementation