/*******************************************************************************
 * Copyright (c) 2006, 2018 IBM Corporation 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:
 *     IBM Corporation - Jeff Briggs, Henry Hughes, Ryan Morse
 *******************************************************************************/

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

import java.io.File;
import java.net.URI;

import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.Localization;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.actions.KernelSourceAction;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.preferences.IDEPreferenceConstants;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.preferences.PathPreferencePage;
import org.eclipse.linuxtools.profiling.launch.IRemoteFileProxy;
import org.eclipse.linuxtools.profiling.launch.RemoteProxyManager;
import org.eclipse.linuxtools.systemtap.graphing.ui.widgets.ExceptionErrorDialog;
import org.eclipse.linuxtools.systemtap.structures.KernelSourceTree;
import org.eclipse.linuxtools.systemtap.structures.TreeNode;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

/**
 * The Kernel Source Browser module for the SystemTap GUI. This browser provides a list of kernel source
 * files and allows the user to open those files in an editor in order to place probes in arbitrary locations.
 * @author Henry Hughes
 * @author Ryan Morse
 */

public class KernelBrowserView extends BrowserView {
    public static final String ID = "org.eclipse.linuxtools.internal.systemtap.ui.ide.views.KernelBrowserView"; //$NON-NLS-1$

    private class KernelRefreshJob extends Job {
        private boolean remote;
        private URI kernelLocationURI;
        private IRemoteFileProxy proxy;
        private String kernelSource;

        public KernelRefreshJob(boolean remote, URI kernelLocationURI, IRemoteFileProxy proxy, String kernelSource) {
            super(Localization.getString("KernelBrowserView.RefreshingKernelSource")); //$NON-NLS-1$
            this.remote = remote;
            this.kernelLocationURI = kernelLocationURI;
            this.proxy = proxy;
            this.kernelSource = kernelSource;
        }

        @Override
        public IStatus run(IProgressMonitor monitor) {
            IPreferenceStore p = IDEPlugin.getDefault().getPreferenceStore();
            KernelSourceTree kst = new KernelSourceTree();
            String excluded[] = p.getString(IDEPreferenceConstants.P_EXCLUDED_KERNEL_SOURCE).split(File.pathSeparator);
            if (remote) {
                try {
                    kst.buildKernelTree(kernelLocationURI, excluded, proxy, monitor);
                } catch (CoreException e) {
                    ExceptionErrorDialog.openError(Localization.getString("KernelBrowserView.CouldNotInitializeTree"), e); //$NON-NLS-1$
                }
            } else {
                kst.buildKernelTree(kernelSource, excluded);
            }
            if (monitor.isCanceled()) {
                setViewerInput(null);
                return Status.CANCEL_STATUS;
            }
            setViewerInput(kst.getTree());
            monitor.done();
            return Status.OK_STATUS;
        }
    }

    /**
     * Creates the UI on the given <code>Composite</code>
     */
    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        refresh();
        makeActions();
    }

    @Override
    protected void makeActions() {
        doubleClickAction = new KernelSourceAction(getSite().getWorkbenchWindow(), this);
        viewer.addDoubleClickListener(doubleClickAction);
        IDEPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(propertyChangeListener);
    }

    @Override
    protected Image getEntryImage(TreeNode treeObj) {
        if (treeObj.toString().equals(
                Localization.getString("KernelBrowserView.NoKernelSourceFound"))) { //$NON-NLS-1$
            return null;
        }
        if (treeObj.toString().lastIndexOf('.') != -1) {
            String item = treeObj.getData().toString();
            if (item.endsWith(".c")) { //$NON-NLS-1$
                return IDEPlugin.getImageDescriptor("icons/files/file_c.gif").createImage(); //$NON-NLS-1$
            }
            if (item.endsWith(".h")) { //$NON-NLS-1$
                return IDEPlugin.getImageDescriptor("icons/files/file_h.gif").createImage(); //$NON-NLS-1$
            }
            return IDEPlugin.getImageDescriptor("icons/vars/var_unk.gif").createImage(); //$NON-NLS-1$
        }
        return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
    }

    /**
     * Updates the kernel source displayed to the user with the new kernel source tree. Usually
     * a response to the user changing the preferences related to the kernel source location, requiring
     * that the application update the kernel source information.
     */
    @Override
    public void refresh() {
        displayLoadingMessage();
        IPreferenceStore p = IDEPlugin.getDefault().getPreferenceStore();
        String kernelSource = p.getString(IDEPreferenceConstants.P_KERNEL_SOURCE);
        if (kernelSource == null || kernelSource.length() < 1) {
            displayMessage(Localization.getString("KernelBrowserView.NoKernelSourceFound")); //$NON-NLS-1$
            return;
        }

        String localOrRemote = p.getString(IDEPreferenceConstants.P_REMOTE_LOCAL_KERNEL_SOURCE);
        URI kernelLocationURI = null;
        IRemoteFileProxy proxy = null;
        boolean remote = localOrRemote.equals(PathPreferencePage.REMOTE);
        if (remote) {
            boolean error = false;
            try {
                kernelLocationURI = IDEPlugin.getDefault().createRemoteUri(kernelSource);
                if (kernelLocationURI == null) {
                    error = true;
                } else {
                    proxy = RemoteProxyManager.getInstance().getFileProxy(kernelLocationURI);
                    if (!validateProxy(proxy, kernelSource)) {
                        error = true;
                    }
                }
            } catch (CoreException e2) {
                error = true;
            }
            if (error) {
                displayMessage(Localization.getString("KernelBrowserView.KernelSourceDirNotFound")); //$NON-NLS-1$
                return;
            }
        }

        KernelRefreshJob refreshJob = new KernelRefreshJob(remote, kernelLocationURI, proxy, kernelSource);
        refreshJob.setPriority(Job.SHORT);
        refreshJob.schedule();
    }

    private boolean validateProxy(IRemoteFileProxy proxy, String kernelSource) {
        if (proxy == null) {
            return false;
        }
        IFileStore fs = proxy.getResource(kernelSource);
        if (fs == null) {
            return false;
        }
        IFileInfo info = fs.fetchInfo();
        if (info == null) {
            return false;
        }
        if (!info.exists()) {
            return false;
        }
        return true;
    }

    /**
     * A <code>IPropertyChangeListener</code> that detects changes to the Kernel Source location
     * and runs the <code>updateKernelSourceTree</code> method.
     */
    private final IPropertyChangeListener propertyChangeListener = event -> {
	    if (event.getProperty().equals(IDEPreferenceConstants.P_KERNEL_SOURCE) ||
	        event.getProperty().equals(IDEPreferenceConstants.P_REMOTE_LOCAL_KERNEL_SOURCE) ||
	        event.getProperty().equals(IDEPreferenceConstants.P_EXCLUDED_KERNEL_SOURCE)) {
	        refresh();
	    }
	};

    @Override
    public void dispose() {
        IDEPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(propertyChangeListener);
        super.dispose();
    }
}
