/*******************************************************************************
 * Copyright (c) 2008, 2018 Phil Muldoon <pkmuldoon@picobot.org>.
 *
 * 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
 *
 * Contributors:
 *    Phil Muldoon <pkmuldoon@picobot.org> - initial API and implementation.
 *******************************************************************************/

package org.eclipse.linuxtools.internal.systemtap.ui.ide.editors.stp;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.TapsetLibrary;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.nodedata.ICompletable;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.nodedata.ProbevarNodeData;
import org.eclipse.linuxtools.systemtap.structures.TreeNode;


/**
 *
 * Build and hold completion metadata for Systemtap. This originally is generated from stap coverage data
 *
 */
public final class STPMetadataSingleton {

    public static TreeNode[] NO_MATCHES = new TreeNode[0];

    private static STPMetadataSingleton instance = null;

    private STPMetadataSingleton() {}

    public static STPMetadataSingleton getInstance() {
        if (instance == null) {
            instance = new STPMetadataSingleton();
        }
        return instance;
    }

    public TreeNode[] getFunctionCompletions(String prefix) {
        TreeNode node = TapsetLibrary.getFunctions();
        return getMatchingChildren(node, prefix);
    }

    public TreeNode[] getProbeCompletions(String prefix) {
        List<TreeNode> matches = new LinkedList<>();
        String groupName = extractProbeGroupName(prefix);

        for (TreeNode node : TapsetLibrary.getProbeCategoryNodes()) {
            TreeNode groupNode = node.getChildByName(groupName);
            if (groupNode != null) {
                node = groupNode;
            }

            matches.addAll(Arrays.asList(getMatchingChildren(node, prefix)));
        }

        return !matches.isEmpty() ? matches.toArray(new TreeNode[matches.size()]) : NO_MATCHES;
    }

    /**
     * Returns a list of variables available in the given probe.
     * @param probe The probe for which to find variables
     * @param prefix The prefix to complete.
     * @return a list of variables matching the prefix.
     */
    public TreeNode[] getProbeVariableCompletions(String probe, String prefix) {
        // The only probes that may have avilable variables are non-static ones.
        TreeNode node = getProbeAlias(probe);
        return node != null ? getMatchingChildren(node, prefix) : NO_MATCHES;
    }

    public boolean isVariableInProbe(String probe, String variable) {
        TreeNode node = getProbeAlias(probe);
        if (node == null) {
            return false;
        }
        for (int i = 0, n = node.getChildCount(); i < n; i++) {
            TreeNode child = node.getChildAt(i);
            String nodeVar = ((ProbevarNodeData) child.getData()).getCompletionText();
            if (nodeVar.equals(variable)) {
                return true;
            }
        }
        return false;
    }

    public TreeNode getProbeAlias(String probe) {
        TreeNode node = TapsetLibrary.getProbeAliases();
        if (node == null) {
            return null;
        }

        node = node.getChildByName(extractProbeGroupName(probe));
        if (node == null) {
            return null;
        }

        return node.getChildByName(probe);
    }

    private TreeNode[] getMatchingChildren(TreeNode node, String prefix) {
        ArrayList<TreeNode> matches = new ArrayList<>();

        for (int i = 0, n = node.getChildCount(); i < n; i++) {
            TreeNode childNode = node.getChildAt(i);
            String childName;
            if (childNode.getData() instanceof ICompletable) {
                childName = ((ICompletable) childNode.getData()).getCompletionText();
            } else {
                childName = childNode.toString();
            }
            if (childName.startsWith(prefix)) {
                matches.add(childNode);
            }
        }

        return matches.toArray(new TreeNode[0]);
    }

    private String extractProbeGroupName(String probeName) {
        int dotIndex = probeName.indexOf('.');
        int parenIndex = probeName.indexOf('(');
        if (dotIndex > 0 && parenIndex > 0) {
            return probeName.substring(0, Math.min(dotIndex, parenIndex));
        }
        if (dotIndex > 0) {
            return probeName.substring(0, dotIndex);
        }
        if (parenIndex > 0) {
            return probeName.substring(0, parenIndex);
        }
        return probeName;
    }

}
