/*******************************************************************************
 * Copyright (c) 2000, 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.dialogs;

import java.util.Collections;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.Util;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
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.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.actions.OpenFileAction;
import org.eclipse.ui.actions.OpenWithMenu;
import org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog;
import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
import org.eclipse.ui.internal.ShowInMenu;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.views.IViewDescriptor;


/**
 * Shows a list of resources to the user with a text entry field for a string
 * pattern used to filter the list of resources.
 *
 * @since 2.1
 */
public class OpenResourceDialog extends FilteredResourcesSelectionDialog {

	private final class ResourceOpenWithMenu extends OpenWithMenu {
		private ResourceOpenWithMenu(IWorkbenchPage page, IAdaptable file) {
			super(page, file);
		}

		@Override
		protected void openEditor(IEditorDescriptor editorDescriptor, boolean openUsingDescriptor) {
			computeResult();
			setResult(Collections.EMPTY_LIST);
			close();
			super.openEditor(editorDescriptor, openUsingDescriptor);
		}
	}

	private final class ResourceShowInMenu extends ShowInMenu {
		private final IStructuredSelection selectedItems;

		private ResourceShowInMenu(IStructuredSelection selectedItems, IWorkbenchWindow workbenchWindow) {
			this.selectedItems = selectedItems;
			setId(ContributionItemFactory.VIEWS_SHOW_IN.getId());
			initialize(workbenchWindow);
		}

		@Override
		protected ShowInContext getContext(IWorkbenchPart sourcePart) {
			return new ShowInContext(null, selectedItems);
		}

		@Override
		protected IWorkbenchPart getSourcePart() {
			return null;
		}

