/*
 * Copyright (c) 2021 the Eclipse Milo Authors
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.eclipse.milo.opcua.sdk.server.nodes;

import org.eclipse.milo.opcua.sdk.core.nodes.VariableNodeProperties;
import org.eclipse.milo.opcua.sdk.core.nodes.VariableTypeNode;
import org.eclipse.milo.opcua.sdk.core.nodes.VariableTypeNodeProperties;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.NodeClass;
import org.jetbrains.annotations.Nullable;

public class UaVariableTypeNode extends UaNode implements VariableTypeNode {

    private DataValue value;
    private NodeId dataType;
    private Integer valueRank;
    private UInteger[] arrayDimensions;
    private Boolean isAbstract;

    public UaVariableTypeNode(
        UaNodeContext context,
        NodeId nodeId,
        QualifiedName browseName,
        LocalizedText displayName,
        LocalizedText description,
        UInteger writeMask,
        UInteger userWriteMask,
        DataValue value,
        NodeId dataType,
        Integer valueRank,
        UInteger[] arrayDimensions,
        Boolean isAbstract) {

        super(context, nodeId, NodeClass.VariableType,
            browseName, displayName, description, writeMask, userWriteMask);

        this.value = value;
        this.dataType = dataType;
        this.valueRank = valueRank;
        this.arrayDimensions = arrayDimensions;
        this.isAbstract = isAbstract;
    }

    @Override
    public DataValue getValue() {
        return (DataValue) filterChain.getAttribute(this, AttributeId.Value);
    }

    @Override
    public NodeId getDataType() {
        return (NodeId) filterChain.getAttribute(this, AttributeId.DataType);
    }

    @Override
    public Integer getValueRank() {
        return (Integer) filterChain.getAttribute(this, AttributeId.ValueRank);
    }

    @Override
    public UInteger[] getArrayDimensions() {
        return (UInteger[]) filterChain.getAttribute(this, AttributeId.ArrayDimensions);
    }

    @Override
    public Boolean getIsAbstract() {
        return (Boolean) filterChain.getAttribute(this, AttributeId.IsAbstract);
    }

    @Override
    public void setValue(DataValue value) {
        filterChain.setAttribute(this, AttributeId.Value, value);
    }

    @Override
    public void setDataType(NodeId dataType) {
        filterChain.setAttribute(this, AttributeId.DataType, dataType);
    }

    @Override
    public void setValueRank(Integer valueRank) {
        filterChain.setAttribute(this, AttributeId.ValueRank, valueRank);
    }

    @Override
    public void setArrayDimensions(UInteger[] arrayDimensions) {
        filterChain.setAttribute(this, AttributeId.ArrayDimensions, arrayDimensions);
    }

    @Override
    public void setIsAbstract(Boolean isAbstract) {
        filterChain.setAttribute(this, AttributeId.IsAbstract, isAbstract);
    }

    @Override
    public synchronized Object getAttribute(AttributeId attributeId) {
        switch (attributeId) {
            case Value:
                return value;

            case DataType:
                return dataType;

            case ValueRank:
                return valueRank;

            case ArrayDimensions:
                return arrayDimensions;

            case IsAbstract:
                return isAbstract;

            default:
                return super.getAttribute(attributeId);
        }
    }

    @Override
    public synchronized void setAttribute(AttributeId attributeId, Object value) {
        switch (attributeId) {
            case Value:
                this.value = (DataValue) value;
                break;

            case DataType:
                dataType = (NodeId) value;
                break;

            case ValueRank:
                valueRank = (Integer) value;
                break;

            case ArrayDimensions:
                arrayDimensions = (UInteger[]) value;
                break;

            case IsAbstract:
                isAbstract = (Boolean) value;
                break;

            default:
                super.setAttribute(attributeId, value);
                return; // prevent firing an attribute change
        }

        fireAttributeChanged(attributeId, value);
    }

    /**
     * Get the value of the NodeVersion Property, if it exists.
     *
     * @return the value of the NodeVersion Property, if it exists.
     * @see VariableTypeNodeProperties#NodeVersion
     */
    @Nullable
    public String getNodeVersion() {
        return getProperty(VariableTypeNodeProperties.NodeVersion).orElse(null);
    }

    /**
     * Set the value of the NodeVersion Property.
     * <p>
     * A PropertyNode will be created if it does not already exist.
     *
     * @param nodeVersion the value to set.
     * @see VariableNodeProperties#NodeVersion
     */
    public void setNodeVersion(String nodeVersion) {
        setProperty(VariableTypeNodeProperties.NodeVersion, nodeVersion);
    }

}
