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

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.util.Tree;

class NodeTable {

    final LinkedHashMap<BrowsePath, NodeId> nodes = new LinkedHashMap<>();

    void addNode(BrowsePath browsePath, NodeId nodeId) {
        nodes.put(browsePath, nodeId);
    }

    Tree<BrowsePath> getBrowsePathTree() {
        BrowsePath parentBrowsePath = nodes.keySet().stream()
            .filter(b -> b.parent == null)
            .findFirst()
            .orElse(null);

        Map<BrowsePath, List<BrowsePath>> grouping = nodes.keySet()
            .stream()
            .filter(b -> b.parent != null)
            .collect(Collectors.groupingBy(b -> b.parent));

        Tree<BrowsePath> root = new Tree<>(null, parentBrowsePath);

        addChildren(grouping, root);

        return root;
    }

    private static void addChildren(Map<BrowsePath, List<BrowsePath>> grouping, Tree<BrowsePath> parent) {
        BrowsePath parentPath = parent.getValue();

        List<BrowsePath> childPaths = grouping.get(parentPath);

        if (childPaths != null) {
            childPaths.forEach(childPath -> {
                Tree<BrowsePath> child = parent.addChild(childPath);

                addChildren(grouping, child);
            });
        }
    }

    static NodeTable merge(NodeTable table1, NodeTable table2) {
        NodeTable mergedNodeTable = new NodeTable();

        // Put all the second table's nodes first
        mergedNodeTable.nodes.putAll(table2.nodes);

        // Allow the first table's nodes to overwrite
        mergedNodeTable.nodes.putAll(table1.nodes);

        return mergedNodeTable;
    }


}
