/*******************************************************************************
 * Copyright (c) 2013, 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.e4.ui.bindings.keys;

import java.util.List;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Widget;

/**
 * A listener that makes sure that global key bindings are processed if no other listeners do any
 * useful work.
 *
 * @since 3.0
 */
final class OutOfOrderListener implements Listener {
	/**
	 * The time at which this listener was last registered to an event. This is the
	 * <code>event.time</code> value.
	 *
	 * @since 3.1
	 */
	private int active = Integer.MIN_VALUE;

	/**
	 * The keyboard interface to which the event should be passed if it is not eaten.
	 */
	private final KeyBindingDispatcher keyboard;

	/**
	 * Constructs a new instance of <code>OutOfOrderListener</code> with a reference to the keyboard
	 * interface which should be allowed to process uneaten events.
	 *
	 * @param workbenchKeyboard
	 *            The keyboard interface for the workbench capable of processing key bindings; must
	 *            not be <code>null</code>.
	 */
	public OutOfOrderListener(KeyBindingDispatcher workbenchKeyboard) {
		keyboard = workbenchKeyboard;
	}

	/**
	 * Handles the key down event on a widget by passing uneaten events to the key binding
	 * architecture. This is used to allow special keys to reach the widget first -- before being
	 * processed by the key binding architecture.
	 *
	 * @param event
	 *            The event to process; must not be <code>null</code>
	 */
	@Override
	public void handleEvent(Event event) {
		// Always remove myself as a listener.
		final Widget widget = event.widget;
		if ((widget != null) && (!widget.isDisposed())) {
			widget.removeListener(event.type, this);
		}

		/*
		 * If the event is still up for grabs, then re-route through the global key filter.
		 */
		if (event.doit) {
			List<KeyStroke> keyStrokes = KeyBindingDispatcher.generatePossibleKeyStrokes(event);
			keyboard.processKeyEvent(keyStrokes, event);
		}
	}

	/**
	 * Returns whether this listener has been hooked by this event already.
	 *
	 * @param timeRegistered
	 *            The <code>event.time</code> for the current event.
	 * @return <code>true</code> if this listener is registered for a different event;
	 *         <code>false</code> otherwise.
	 *
	 * @since 3.1
	 */
	final boolean isActive(final int timeRegistered) {
		return (active == timeRegistered);
	}

	/**
	 * Sets the event time at which this listener was last registered with a widget.
	 *
	 * @param timeRegistered
	 *            The time at which this listener was last registered with a widget.
	 *
	 * @since 3.1
	 */
	final void setActive(final int timeRegistered) {
		active = timeRegistered;
	}
}
