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

import java.util.Comparator;
import java.util.Map;
import java.util.function.Consumer;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
import org.eclipse.milo.opcua.stack.core.util.Namespaces;
import org.jetbrains.annotations.Nullable;

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

public class NamespaceTable {

    private final BiMap<UShort, String> uriTable = HashBiMap.create();

    public NamespaceTable() {
        uriTable.put(ushort(0), Namespaces.OPC_UA);
    }

    /**
     * Add a URI to the NamespaceTable and return the index.
     * <p>
     * If the URI is not present in the table the next available index is assigned.
     * <p>
     * If the table already contains the URI the current index is returned.
     *
     * @param uri the namespace URI to add.
     * @return the index assigned to the URI.
     */
    public synchronized UShort addUri(String uri) {
        if (uriTable.containsValue(uri)) {
            return uriTable.inverse().get(uri);
        } else {
            UShort index = ushort(1);

            while (uriTable.containsKey(index)) {
                index = ushort(index.intValue() + 1);
                if (index.intValue() == 65535) {
                    throw new UaRuntimeException(StatusCodes.Bad_InternalError, "uri table full");
                }
            }
            uriTable.put(index, uri);

            return index;
        }
    }

    public synchronized void putUri(String uri, UShort index) {
        uriTable.put(index, uri);
    }

    public synchronized String getUri(int index) {
        return getUri(ushort(index));
    }

    public synchronized String getUri(UShort index) {
        return uriTable.get(index);
    }

    /**
     * @param uri the namespace URI to look up.
     * @return the index of the namespace URI, or {@code null} if it is not present.
     */
    @Nullable
    public synchronized UShort getIndex(String uri) {
        return uriTable.inverse().getOrDefault(uri, null);
    }

    public synchronized void update(Consumer<BiMap<UShort, String>> uriTableConsumer) {
        uriTableConsumer.accept(uriTable);
    }

    public synchronized String[] toArray() {
        return uriTable.entrySet().stream()
            .sorted(Comparator.comparingInt(e -> e.getKey().intValue()))
            .map(Map.Entry::getValue)
            .toArray(String[]::new);
    }

}
