/*******************************************************************************
 * Copyright (c) 2004, 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:
 *    Keith Seitz <keiths@redhat.com> - initial API and implementation
 *    Kent Sebastian <ksebasti@redhat.com> -
 *    Thavidu Ranatunga (IBM) - derived from
 *       org.eclipse.linuxtools.oprofile.launch.configuration.OprofileSetupTab
 *******************************************************************************/
package org.eclipse.linuxtools.internal.perf.launch;

import java.io.File;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.linuxtools.internal.perf.PerfCore;
import org.eclipse.linuxtools.internal.perf.PerfPlugin;
import org.eclipse.linuxtools.internal.perf.PerfVersion;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;

public class PerfOptionsTab extends AbstractLaunchConfigurationTab {
    private ILaunchConfiguration lastConfig;

    protected Text txtKernelLocation;
    protected Button chkRecordRealtime;
    protected Spinner rtPriority;
    protected Button chkRecordVerbose;
    protected Button chkSourceLineNumbers;
    protected Button chkKernelSourceLineNumbers;
    protected Button chkMultiplexEvents;
    protected Button chkModuleSymbols;
    protected Button chkHideUnresolvedSymbols;
    protected Button chkShowSourceDisassembly;
    protected Button chkShowStat;
    protected Spinner statRunCount;

    private Composite top;
    private ScrolledComposite scrollTop;

    private final PerfVersion multiplexEventsVersion = new PerfVersion (2, 6, 35);

