/*******************************************************************************
 * Copyright (c) 2014, 2018 Red Hat, Inc. and others.
 *
 * 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:
 *     Red Hat - initial API and implementation
 *******************************************************************************/

package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures;

import java.util.HashMap;
import java.util.Iterator;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.preferences.IDEPreferenceConstants;
import org.eclipse.linuxtools.man.parser.ManPage;
import org.eclipse.linuxtools.systemtap.ui.consolelog.internal.ConsoleLogPlugin;
import org.eclipse.linuxtools.systemtap.ui.consolelog.preferences.ConsoleLogPreferenceConstants;

public class ManpageCacher {
    private static final String SPLITTER = "::"; //$NON-NLS-1$
    private static final String NO_MAN_ENTRY = "No manual entry for "; //$NON-NLS-1$
    private static HashMap<String, String> pages = new HashMap<>();

    public static boolean isEmptyDocumentation(String documentation) {
        return documentation != null && documentation.startsWith(NO_MAN_ENTRY);
    }

    private static String makeEmptyDocumentation(String element) {
        return NO_MAN_ENTRY + element;
    }

    public static synchronized void clear() {
        clear(TapsetItemType.values());
    }

    public static synchronized void clear(TapsetItemType ...types) {
        Iterator<String> keys = pages.keySet().iterator();
        while (keys.hasNext()) {
            for (TapsetItemType type : types) {
                if (keys.next().startsWith(type.toString())) {
                    keys.remove();
                    break;
                }
            }
        }
    }

    /**
     * Returns the documentation for the given probe, function, or tapset. Will never be <code>null</code>.
     */
    public static synchronized String getDocumentation(TapsetItemType prefix, String ...elements) {
        String fullElement = createFullElement(prefix, elements);
        String documentation = pages.get(fullElement);
        if (documentation == null) {
            // If the requested element is a probe variable,
            // fetch the documentation for the parent probe then check the map
            if (prefix == TapsetItemType.PROBEVAR) {
                getDocumentation(TapsetItemType.PROBE, elements[0]);
                documentation = pages.get(fullElement);
                if (documentation == null) {
                    documentation = makeEmptyDocumentation(fullElement);
                    pages.put(fullElement, documentation);
                }
                return documentation;
            }

            // Otherwise, get the documentation for the requested element.
            IPreferenceStore p = IDEPlugin.getDefault().getPreferenceStore();
            ManPage manpage = null;
            if (!p.getBoolean(IDEPreferenceConstants.P_REMOTE_PROBES)) {
                manpage = new ManPage(fullElement);
            } else {
                p = ConsoleLogPlugin.getDefault().getPreferenceStore();
                String user = p.getString(ConsoleLogPreferenceConstants.SCP_USER);
                String host = p.getString(ConsoleLogPreferenceConstants.HOST_NAME);
                String password = p.getString(ConsoleLogPreferenceConstants.SCP_PASSWORD);
                manpage = new ManPage(fullElement, user, host, password);
            }
            documentation = manpage.getStrippedTextPage().toString();
            pages.put(fullElement, documentation);

            // If the requested element is a probe and a documentation page was
            // found for it, parse the documentation for the variables if present.
            if (!isEmptyDocumentation(documentation) && prefix == TapsetItemType.PROBE) {
                getProbeVariableDocumentation(documentation, elements[0]);
            }
        }
        return documentation;
    }

    private static void getProbeVariableDocumentation(String documentation, String probe) {
        // Parse out the variables
        String[] sections = documentation.split("VALUES"); //$NON-NLS-1$
        if (sections.length > 1) {
            // Discard any other sections
            String variablesString = sections[1].split("CONTEXT|DESCRIPTION|SystemTap Tapset Reference")[0].trim(); //$NON-NLS-1$
            String[] variables = variablesString.split("\n"); //$NON-NLS-1$
            int i = 0;
            if (!variables[0].equals("None")) { //$NON-NLS-1$
                while (i < variables.length) {
                    String variableName = variables[i].trim();
                    StringBuilder variableDocumentation = new StringBuilder();
                    i++;
                    while (i < variables.length && !variables[i].isEmpty()) {
                        variableDocumentation.append(variables[i].trim());
                        variableDocumentation.append("\n"); //$NON-NLS-1$
                        i++;
                    }

                    pages.put(createFullElement(TapsetItemType.PROBEVAR, probe, variableName),
                            variableDocumentation.toString().trim());
                    i++;
                }
            }
        }
    }

    private static String createFullElement(TapsetItemType prefix, String ...elements) {
        StringBuilder fullElement = new StringBuilder();
        fullElement.append(prefix.toString());
        for (String element : elements) {
            fullElement.append(SPLITTER + element);
        }
        return fullElement.toString();
    }
}
