/**********************************************************************
 * Copyright (c) 2014 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Bernd Hufmann - Initial API and implementation
 **********************************************************************/
package org.eclipse.tracecompass.internal.lttng2.control.ui.views.dialogs;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.lttng2.control.ui.Activator;
import org.eclipse.tracecompass.internal.lttng2.control.ui.views.messages.Messages;
import org.eclipse.tracecompass.tmf.core.io.BufferedRandomAccessFile;
import org.eclipse.tracecompass.tmf.ui.dialog.TmfFileDialogFactory;

import com.google.common.collect.ImmutableList;

/**
 * <p>
 * Dialog box for selecting a command script. It parses the script and
 * provides a list of shell commands to be executed.
 * </p>
 *
 * @author Bernd Hufmann
 */
public class OpenCommandScriptDialog extends Dialog implements ISelectCommandScriptDialog {

    // ------------------------------------------------------------------------
    // Constants
    // ------------------------------------------------------------------------
    /**
     * The icon file for this dialog box.
     */
    public static final String CREATE_SESSION_ICON_FILE = "icons/elcl16/add_button.gif"; //$NON-NLS-1$

    // Dialog settings constants
    private static final String DIALOG_SETTINGS_SECTION = "OpenCommandScriptDialog"; //$NON-NLS-1$
    private static final String FILE_NAME_ID = "STORE_FILE_NAME_ID"; //$NON-NLS-1$
    private static final int COMBO_HISTORY_LENGTH = 5;

    // ------------------------------------------------------------------------
    // Attributes
    // ------------------------------------------------------------------------

    // Dialog attributes
    private Control fControl = null;
    private Composite fDialogComposite = null;
    private Button fBrowseButton;
    private Label fFileNameLabel = null;
    private Combo fFileNameCombo = null;

    // Output list of commands
    private List<String> fCommands = null;

    // ------------------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------------------
    /**
     * Constructor
     * @param shell - a shell for the display of the dialog
     */
    public OpenCommandScriptDialog(Shell shell) {
        super(shell);
        setShellStyle(SWT.RESIZE | getShellStyle());
    }

    // ------------------------------------------------------------------------
    // Accessors
    // ------------------------------------------------------------------------

    @Override
    @NonNull public List<String> getCommands() {
        if (fCommands != null) {
            return fCommands;
        }
        return new ArrayList<>();
    }

    // ------------------------------------------------------------------------
    // Operations
    // ------------------------------------------------------------------------

