/*******************************************************************************
 * Copyright (c) 2006, 2015 IBM Corporation and others.
 *
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal.ide;

import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
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.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.events.IExpansionListener;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.preferences.SettingsTransfer;

/**
 * The ChooseWorkspaceWithSettingsDialog is the dialog used to switch workspaces
 * with an optional settings export.
 *
 * @since 3.3
 *
 */
public class ChooseWorkspaceWithSettingsDialog extends ChooseWorkspaceDialog {

	private static final String WORKBENCH_SETTINGS = "WORKBENCH_SETTINGS"; //$NON-NLS-1$
	private static final String ENABLED_TRANSFERS = "ENABLED_TRANSFERS"; //$NON-NLS-1$

	/**
	 * The class attribute for a settings transfer.
	 */
	private static final String ATT_CLASS = "class"; //$NON-NLS-1$
	/**
	 * The name attribute for the settings transfer.
	 */
	private static final String ATT_NAME = "name"; //$NON-NLS-1$
	/**
	 * The id attribute for the settings transfer.
	 */
	private static final String ATT_ID = "id"; //$NON-NLS-1$
	private static final String ATT_HELP_CONTEXT = "helpContext"; //$NON-NLS-1$

	private Collection<IConfigurationElement> selectedSettings = new HashSet<>();

