/*
 * 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.core;

import java.util.Objects;
import java.util.Optional;

import com.google.common.base.MoreObjects;
import org.eclipse.milo.opcua.stack.core.NamespaceTable;
import org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId;
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.builtin.unsigned.UShort;
import org.jetbrains.annotations.Nullable;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

public class QualifiedProperty<T> {

    private final String namespaceUri;
    private final String browseName;
    private final ExpandedNodeId dataType;
    private final Integer valueRank;
    private final Class<T> javaType;

    public QualifiedProperty(
        String namespaceUri,
        String browseName,
        ExpandedNodeId dataType,
        Integer valueRank,
        Class<T> javaType
    ) {

        this.namespaceUri = namespaceUri;
        this.browseName = browseName;
        this.dataType = dataType;
        this.valueRank = valueRank;
        this.javaType = javaType;
    }

    public String getNamespaceUri() {
        return namespaceUri;
    }

    public String getBrowseName() {
        return browseName;
    }

    public ExpandedNodeId getDataType() {
        return dataType;
    }

    public Integer getValueRank() {
        return valueRank;
    }

    public Class<T> getJavaType() {
        return javaType;
    }

    @Nullable
    public UInteger[] getArrayDimensions() {
        int valueRank = getValueRank();

        if (valueRank <= 0) {
            return null;
        } else {
            UInteger[] arrayDimensions = new UInteger[valueRank];
            for (int i = 0; i < valueRank; i++) {
                arrayDimensions[i] = uint(0);
            }
            return arrayDimensions;
        }
    }

    /**
     * Get the {@link QualifiedName} of this property.
     * <p>
     * The {@code namespaceUri} must be present in {@code namespaceTable} or an empty {@link Optional} will be returned.
     *
     * @param namespaceTable the {@link NamespaceTable} containing the namespace URIs and their indices.
     * @return the {@link QualifiedName} of this property, or an empty {@link Optional} if the namespace URI is not
     * present in {@code namespaceTable}.
     */
    public Optional<QualifiedName> getQualifiedName(NamespaceTable namespaceTable) {
        UShort namespaceIndex = namespaceTable.getIndex(namespaceUri);

        if (namespaceIndex != null) {
            return Optional.of(new QualifiedName(namespaceIndex, browseName));
        } else {
            return Optional.empty();
        }
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
            .add("namespaceUri", namespaceUri)
            .add("browseName", browseName)
            .add("dataType", dataType)
            .add("valueRank", valueRank)
            .add("javaType", javaType)
            .toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        QualifiedProperty<?> that = (QualifiedProperty<?>) o;

        return Objects.equals(namespaceUri, that.namespaceUri) &&
            Objects.equals(browseName, that.browseName) &&
            Objects.equals(dataType, that.dataType) &&
            Objects.equals(valueRank, that.valueRank) &&
            Objects.equals(javaType, that.javaType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(namespaceUri, browseName, dataType, valueRank, javaType);
    }

}