    @Override
    protected Control createContents(Composite parent) {
        fControl = super.createContents(parent);

        /* set the shell minimum size */
        Point clientArea = fControl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
        Rectangle trim = getShell().computeTrim(0, 0, clientArea.x, clientArea.y);
        getShell().setMinimumSize(trim.width, trim.height);

        return fControl;
    }

    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText(Messages.TraceControl_ExecuteScriptDialogTitle);
        newShell.setImage(Activator.getDefault().loadIcon(CREATE_SESSION_ICON_FILE));
    }

    @Override
    protected Control createDialogArea(Composite parent) {

        // Main dialog panel
        fDialogComposite = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout(1, true);
        fDialogComposite.setLayout(layout);
        fDialogComposite.setLayoutData(new GridData(GridData.FILL_BOTH));

        Group sessionGroup = new Group(fDialogComposite, SWT.SHADOW_NONE);
        sessionGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        sessionGroup.setLayout(new GridLayout(6, true));

        fFileNameLabel = new Label(sessionGroup, SWT.RIGHT);
        fFileNameLabel.setText(Messages.TraceControl_ExecuteScriptSelectLabel);
        fFileNameCombo = new Combo(sessionGroup, SWT.BORDER);

        fBrowseButton = new Button(sessionGroup, SWT.PUSH);
        fBrowseButton.setText(Messages.TraceControl_ExecuteScriptBrowseText);
        fBrowseButton.addListener(SWT.Selection, event -> handleFilePathBrowseButtonPressed(SWT.OPEN));

        // layout widgets
        GridData data = new GridData(GridData.FILL_HORIZONTAL);
        data.horizontalSpan = 1;
        data.grabExcessHorizontalSpace = false;
        fFileNameLabel.setLayoutData(data);

        data = new GridData(GridData.FILL_HORIZONTAL);
        data.horizontalSpan = 4;
        fFileNameCombo.setLayoutData(data);

        data = new GridData(GridData.FILL_HORIZONTAL);
        data.horizontalSpan = 1;

        // Initialize a empty list
        fCommands = new ArrayList<>();

        restoreWidgetValues();

        return fDialogComposite;
    }

    private void restoreWidgetValues() {
        IDialogSettings workbenchSettings = Activator.getDefault().getDialogSettings();
        IDialogSettings settings = workbenchSettings.getSection(DIALOG_SETTINGS_SECTION);
        if (settings == null) {
            settings = workbenchSettings.addNewSection(DIALOG_SETTINGS_SECTION);
        }
        String[] fileNames = settings.getArray(FILE_NAME_ID);
        if ((fileNames != null) && (fileNames.length != 0)) {
            for (int i = 0; i < fileNames.length; i++) {
                fFileNameCombo.add(fileNames[i]);
            }
        }
    }

    private void saveWidgetValues() {
        IDialogSettings workbenchSettings = Activator.getDefault().getDialogSettings();
        IDialogSettings settings = workbenchSettings.getSection(DIALOG_SETTINGS_SECTION);
        if (settings != null) {
            // update file names history
            String[] fileNames = settings.getArray(FILE_NAME_ID);
            if (fileNames == null) {
                fileNames = new String[0];
            }

            fileNames = addToHistory(fileNames, fFileNameCombo.getText().trim());
            settings.put(FILE_NAME_ID, fileNames);
        }
    }

    /**
     * Adds an entry to a history, while taking care of duplicate history items
     * and excessively long histories.  The assumption is made that all histories
     * should be of length <code>COMBO_HISTORY_LENGTH</code>.
     *
     * @param history the current history
     * @param newEntry the entry to add to the history
     */
    private static String[] addToHistory(String[] history, String newEntry) {
        List<String> list = new ArrayList<>(Arrays.asList(history));
        list.remove(newEntry);
        list.add(0, newEntry);

        // since only one new item was added, we can be over the limit
        // by at most one item
        if (list.size() > COMBO_HISTORY_LENGTH) {
            list.remove(COMBO_HISTORY_LENGTH);
        }
        String[] r = new String[list.size()];
        list.toArray(r);
        return r;
    }

    private void handleFilePathBrowseButtonPressed(int fileDialogStyle) {
        FileDialog dialog = TmfFileDialogFactory.create(getShell(), fileDialogStyle | SWT.SHEET);
        dialog.setFilterExtensions(new String[] { "*.*", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
        dialog.setText(Messages.TraceControl_ExecuteScriptDialogTitle);
        String selectedFileName = dialog.open();
        if (selectedFileName != null) {
            fFileNameCombo.setText(selectedFileName);
        }
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, true);
        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
    }

    @Override
    protected void okPressed() {
        // Validate input data
        String sessionPath = fFileNameCombo.getText();

        if (!"".equals(sessionPath)) { //$NON-NLS-1$

            ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
            try (BufferedRandomAccessFile rafile = new BufferedRandomAccessFile(sessionPath, "r")) { //$NON-NLS-1$
                String line = rafile.getNextLine();
                while (line != null) {
                    builder.add(line);
                    line = rafile.getNextLine();
                }
            } catch (IOException e) {
                ErrorDialog.openError(getShell(), null, null, new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, e.getLocalizedMessage(), e));
                return;
            }
            saveWidgetValues();
            fCommands = builder.build();
            super.okPressed();
        }
    }
}
