/*******************************************************************************
 * Copyright (c) 2016 vogella GmbH 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:
 * 	Simon Scholz <simon.scholz@vogella.com> - initial API and implementation;
 * 	Patrik Suzzi <psuzzi@gmail.com> - Bug 491572, 491785, 492749
 ******************************************************************************/

package org.eclipse.ui.internal.handlers;

import java.util.Arrays;
import java.util.Optional;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.core.commands.ECommandService;
import org.eclipse.e4.ui.bindings.internal.BindingTableManager;
import org.eclipse.e4.ui.bindings.internal.ContextSet;
import org.eclipse.jface.bindings.Binding;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.keys.IBindingService;

/**
 * Handler, which enables a full screen mode.
 *
 * @since 3.5
 *
 */
public class FullScreenHandler extends AbstractHandler {

	private static final String FULL_SCREEN_COMMAND_ID = "org.eclipse.ui.window.fullscreenmode"; //$NON-NLS-1$
	private static final String FULL_SCREEN_COMMAND_DO_NOT_SHOW_INFO_AGAIN_PREF_ID = "org.eclipse.ui.window.fullscreenmode.donotshowinfoagain"; //$NON-NLS-1$

	private int timeLastEvent;
	private FullScreenInfoPopup fullScreenInfoPopup;

	@Override
	public Object execute(ExecutionEvent event) {
		// 493186 skips execution of duplicated event
		if (checkDuplicatedEvent(event)) {
			return null;
		}
		IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
		Shell shell = window.getShell();
		IBindingService bindingService = window.getService(IBindingService.class);
		ECommandService commandService = window.getService(ECommandService.class);
		BindingTableManager bindingTableManager = window.getService(BindingTableManager.class);
		IContextService bindingContextService = window.getService(IContextService.class);

		boolean showInfoPopup = !WorkbenchPlugin.getDefault().getPreferenceStore()
				.getBoolean(FULL_SCREEN_COMMAND_DO_NOT_SHOW_INFO_AGAIN_PREF_ID);

		Optional<TriggerSequence> sequence = getKeybindingSequence(bindingService, commandService, bindingTableManager,
				bindingContextService, FULL_SCREEN_COMMAND_ID);

		String keybinding = sequence.map(TriggerSequence::format).orElse(""); //$NON-NLS-1$

		shell.setFullScreen(!shell.getFullScreen());

		if (shell.getFullScreen()) {
			String message = WorkbenchMessages.ToggleFullScreenMode_ActivationPopup_Description_NoKeybinding;
			if (!keybinding.isEmpty()) {
				message = NLS.bind(WorkbenchMessages.ToggleFullScreenMode_ActivationPopup_Description, keybinding);
			}
			if (showInfoPopup) {
				fullScreenInfoPopup = new FullScreenInfoPopup(shell, PopupDialog.HOVER_SHELLSTYLE, true, false, false,
						false, false, null, null, message);
				fullScreenInfoPopup.open();
			}
		} else if (fullScreenInfoPopup != null) {
			fullScreenInfoPopup.close();
		}
		shell.requestLayout();
		return Status.OK_STATUS;
	}

	/**
	 * Check if an event is duplicate, by recording and comparing the time of the
	 * trigger event. Returns true if an event is triggered twice with an event with
	 * the same time
	 */
	boolean checkDuplicatedEvent(ExecutionEvent event) {
		if (event != null && event.getTrigger() != null && event.getTrigger() instanceof Event) {
			int time = ((Event) event.getTrigger()).time;
			if (time == timeLastEvent) {
				return true;
			}
			timeLastEvent = time;
		}
		return false;
	}

	private static class FullScreenInfoPopup extends PopupDialog {

		private String message;
		private String messageDoNotShowAgain;

		public FullScreenInfoPopup(Shell parent, int shellStyle, boolean takeFocusOnOpen, boolean persistSize,
				boolean persistLocation, boolean showDialogMenu, boolean showPersistActions, String titleText,
				String infoText, String message) {
			super(parent, shellStyle, takeFocusOnOpen, persistSize, persistLocation, showDialogMenu, showPersistActions,
					titleText, infoText);
			this.message = message;
			this.messageDoNotShowAgain = WorkbenchMessages.ToggleFullScreenMode_ActivationPopup_DoNotShowAgain;
		}

		@Override
		protected Point getInitialLocation(Point initialSize) {
			if (getShell().getParent() == null) {
				return super.getInitialLocation(initialSize);
			}
			Rectangle bounds = getShell().getParent().getMonitor().getBounds();
			GC gc = new GC(getShell().getDisplay());
			int textExtendX = gc.textExtent(message).x;
			gc.dispose();

			return new Point(bounds.x + bounds.width / 2 - textExtendX / 2, bounds.y + bounds.height / 5);
		}

		@Override
		protected Control createDialogArea(Composite parent) {
			Composite composite = (Composite) super.createDialogArea(parent);

			Label label = new Label(composite, SWT.NONE);
			label.setText(message);
			GridData gd = new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
			gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING;
			gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING;
			label.setLayoutData(gd);

			Button btnDoNotShow = new Button(composite, SWT.CHECK);
			btnDoNotShow.setText(messageDoNotShowAgain);
			btnDoNotShow.setSelection(WorkbenchPlugin.getDefault().getPreferenceStore()
					.getBoolean(FULL_SCREEN_COMMAND_DO_NOT_SHOW_INFO_AGAIN_PREF_ID));
			GridData gd2 = new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
			gd2.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING;
			gd2.verticalIndent = PopupDialog.POPUP_VERTICALSPACING;
			btnDoNotShow.setLayoutData(gd2);

			composite.addDisposeListener(e -> {
				WorkbenchPlugin.getDefault().getPreferenceStore()
						.setValue(FULL_SCREEN_COMMAND_DO_NOT_SHOW_INFO_AGAIN_PREF_ID, btnDoNotShow.getSelection());
			});

			return composite;
		}

	}

	protected Optional<TriggerSequence> getKeybindingSequence(IBindingService bindingService,
			ECommandService eCommandService, BindingTableManager bindingTableManager, IContextService contextService,
			String commandId) {
		TriggerSequence triggerSequence = bindingService.getBestActiveBindingFor(commandId);
		// FIXME Bug 491701 - [KeyBinding] get best active binding is not
		// working
		if (triggerSequence == null) {
			ParameterizedCommand cmd = eCommandService.createCommand(commandId, null);
			ContextSet contextSet = bindingTableManager
					.createContextSet(Arrays.asList(contextService.getDefinedContexts()));
			Binding binding = bindingTableManager.getBestSequenceFor(contextSet, cmd);
			if (binding != null) {
				triggerSequence = binding.getTriggerSequence();
			}
		}
		return Optional.ofNullable(triggerSequence);
	}

}
