/*******************************************************************************
 * Copyright (c) 2010, 2016 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:
 *     Serge Beauchamp (Freescale Semiconductor) - initial API and implementation
 *     Mickael Istria (Red Hat Inc.) - Bug 486901
 *******************************************************************************/
package org.eclipse.ui.ide.dialogs;

import java.io.File;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.internal.ide.IDEInternalPreferences;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
import org.eclipse.ui.internal.ide.dialogs.LinkedResourcesPreferencePage;
import org.eclipse.ui.internal.ide.dialogs.RelativePathVariableGroup;


/**
 * Dialog to let the user customise how files and resources are created in a project
 * hierarchy after the user drag and drop items on a workspace container.
 *
 * Files and folders can be created either by copying the source objects, creating
 * linked resources, and/or creating virtual folders.
 * @noextend This class is not intended to be subclassed by clients.
 * @since 3.6
 *
 */
public class ImportTypeDialog extends TrayDialog {

	/**
	 * Copy the files and folders to the destination
	 */
	public static final int IMPORT_COPY 			= 1;
	/**
	 * Import only files
	 */
	public static final int IMPORT_FILES_ONLY		= 16;
	/**
	 * Create linked resources for each file and folder
	 */
	public static final int IMPORT_LINK 			= 4;
	/**
	 * Move the files and folders to the destination
	 */
	public static final int IMPORT_MOVE 			= 8;
	/**
	 * Do not perform an import operation
	 */
	public static final int IMPORT_NONE 			= 0;
	/**
	 * Recreate the file and folder hierarchy using groups and links
	 */
	public static final int IMPORT_VIRTUAL_FOLDERS_AND_LINKS = 2;

	private Button copyButton = null;

	private int currentSelection;

	private Button linkButton = null;

	private Button moveButton = null;

	private int operationMask;
	private String preferredVariable;
	private IResource receivingResource = null;
	private Button shadowCopyButton = null;
	private String variable = null;

	private RelativePathVariableGroup relativePathVariableGroup;

	/**
	 * Creates the Import Type Dialog when resources are dragged and dropped from an Eclipse
	 * view.
	 *
	 * @param shell
	 * 			the parent Shell
	 * @param dropOperation
	 * 		The dropOperation that was used by the user
	 * @param sources
	 * 		The list of resources that were dragged
	 * @param target
	 * 		The target container onto which the resources were dropped
	 */
	public ImportTypeDialog(Shell shell, int dropOperation,
			IResource[] sources, IContainer target) {
		this(shell, selectAppropriateMask(dropOperation, sources, target), RelativePathVariableGroup.getPreferredVariable(sources, target));
	}

	/**
	 * Creates the Import Type Dialog when files are dragged and dropped from the
	 * operating system's shell (Windows Explorer on Windows Platform, for example).
	 *
	 * @param shell
	 * 			the parent Shell
	 * @param dropOperation
	 * 		The dropOperation that was used by the user
	 * @param names
	 * 		The list of files that were dragged
	 * @param target
	 * 		The target container onto which the files were dropped
	 */
	public ImportTypeDialog(Shell shell, int dropOperation, String[] names, IContainer target) {
		this(shell, selectAppropriateMask(dropOperation, names, target), RelativePathVariableGroup.getPreferredVariable(names, target));
	}

	/**
	 * @param parentShell
	 * @param operationMask
	 */
	private ImportTypeDialog(Shell parentShell, int operationMask, String preferredVariable) {
		super(parentShell);

		this.preferredVariable = preferredVariable;
		this.operationMask = operationMask;
		currentSelection = 0;
		String tmp = readContextPreference(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_TYPE);
		if (tmp.length() > 0)
			currentSelection = Integer.parseInt(tmp);
		currentSelection = currentSelection & operationMask;
		if (currentSelection == 0) {
			if (hasFlag(IMPORT_COPY))
				currentSelection = IMPORT_COPY;
			else
				currentSelection = IMPORT_MOVE;
		}

		IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
		if (store.getBoolean(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_RELATIVE))
			variable = preferredVariable;
	}

	@Override
	public boolean close() {
		return super.close();
	}