    @Override
    public Image getImage() {
        return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT);
    }

    @Override
    public boolean isValid(ILaunchConfiguration config) {
        if (txtKernelLocation != null) {
            String filename = txtKernelLocation.getText();
            if (filename.length() > 0) {
                File file = new File(filename);
                return (file.exists() && file.isFile());
            }
        }
        return true;
    }

    @Override
    public void createControl(Composite parent) {
        scrollTop = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
        scrollTop.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        scrollTop.setExpandVertical(true);
        scrollTop.setExpandHorizontal(true);

        setControl(scrollTop);

        top = new Composite(scrollTop, SWT.NONE);
        top.setLayout(new GridLayout());

        createVerticalSpacer(top, 1);
        GridData data;

        // Kernel Selection
        Composite kernelComp = new Composite(top, SWT.NONE);
        GridLayout parallelLayout = new GridLayout(2, false);
        data = new GridData(GridData.FILL_HORIZONTAL);
        parallelLayout.marginHeight = 0;
        parallelLayout.marginWidth = 0;
        kernelComp.setLayout(parallelLayout);
        kernelComp.setLayoutData(data);

        Label kernelLabel = new Label(kernelComp, SWT.NONE);
        kernelLabel.setText(PerfPlugin.STRINGS_Kernel_Location);
        data = new GridData();
        data.horizontalSpan = 2;
        kernelLabel.setLayoutData(data);

        txtKernelLocation = new Text(kernelComp, SWT.SINGLE | SWT.BORDER);
        data = new GridData(GridData.FILL_HORIZONTAL);
        txtKernelLocation.setLayoutData(data);
        txtKernelLocation.addModifyListener(mev -> handleKernelImageFileTextModify(txtKernelLocation));

        Button button = createPushButton(kernelComp, Messages.PerfOptionsTab_Browse, null);
        final Shell shell = top.getShell();
		button.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> showFileDialog(shell)));

        createVerticalSpacer(top, 1);

        // Create checkbox options container
        Composite chkBoxComp = new Composite(top, SWT.NONE);
        GridLayout chkBoxLayout = new GridLayout();
        chkBoxLayout.marginHeight = 0;
        chkBoxLayout.marginWidth = 0;
        chkBoxComp.setLayout(chkBoxLayout);

        chkRecordVerbose = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_Record_Verbose);
        chkModuleSymbols = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_ModuleSymbols);
        chkHideUnresolvedSymbols = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_HideUnresolvedSymbols);
        chkSourceLineNumbers = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_SourceLineNumbers);
        chkShowSourceDisassembly = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_ShowSourceDisassembly);

        Composite showStatComp = new Composite(top, SWT.NONE);
        showStatComp.setLayout(parallelLayout);

        chkShowStat = createCheckButtonHelper(showStatComp, PerfPlugin.STRINGS_ShowStat);
		chkShowStat.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> handleShowStatSelection()));
        statRunCount = new Spinner(showStatComp, SWT.BORDER);
        statRunCount.setEnabled(false);
        statRunCount.setMinimum(1);
        statRunCount.addModifyListener(e -> updateLaunchConfigurationDialog());

		chkSourceLineNumbers.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
			if ((chkKernelSourceLineNumbers != null) && (!chkSourceLineNumbers.getSelection())) {
				chkKernelSourceLineNumbers.setEnabled(false);
			} else {
				chkKernelSourceLineNumbers.setEnabled(true);
			}
		}));
        chkKernelSourceLineNumbers = createCheckButtonHelper(chkBoxComp, PerfPlugin.STRINGS_Kernel_SourceLineNumbers);

        Composite realtimeComp = new Composite(top, SWT.NONE);
        realtimeComp.setLayout(parallelLayout);

        chkRecordRealtime = createCheckButtonHelper(realtimeComp, PerfPlugin.STRINGS_Record_Realtime);
		chkRecordRealtime.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
			rtPriority.setEnabled(chkRecordRealtime.getSelection());
		}));
        rtPriority = new Spinner(realtimeComp, SWT.BORDER);
        rtPriority.setEnabled(chkRecordRealtime.getSelection());
        rtPriority.setMinimum(1);
        rtPriority.addModifyListener(e -> updateLaunchConfigurationDialog());

        // A disabled button does not respond to mouse events so use a composite.
        final Composite multiplexEventsComp = new Composite(chkBoxComp, SWT.NONE);
        multiplexEventsComp.setLayout(chkBoxLayout);
        multiplexEventsComp.addListener(SWT.MouseHover, event -> multiplexEventsComp.setToolTipText(Messages.PerfOptionsTab_Requires_LTE + multiplexEventsVersion));
        chkMultiplexEvents = createCheckButtonHelper(multiplexEventsComp, PerfPlugin.STRINGS_Multiplex);

        scrollTop.setContent(top);
        recomputeSize();
        updateLaunchConfigurationDialog();
    }

    private void recomputeSize() {
        Point point = top.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        top.setSize(point);
        scrollTop.setMinSize(point);
    }

    // Helper function for creating buttons.
    private Button createCheckButtonHelper(Composite parent, String label) {
        final Button b = new Button(parent, SWT.CHECK);
        b.setText(label);
		b.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
			setDirty(true);
			updateLaunchConfigurationDialog();
		}));
        return b;
    }

    // handles text modification event for kernel file location
    private void handleKernelImageFileTextModify(Text text) {
        String errorMessage = null;
        String filename = text.getText();

        if (filename.length() > 0) {
            File file = new File(filename);
            if (!file.exists() || !file.isFile()) {
                errorMessage = Messages.PerfOptionsTab_Loc_DNE;
            }
        }

        // Update dialog and error message
        setErrorMessage(errorMessage);
        updateLaunchConfigurationDialog();
    }

    /**
     * Handle selection of show stat button
     */
    private void handleShowStatSelection() {
        if (chkShowStat.getSelection()) {
            statRunCount.setEnabled(true);
            toggleButtonsEnablement(false);
        } else {
            statRunCount.setEnabled(false);
            toggleButtonsEnablement(true);
        }
    }

    // Displays a file dialog to allow the user to select the kernel image file
    private void showFileDialog(Shell shell) {
        FileDialog fDialog = new FileDialog(shell, SWT.OPEN);
        File kernel = new File(txtKernelLocation.getText());
        if (!kernel.exists()) {
            kernel = new File("/boot"); //$NON-NLS-1$
            if (!kernel.exists()) {
                kernel = new File("/"); //$NON-NLS-1$
            }
        }
        fDialog.setFileName(kernel.toString());
        fDialog.setText(Messages.PerfOptionsTab_Kernel_Prompt);
        String newKernel = fDialog.open();
        if (newKernel != null) {
            kernel = new File(newKernel);
            if (!kernel.exists()) {
                MessageBox mb = new MessageBox(shell, SWT.ICON_ERROR | SWT.RETRY | SWT.CANCEL);
                mb.setMessage(Messages.PerfOptionsTab_File_DNE);
                switch (mb.open()) {
                    case SWT.RETRY:
                        showFileDialog(shell);
                        break;
                    default:
                }
            } else {
                txtKernelLocation.setText(newKernel);
            }
        }
    }

    /**
     * Toggle enablement of all buttons, excluding the stat button.
     * @param enable enablement of buttons
     */
    private void toggleButtonsEnablement(boolean enable) {
    	PerfVersion version = PerfCore.getPerfVersion(lastConfig);
        txtKernelLocation.setEnabled(enable);
        chkRecordRealtime.setEnabled(enable);
        chkRecordVerbose.setEnabled(enable);
        chkSourceLineNumbers.setEnabled(enable);
        chkKernelSourceLineNumbers.setEnabled(enable);
        if (version != null && multiplexEventsVersion.isNewer(version)) {
            chkMultiplexEvents.setEnabled(enable);
        } else {
            chkMultiplexEvents.setEnabled(false);
        }
        chkModuleSymbols.setEnabled(enable);
        chkHideUnresolvedSymbols.setEnabled(enable);
        chkShowSourceDisassembly.setEnabled(enable);
    }

    @Override
    public String getName() {
        return Messages.PerfOptionsTab_Options;
    }

    @Override
    public void initializeFrom(ILaunchConfiguration config) {

        // Keep track of the last configuration loaded
        lastConfig = config;

        PerfVersion perfVersion = PerfCore.getPerfVersion(config);

        if (perfVersion == null) {
        	setErrorMessage(Messages.PerfLaunchConfigDelegate_perf_not_found);
        } else {
        	setErrorMessage(null);
        }

        try {

            if (perfVersion != null && multiplexEventsVersion.isNewer(perfVersion)) {
                chkMultiplexEvents.setSelection(config.getAttribute(PerfPlugin.ATTR_Multiplex, PerfPlugin.ATTR_Multiplex_default));
            }

            txtKernelLocation.setText(config.getAttribute(PerfPlugin.ATTR_Kernel_Location, PerfPlugin.ATTR_Kernel_Location_default));
            chkRecordRealtime.setSelection(config.getAttribute(PerfPlugin.ATTR_Record_Realtime, PerfPlugin.ATTR_Record_Realtime_default));
            int priority = config.getAttribute(PerfPlugin.ATTR_Record_Realtime_Priority, PerfPlugin.ATTR_Record_Realtime_Priority_default);
            rtPriority.setEnabled(chkRecordRealtime.getSelection());
            rtPriority.setSelection(priority);
            chkRecordVerbose.setSelection(config.getAttribute(PerfPlugin.ATTR_Record_Verbose, PerfPlugin.ATTR_Record_Verbose_default));
            chkSourceLineNumbers.setSelection(config.getAttribute(PerfPlugin.ATTR_SourceLineNumbers, PerfPlugin.ATTR_SourceLineNumbers_default));
            chkKernelSourceLineNumbers.setSelection(config.getAttribute(PerfPlugin.ATTR_Kernel_SourceLineNumbers, PerfPlugin.ATTR_Kernel_SourceLineNumbers_default));

            chkModuleSymbols.setSelection(config.getAttribute(PerfPlugin.ATTR_ModuleSymbols, PerfPlugin.ATTR_ModuleSymbols_default));
            chkHideUnresolvedSymbols.setSelection(config.getAttribute(PerfPlugin.ATTR_HideUnresolvedSymbols, PerfPlugin.ATTR_HideUnresolvedSymbols_default));
            chkShowSourceDisassembly.setSelection(config.getAttribute(PerfPlugin.ATTR_ShowSourceDisassembly, PerfPlugin.ATTR_ShowSourceDisassembly_default));
            chkShowStat.setSelection(config.getAttribute(PerfPlugin.ATTR_ShowStat, PerfPlugin.ATTR_ShowStat_default));
            int runCount = config.getAttribute(PerfPlugin.ATTR_StatRunCount, PerfPlugin.ATTR_StatRunCount_default);
            statRunCount.setSelection(runCount);
            handleShowStatSelection();
        } catch (CoreException e) {
            // do nothing
        }
    }

    @Override
    public void performApply(ILaunchConfigurationWorkingCopy wconfig) {

        PerfVersion perfVersion = PerfCore.getPerfVersion(wconfig);

        if (perfVersion != null && multiplexEventsVersion.isNewer(perfVersion)) {
            wconfig.setAttribute(PerfPlugin.ATTR_Multiplex, chkMultiplexEvents.getSelection());
        }

        wconfig.setAttribute(PerfPlugin.ATTR_Kernel_Location, txtKernelLocation.getText());
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Realtime, chkRecordRealtime.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Realtime_Priority, rtPriority.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Verbose, chkRecordVerbose.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_SourceLineNumbers, chkSourceLineNumbers.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_Kernel_SourceLineNumbers, chkKernelSourceLineNumbers.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_ModuleSymbols, chkModuleSymbols.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_HideUnresolvedSymbols, chkHideUnresolvedSymbols.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_ShowSourceDisassembly, chkShowSourceDisassembly.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_ShowStat, chkShowStat.getSelection());
        wconfig.setAttribute(PerfPlugin.ATTR_StatRunCount, statRunCount.getSelection());
    }

    @Override
    public void setDefaults(ILaunchConfigurationWorkingCopy wconfig) {
        wconfig.setAttribute(PerfPlugin.ATTR_Kernel_Location, PerfPlugin.ATTR_Kernel_Location_default);
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Realtime, PerfPlugin.ATTR_Record_Realtime_default);
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Realtime_Priority, PerfPlugin.ATTR_Record_Realtime_Priority_default);
        wconfig.setAttribute(PerfPlugin.ATTR_Record_Verbose, PerfPlugin.ATTR_Record_Verbose_default);
        wconfig.setAttribute(PerfPlugin.ATTR_SourceLineNumbers, PerfPlugin.ATTR_SourceLineNumbers_default);
        wconfig.setAttribute(PerfPlugin.ATTR_Kernel_SourceLineNumbers, PerfPlugin.ATTR_Kernel_SourceLineNumbers_default);
        wconfig.setAttribute(PerfPlugin.ATTR_Multiplex, PerfPlugin.ATTR_Multiplex_default);
        wconfig.setAttribute(PerfPlugin.ATTR_ModuleSymbols, PerfPlugin.ATTR_ModuleSymbols_default);
        wconfig.setAttribute(PerfPlugin.ATTR_HideUnresolvedSymbols, PerfPlugin.ATTR_HideUnresolvedSymbols_default);
        wconfig.setAttribute(PerfPlugin.ATTR_ShowSourceDisassembly, PerfPlugin.ATTR_ShowSourceDisassembly_default);
        wconfig.setAttribute(PerfPlugin.ATTR_ShowStat, PerfPlugin.ATTR_ShowStat_default);
        wconfig.setAttribute(PerfPlugin.ATTR_StatRunCount, PerfPlugin.ATTR_StatRunCount_default);
    }

}