		@Override
		protected IContributionItem getContributionItem(IViewDescriptor viewDescriptor) {
			final String targetId= viewDescriptor.getId();
			String label = '&' + viewDescriptor.getLabel();
			ImageDescriptor icon = viewDescriptor.getImageDescriptor();
			Action action = new Action(label, icon) {
				@Override
				public void run() {
					computeResult();
					setResult(Collections.EMPTY_LIST);
					close();

					IWorkbenchPage page = getActivePage();
					IViewPart view;
					try {
						view = page.showView(targetId);
						IShowInTarget target = getShowInTarget(view);
						if (!(target != null && target.show(getContext(null)))) {
							page.getWorkbenchWindow().getShell().getDisplay().beep();
						}
					} catch (PartInitException e) {
						StatusManager.getManager().handle(new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH,
										IStatus.ERROR, "", e)); //$NON-NLS-1$
					}
				}
				private IShowInTarget getShowInTarget(IWorkbenchPart targetPart) {
					return Adapters.adapt(targetPart, IShowInTarget.class);
				}
			};
			action.setId(targetId);
			return new ActionContributionItem(action);
		}
	}

	private static final int OPEN_WITH_ID = IDialogConstants.CLIENT_ID + 1;
	private static final int SHOW_IN_ID = IDialogConstants.CLIENT_ID + 2;

	private Button showInButton;
	private Button openWithButton;

	/**
	 * Creates a new instance of the class.
	 *
	 * @param parentShell
	 *            the parent shell
	 * @param container
	 *            the container
	 * @param typesMask
	 *            the types mask
	 */
	public OpenResourceDialog(Shell parentShell, IContainer container,
			int typesMask) {
		super(parentShell, true, container, typesMask);
		setTitle(IDEWorkbenchMessages.OpenResourceDialog_title);
	}

	@Override
	protected void fillContextMenu(IMenuManager menuManager) {
		super.fillContextMenu(menuManager);

		final IStructuredSelection selectedItems = getSelectedItems();
		if (selectedItems.isEmpty()) {
			return;
		}

		IWorkbenchPage activePage = getActivePage();
		if (activePage == null) {
			return;
		}

		menuManager.add(new Separator());

		// Add 'Open' menu item
		OpenFileAction openFileAction = new OpenFileAction(activePage) {
			@Override
			public void run() {
				okPressed();
			}
		};
		openFileAction.selectionChanged(selectedItems);
		if (openFileAction.isEnabled()) {
			menuManager.add(openFileAction);

			IAdaptable selectedAdaptable = getSelectedAdaptable();
			if (selectedAdaptable != null) {

				// Add 'Open With' sub-menu
				MenuManager subMenu = new MenuManager(IDEWorkbenchMessages.OpenResourceDialog_openWithMenu_label);
				OpenWithMenu openWithMenu = new ResourceOpenWithMenu(activePage, selectedAdaptable);
				subMenu.add(openWithMenu);
				menuManager.add(subMenu);
			}
		}


		// Add 'Show In' sub-menu
		MenuManager showInMenuManager = new MenuManager(IDEWorkbenchMessages.OpenResourceDialog_showInMenu_label);
		ShowInMenu showInMenu = new ResourceShowInMenu(selectedItems, activePage.getWorkbenchWindow());
		showInMenuManager.add(showInMenu);
		menuManager.add(showInMenuManager);
	}

	@Override
	protected void createButtonsForButtonBar(final Composite parent) {
		GridLayout parentLayout = (GridLayout)parent.getLayout();
		parentLayout.makeColumnsEqualWidth = false;

		showInButton = createDropdownButton(parent, SHOW_IN_ID, IDEWorkbenchMessages.OpenResourceDialog_showInButton_text,
				new MouseAdapter() {
					@Override
					public void mouseDown(MouseEvent e) {
						showShowInMenu();
					}
				});
		setButtonLayoutData(showInButton);

		openWithButton = createDropdownButton(parent, OPEN_WITH_ID, IDEWorkbenchMessages.OpenResourceDialog_openWithButton_text,
				new MouseAdapter() {
					@Override
					public void mouseDown(MouseEvent e) {
						showOpenWithMenu();
					}
				});
		setButtonLayoutData(openWithButton);

		GridData showInLayoutData = (GridData) showInButton.getLayoutData();
		GridData openWithLayoutData = (GridData) openWithButton.getLayoutData();
		int buttonWidth = Math.max(showInLayoutData.widthHint, openWithLayoutData.widthHint);
		showInLayoutData.widthHint = buttonWidth;
		openWithLayoutData.widthHint = buttonWidth;

		new Label(parent, SWT.NONE).setLayoutData(new GridData(5, 0));
		parentLayout.numColumns++;

		Button okButton = createButton(parent, IDialogConstants.OK_ID, IDEWorkbenchMessages.OpenResourceDialog_openButton_text, true);
		Button cancelButton = createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);

		GridData cancelLayoutData = (GridData) cancelButton.getLayoutData();
		GridData okLayoutData = (GridData) okButton.getLayoutData();
		buttonWidth = Math.max(cancelLayoutData.widthHint, okLayoutData.widthHint);
		cancelLayoutData.widthHint = buttonWidth;
		okLayoutData.widthHint = buttonWidth;
	}

	private Button createDropdownButton(final Composite parent, int id, String label, MouseListener mouseListener) {
		char textEmbedding = parent.getOrientation() == SWT.LEFT_TO_RIGHT ? '\u202a' : '\u202b';
		Button button = createButton(parent, id, textEmbedding + label + '\u202c', false);
		if (Util.isMac()) {
			// Button#setOrientation(int) is a no-op on the Mac. Use a Unicode BLACK DOWN-POINTING SMALL TRIANGLE.
			button.setText(button.getText() + " \u25BE"); //$NON-NLS-1$
		} else {
			int dropDownOrientation = parent.getOrientation() == SWT.LEFT_TO_RIGHT ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
			button.setOrientation(dropDownOrientation);
			button.setImage(WorkbenchImages.getImage(IWorkbenchGraphicConstants.IMG_LCL_BUTTON_MENU));
			button.addMouseListener(mouseListener);
		}
		return button;
	}

	@Override
	protected void buttonPressed(int buttonId) {
		switch (buttonId) {
		case OPEN_WITH_ID:
			showOpenWithMenu();
			break;
		case SHOW_IN_ID:
			showShowInMenu();
			break;
		default:
			super.buttonPressed(buttonId);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void updateButtonsEnableState(IStatus status) {
		Button okButton = getOkButton();
		if (isButtonReady(okButton) && isButtonReady(showInButton) && isButtonReady(openWithButton)) {
			if (status.matches(IStatus.ERROR) || getSelectedItems().size() == 0) {
				okButton.setEnabled(false);
				openWithButton.setEnabled(false);
				showInButton.setEnabled(false);
			} else if (getSelectedItems().toList().stream().allMatch(IFile.class::isInstance)) {
				// selection contains only IFile
				okButton.setEnabled(true);
				openWithButton.setEnabled(getSelectedItems().size() == 1);
				showInButton.setEnabled(true);
			} else {
				okButton.setEnabled(false);
				openWithButton.setEnabled(false);
				showInButton.setEnabled(true);
			}
		}
	}

	private boolean isButtonReady(Button button) {
		return button != null && !button.isDisposed();
	}

	private IAdaptable getSelectedAdaptable() {
		IStructuredSelection s = getSelectedItems();
		if (s.size() != 1) {
			return null;
		}
		Object selectedElement = s.getFirstElement();
		if (selectedElement instanceof IAdaptable) {
			return (IAdaptable) selectedElement;
		}
		return null;
	}

	private IWorkbenchPage getActivePage() {
		IWorkbenchWindow activeWorkbenchWindow= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if (activeWorkbenchWindow == null) {
			return null;
		}
		return activeWorkbenchWindow.getActivePage();
	}

	private void showOpenWithMenu() {
		IWorkbenchPage activePage = getActivePage();
		if (activePage == null) {
			return;
		}
		IAdaptable selectedAdaptable = getSelectedAdaptable();
		if (selectedAdaptable == null) {
			return;
		}

		ResourceOpenWithMenu openWithMenu = new ResourceOpenWithMenu(activePage, selectedAdaptable);
		showMenu(openWithButton, openWithMenu);
	}

	private void showShowInMenu() {
		IWorkbenchPage activePage = getActivePage();
		if (activePage == null) {
			return;
		}
		IStructuredSelection selectedItems = getSelectedItems();
		if (selectedItems.isEmpty()) {
			return;
		}

		ShowInMenu showInMenu = new ResourceShowInMenu(selectedItems, activePage.getWorkbenchWindow());
		showMenu(showInButton, showInMenu);
	}

	private void showMenu(Button button, IContributionItem menuContribution) {
		Menu menu = new Menu(button);
		Point p = button.getLocation();
		p.y = p.y + button.getSize().y;
		p = button.getParent().toDisplay(p);

		menu.setLocation(p);
		menuContribution.fill(menu, 0);
		menu.setVisible(true);
	}
}