	/**
	 * Get the user selection from the dialog.
	 * @return The current selection (one of IMPORT_COPY, IMPORT_VIRTUAL_FOLDERS_AND_LINKS, IMPORT_LINK and IMPORT_MOVE)
	 */
	public int getSelection() {
		return currentSelection;
	}

	/**
	 * Get the selected variable if the selection is either IMPORT_VIRTUAL_FOLDERS_AND_LINKS or IMPORT_LINK
	 * @return The currently selected variable, or AUTOMATIC or ABSOLUTE_PATH
	 */
	public String getVariable() {
		return variable;
	}

	/** Set the project that is the destination of the import operation
	 * @param resource the resource
	 */
	public void setResource(IResource resource) {
		receivingResource = resource;
	}

	private boolean hasFlag(int flag) {
		return (operationMask & flag) != 0;
	}

	// the format of the context is operationMask,value:operationMask,value:operationMask,value
	private String readContextPreference(String key) {
		String value = IDEWorkbenchPlugin.getDefault().getPreferenceStore().getString(key);
		for (String keyPair : value.split(":")) { //$NON-NLS-1$
			String [] element = keyPair.split(","); //$NON-NLS-1$
			if (element.length == 2) {
				if (element[0].equals(Integer.toString(operationMask)))
					return element[1];
			}
		}
		return ""; //$NON-NLS-1$
	}

	private void refreshSelection() {
		if (copyButton != null)
			copyButton.setSelection(currentSelection == IMPORT_COPY);
		if (shadowCopyButton != null)
			shadowCopyButton.setSelection(currentSelection == IMPORT_VIRTUAL_FOLDERS_AND_LINKS);
		if (linkButton != null)
			linkButton.setSelection(currentSelection == IMPORT_LINK);
		if (moveButton != null)
			moveButton.setSelection(currentSelection == IMPORT_MOVE);
		if (relativePathVariableGroup != null) {
			relativePathVariableGroup.setEnabled((currentSelection & (IMPORT_VIRTUAL_FOLDERS_AND_LINKS | IMPORT_LINK)) != 0);
		}
	}

	private void writeContextPreference(String key, String value) {
		String oldValue = IDEWorkbenchPlugin.getDefault().getPreferenceStore().getString(key);
		StringBuilder buffer = new StringBuilder();
		String [] keyPairs = oldValue.split(":"); //$NON-NLS-1$
		boolean found = false;
		for (int i = 0; i < keyPairs.length; i++) {
			if (i > 0)
				buffer.append(":"); //$NON-NLS-1$
			String [] element = keyPairs[i].split(","); //$NON-NLS-1$
			if (element.length == 2) {
				if (element[0].equals(Integer.toString(operationMask))) {
					buffer.append(element[0] + "," + value); //$NON-NLS-1$
					found = true;
				}
				else
					buffer.append(keyPairs[i]);
			}
		}
		if (!found) {
			if (buffer.length() > 0)
				buffer.append(":"); //$NON-NLS-1$
			buffer.append(operationMask + "," + value); //$NON-NLS-1$
		}
		String newValue = buffer.toString();
		IDEWorkbenchPlugin.getDefault().getPreferenceStore().setValue(key, newValue);
	}

	@Override
	protected void buttonPressed(int buttonId) {
		if (buttonId == IDialogConstants.OK_ID) {
			writeContextPreference(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_TYPE, Integer.toString(currentSelection));

			IPreferenceStore store = IDEWorkbenchPlugin.getDefault().getPreferenceStore();
			store.putValue(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_RELATIVE, Boolean.toString(variable != null));
		}
		super.buttonPressed(buttonId);
	}

