/*
 * Copyright (c) 2019 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.util;

import java.util.Set;

import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.WriteMask;
import org.eclipse.milo.opcua.sdk.server.nodes.AttributeContext;
import org.eclipse.milo.opcua.sdk.server.nodes.UaServerNode;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.StatusCodes;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.jetbrains.annotations.Nullable;

public class AttributeUtil {

    /**
     * DataValue for a non-value attribute; no source timestamp included.
     */
    public static DataValue dv(Object o) {
        return new DataValue(new Variant(o), StatusCode.GOOD, null, DateTime.now());
    }

    @Nullable
    @SuppressWarnings("unchecked")
    public static <T> T extract(DataValue value) throws UaException {
        Variant variant = value.getValue();
        if (variant == null) return null;

        Object o = variant.getValue();
        if (o == null) return null;

        try {
            return (T) o;
        } catch (ClassCastException e) {
            throw new UaException(
                StatusCodes.Bad_TypeMismatch,
                "attribute value was not of the expected type: " + o
            );
        }
    }

    public static Set<AccessLevel> getAccessLevels(
        UaServerNode node,
        AttributeContext context) throws UaException {

        UByte accessLevel = extract(
            node.getAttribute(
                context,
                AttributeId.AccessLevel)
        );

        if (accessLevel != null) {
            return AccessLevel.fromValue(accessLevel);
        } else {
            return AccessLevel.NONE;
        }
    }

    public static Set<AccessLevel> getUserAccessLevels(
        UaServerNode node,
        AttributeContext context) throws UaException {

        UByte userAccessLevel = extract(
            node.getAttribute(
                context,
                AttributeId.UserAccessLevel)
        );

        if (userAccessLevel != null) {
            return AccessLevel.fromValue(userAccessLevel);
        } else {
            return AccessLevel.NONE;
        }
    }

    public static Set<WriteMask> getWriteMasks(
        UaServerNode node,
        AttributeContext internalContext) throws UaException {

        UInteger writeMask = extract(
            node.getAttribute(
                internalContext,
                AttributeId.WriteMask
            )
        );

        if (writeMask != null) {
            return WriteMask.fromMask(writeMask);
        } else {
            return WriteMask.NONE;
        }
    }

    public static Set<WriteMask> getUserWriteMasks(
        UaServerNode node,
        AttributeContext internalContext) throws UaException {

        UInteger userWriteMask = extract(
            node.getAttribute(
                internalContext,
                AttributeId.UserWriteMask
            )
        );

        if (userWriteMask != null) {
            return WriteMask.fromMask(userWriteMask);
        } else {
            return WriteMask.NONE;
        }
    }

}