	/**
	 * Open a new instance of the receiver.
	 *
	 * @param parentShell
	 * @param launchData
	 * @param suppressAskAgain
	 * @param centerOnMonitor
	 */
	public ChooseWorkspaceWithSettingsDialog(Shell parentShell,
			ChooseWorkspaceData launchData, boolean suppressAskAgain,
			boolean centerOnMonitor) {
		super(parentShell, launchData, suppressAskAgain, centerOnMonitor);
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		Control top = super.createDialogArea(parent);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
				IIDEHelpContextIds.SWITCH_WORKSPACE_ACTION);
		createSettingsControls((Composite) top);
		applyDialogFont(parent);
		return top;

	}

	/**
	 * Create the controls for selecting the controls we are going to export.
	 *
	 * @param workArea
	 */
	private void createSettingsControls(Composite workArea) {
		final FormToolkit toolkit = new FormToolkit(workArea.getDisplay());
		workArea.addDisposeListener(e -> toolkit.dispose());
		final ScrolledForm form = toolkit.createScrolledForm(workArea);
		form.getBody().setBackground(workArea.getBackground());
		form.getBody().setLayout(new GridLayout());
		form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		final ExpandableComposite copySettingsExpandable =
				toolkit.createExpandableComposite(form.getBody(), ExpandableComposite.TWISTIE);

		copySettingsExpandable.setText(IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_SettingsGroupName);
		copySettingsExpandable.setBackground(workArea.getBackground());
		copySettingsExpandable.setLayout(new GridLayout());
		copySettingsExpandable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		copySettingsExpandable.addExpansionListener(new IExpansionListener() {

			@Override
			public void expansionStateChanged(ExpansionEvent e) {
				form.reflow(true);
				Point size = getInitialSize();
				Shell shell = getShell();
				shell.setBounds(getConstrainedShellBounds(
						new Rectangle(shell.getLocation().x, shell.getLocation().y, size.x, size.y)));
			}

			@Override
			public void expansionStateChanging(ExpansionEvent e) {
				// Nothing to do here

			}
		});

		Composite sectionClient = toolkit.createComposite(copySettingsExpandable);
		sectionClient.setBackground(workArea.getBackground());

		if (createButtons(toolkit, sectionClient))
			copySettingsExpandable.setExpanded(true);

		copySettingsExpandable.setClient(sectionClient);

	}

	/**
	 * Create the buttons for the settings transfer.
	 *
	 * @param toolkit
	 * @param sectionClient
	 * @return boolean <code>true</code> if any were selected
	 */
	private boolean createButtons(FormToolkit toolkit, Composite sectionClient) {
		String[] enabledSettings = getEnabledSettings(IDEWorkbenchPlugin
				.getDefault().getDialogSettings()
				.getSection(WORKBENCH_SETTINGS));

		RowLayout layout = new RowLayout(SWT.VERTICAL);
		layout.marginLeft = 14;
		layout.spacing = 6;
		sectionClient.setLayout(layout);

		for (final IConfigurationElement settingsTransfer : SettingsTransfer.getSettingsTransfers()) {
			final Button button = toolkit.createButton(sectionClient,
					settingsTransfer.getAttribute(ATT_NAME), SWT.CHECK);

			ControlDecoration deco = new ControlDecoration(button, SWT.TOP | SWT.RIGHT);
			Image image = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_WARNING)
					.getImage();
			deco.setDescriptionText(IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_copySettingsDecoLabel);
			deco.setImage(image);

			toggleDecoForSettingsImportButtons(button, deco);
			getCombo().addModifyListener(e -> toggleDecoForSettingsImportButtons(button, deco));

			String helpId = settingsTransfer.getAttribute(ATT_HELP_CONTEXT);

			if (helpId != null)
				PlatformUI.getWorkbench().getHelpSystem().setHelp(button, helpId);

			if (enabledSettings != null && enabledSettings.length > 0) {

				String id = settingsTransfer.getAttribute(ATT_ID);
				for (String enabledSetting : enabledSettings) {
					if (enabledSetting.equals(id)) {
						button.setSelection(true);
						selectedSettings.add(settingsTransfer);
						break;
					}
				}
			}

			button.setBackground(sectionClient.getBackground());
			button.addSelectionListener(new SelectionAdapter() {

				@Override
				public void widgetSelected(SelectionEvent e) {
					if (button.getSelection()) {
						selectedSettings.add(settingsTransfer);
					} else {
						selectedSettings.remove(settingsTransfer);
					}
					toggleDecoForSettingsImportButtons(button, deco);
				}
			});

		}
		return enabledSettings != null && enabledSettings.length > 0;
	}

	private void toggleDecoForSettingsImportButtons(Button button, ControlDecoration deco) {
		if (!button.getSelection()) {
			deco.hide();
			return;
		}

		if (Files.exists(Paths.get(getWorkspaceLocation()), LinkOption.NOFOLLOW_LINKS)) {
			deco.show();
		} else {
			deco.hide();
		}
	}
	/**
	 * Get the settings for the receiver based on the entries in section.
	 *
	 * @param section
	 * @return String[] or <code>null</code>
	 */
	private String[] getEnabledSettings(IDialogSettings section) {

		if (section == null)
			return null;

		return section.getArray(ENABLED_TRANSFERS);

	}

	@Override
	protected void okPressed() {
		Iterator<IConfigurationElement> settingsIterator = selectedSettings.iterator();
		MultiStatus result = new MultiStatus(
				PlatformUI.PLUGIN_ID,
				IStatus.OK,
				IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_ProblemsTransferTitle,
				null);

		IPath path = new Path(getWorkspaceLocation());
		String[] selectionIDs = new String[selectedSettings.size()];
		int index = 0;

		while (settingsIterator.hasNext()) {
			IConfigurationElement elem = settingsIterator
					.next();
			result.add(transferSettings(elem, path));
			selectionIDs[index] = elem.getAttribute(ATT_ID);
		}
		if (result.getSeverity() != IStatus.OK) {
			ErrorDialog
					.openError(
							getShell(),
							IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_TransferFailedMessage,
							IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_SaveSettingsFailed,
							result);
			return;
		}

		saveSettings(selectionIDs);
		super.okPressed();
	}

	/**
	 * Save the ids of the selected elements.
	 *
	 * @param selectionIDs
	 */
	private void saveSettings(String[] selectionIDs) {
		IDialogSettings settings = IDEWorkbenchPlugin.getDefault()
				.getDialogSettings().getSection(WORKBENCH_SETTINGS);

		if (settings == null)
			settings = IDEWorkbenchPlugin.getDefault().getDialogSettings()
					.addNewSection(WORKBENCH_SETTINGS);

		settings.put(ENABLED_TRANSFERS, selectionIDs);

	}

	/**
	 * Take the values from element and execute the class for path.
	 *
	 * @param elem
	 * @param path
	 * @return IStatus the result of the settings transfer.
	 */
	private IStatus transferSettings(final IConfigurationElement element,
			final IPath path) {

		final IStatus[] exceptions = new IStatus[1];

		SafeRunner.run(new ISafeRunnable() {
			@Override
			public void run() throws Exception {

				try {
					SettingsTransfer transfer = (SettingsTransfer) WorkbenchPlugin
							.createExtension(element, ATT_CLASS);
					transfer.transferSettings(path);
				} catch (CoreException exception) {
					exceptions[0] = exception.getStatus();
				}

			}

			@Override
			public void handleException(Throwable exception) {
				exceptions[0] = StatusUtil
						.newStatus(
								IStatus.ERROR,
								NLS
										.bind(
												IDEWorkbenchMessages.ChooseWorkspaceWithSettingsDialog_ClassCreationFailed,
												element.getAttribute(ATT_CLASS)),
								exception);

			}
		});

		if (exceptions[0] != null)
			return exceptions[0];

		return Status.OK_STATUS;

	}

	@Override
	protected int getDialogBoundsStrategy() {
		return DIALOG_PERSISTLOCATION;
	}
}