	@Override
	protected void configureShell(Shell shell) {
		super.configureShell(shell);
		String title = (operationMask & IMPORT_FILES_ONLY) != 0 ? IDEWorkbenchMessages.ImportTypeDialog_titleFilesOnly:
			IDEWorkbenchMessages.ImportTypeDialog_title;
		shell.setText(title);
		PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
				IIDEHelpContextIds.IMPORT_TYPE_DIALOG);
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		boolean linkIsOnlyChoice = hasFlag(IMPORT_LINK) && !(hasFlag(IMPORT_COPY | IMPORT_MOVE) || (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS) && !hasFlag(IMPORT_FILES_ONLY)));

		if (!linkIsOnlyChoice)
			createMessageArea(parent);
		Composite composite = new Composite(parent, 0);
		GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
		composite.setLayoutData(gridData);
		composite.setFont(parent.getFont());


		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
		layout.verticalSpacing= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
		layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);

		int indent= linkIsOnlyChoice ? 0: convertWidthInCharsToPixels(3);

		layout.marginWidth += indent;
		composite.setLayout(layout);
		SelectionListener listener = new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				currentSelection = ((Integer) e.widget.getData()).intValue();
				refreshSelection();
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				currentSelection = ((Integer) e.widget.getData()).intValue();
				refreshSelection();
			}
		};

		if (hasFlag(IMPORT_COPY)) {
			copyButton = new Button(composite, SWT.RADIO);
			copyButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_copyFiles: IDEWorkbenchMessages.ImportTypeDialog_copyFilesAndDirectories);
			gridData = new GridData(GridData.FILL_HORIZONTAL);
			copyButton.setLayoutData(gridData);
			copyButton.setData(IMPORT_COPY);
			copyButton.addSelectionListener(listener);
			copyButton.setFont(parent.getFont());
		}

		if (hasFlag(IMPORT_MOVE)) {
			moveButton = new Button(composite, SWT.RADIO);
			moveButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_moveFiles:IDEWorkbenchMessages.ImportTypeDialog_moveFilesAndDirectories);
			gridData = new GridData(GridData.FILL_HORIZONTAL);
			moveButton.setLayoutData(gridData);
			moveButton.setData(IMPORT_MOVE);
			moveButton.addSelectionListener(listener);
			moveButton.setFont(parent.getFont());
		}

		if (hasFlag(IMPORT_LINK) && !linkIsOnlyChoice) {
			linkButton = new Button(composite, SWT.RADIO);
			linkButton.setText(hasFlag(IMPORT_FILES_ONLY) ? IDEWorkbenchMessages.ImportTypeDialog_linkFiles:IDEWorkbenchMessages.ImportTypeDialog_createLinks);
			gridData = new GridData(GridData.FILL_HORIZONTAL);
			linkButton.setLayoutData(gridData);
			linkButton.setData(IMPORT_LINK);
			linkButton.addSelectionListener(listener);
			linkButton.setFont(parent.getFont());
		}

		if (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS) && !hasFlag(IMPORT_FILES_ONLY)) {
			shadowCopyButton = new Button(composite, SWT.RADIO);
			shadowCopyButton.setText(IDEWorkbenchMessages.ImportTypeDialog_recreateFilesAndDirectories);
			gridData = new GridData(GridData.FILL_HORIZONTAL);
			shadowCopyButton.setLayoutData(gridData);
			shadowCopyButton.setData(IMPORT_VIRTUAL_FOLDERS_AND_LINKS);
			shadowCopyButton.addSelectionListener(listener);
			shadowCopyButton.setFont(parent.getFont());
		}

		if (hasFlag(IMPORT_VIRTUAL_FOLDERS_AND_LINKS | IMPORT_LINK)) {
			relativePathVariableGroup = new RelativePathVariableGroup(new RelativePathVariableGroup.IModel() {
				@Override
				public IResource getResource() {
					return receivingResource;
				}
				@Override
				public void setVariable(String string) {
					variable = string;
				}
				@Override
				public String getVariable() {
					return variable;
				}
			});

			int groupIndent = 0;

			if (!linkIsOnlyChoice) {
				Button tmp = new Button(composite, SWT.CHECK);
				tmp.setText("."); //$NON-NLS-1$
				groupIndent = tmp.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
				tmp.dispose();

				Label tmpLabel = new Label(composite, SWT.NONE);
				tmpLabel.setText("."); //$NON-NLS-1$
				groupIndent -= tmpLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
				tmpLabel.dispose();
			}

			Composite variableGroup = new Composite(composite, 0);
			gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
			gridData.horizontalIndent = groupIndent;
			variableGroup.setLayoutData(gridData);
			variableGroup.setFont(parent.getFont());

			layout = new GridLayout();
			layout.numColumns = 2;
			layout.marginWidth= 0;
			variableGroup.setLayout(layout);

			relativePathVariableGroup.createContents(variableGroup);
			relativePathVariableGroup.setSelection(variable != null);
			if (variable != null)
				relativePathVariableGroup.selectVariable(variable);
			else
				relativePathVariableGroup.selectVariable(preferredVariable);
		}

		if (linkIsOnlyChoice) {
			currentSelection = IMPORT_LINK;
			parent.getShell().setText(IDEWorkbenchMessages.ImportTypeDialog_titleFilesLinking);
		}
		createLinkControl(parent);
		refreshSelection();
		return composite;
	}

	private Control createLinkControl(Composite composite) {
		Link link= new Link(composite, SWT.WRAP | SWT.RIGHT);
		link.setText(IDEWorkbenchMessages.ImportTypeDialog_configureSettings);
		link.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				openSettingsPage();
			}
		});
		GridData gridData= new GridData(GridData.FILL, GridData.CENTER, true, false);
		gridData.horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
		link.setLayoutData(gridData);
		link.setFont(composite.getFont());

		return link;
	}

	protected void openSettingsPage() {
		String prefID = LinkedResourcesPreferencePage.PREF_ID;
		PreferencesUtil.createPreferenceDialogOn(getShell(), prefID, new String[] {prefID}, null).open();
	}

	protected Control createMessageArea(Composite parent) {
		Composite composite = new Composite(parent, 0);
		GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
		composite.setLayoutData(gridData);
		composite.setFont(parent.getFont());


		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		layout.marginTop= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
		layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
		composite.setLayout(layout);

		String message = (operationMask & IMPORT_FILES_ONLY) != 0 ? IDEWorkbenchMessages.ImportTypeDialog_questionFilesOnly:
			IDEWorkbenchMessages.ImportTypeDialog_question;

		// create message
		if (message != null) {
			Label messageLabel = new Label(composite, SWT.WRAP);
			messageLabel.setFont(parent.getFont());
			messageLabel.setText(message);
			gridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
			messageLabel.setLayoutData(gridData);
		}
		return composite;
	}

	/**
	 * @param resources
	 * 		The list of items that were dragged
	 * @return true if a set of paths are files only or a mix of files and folders, false otherwise
	 */
	private static boolean areOnlyFiles(IResource[] resources) {
		for (IResource resource : resources) {
			if (resource.getType() != IResource.FILE)
				return false;
		}
		return true;
	}

	/**
	 * @param names
	 * 		The list of items that were dragged
	 * @return true if a set of paths are files only or a mix of files and folders, false otherwise
	 */
	private static boolean areOnlyFiles(String[] names) {
		for (String name : names) {
			File file = new File(name);
			if (file.exists() && !file.isFile())
				return false;
		}
		return true;
	}

	/**
	 * Select the most appropriate mode that should be used for the dialog given
	 * the items dropped on the container, the container type, and the drop operation.
	 *
	 * @param dropOperation
	 * @param resources
	 * 		The list of items that were dragged
	 * @param target
	 * 		The target container onto which the items were dropped
	 * @return the appropriate import mask given the files dropped on the target
	 */
	private static int selectAppropriateMask(int dropOperation, IResource[] resources, IContainer target) {
		int mask = ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS | ImportTypeDialog.IMPORT_LINK;
		if (!target.isVirtual() && (dropOperation != DND.DROP_LINK))
			mask |= ImportTypeDialog.IMPORT_COPY;
		if (areOnlyFiles(resources))
			mask |= ImportTypeDialog.IMPORT_FILES_ONLY;
		return mask;
	}

	/**
	 * Select the most appropriate mode that should be used for the dialog given
	 * the items dropped on the container, the container type, and the drop operation.
	 *
	 * @param dropOperation
	 * @param names
	 * 		The list of items that were dragged
	 * @param target
	 * 		The target container onto which the items were dropped
	 * @return the appropriate import mask given the files dropped on the target
	 */
	private static int selectAppropriateMask(int dropOperation, String[] names, IContainer target) {
		int mask = ImportTypeDialog.IMPORT_VIRTUAL_FOLDERS_AND_LINKS | ImportTypeDialog.IMPORT_LINK;
		if (!target.isVirtual() && (dropOperation != DND.DROP_LINK))
			mask |= ImportTypeDialog.IMPORT_COPY;
		if (areOnlyFiles(names))
			mask |= ImportTypeDialog.IMPORT_FILES_ONLY;
		return mask;
	}
}
