/*
 * Copyright 2020-2024 MicroEJ Corp. All rights reserved.
 * This library is provided in source code for use, modification and test, subject to license terms.
 * Any modification of the source code will break MicroEJ Corp. warranties on the whole library.
 */
package ej.mwt.event;

import ej.annotation.Nullable;
import ej.basictool.ThreadUtils;
import ej.mwt.Desktop;
import ej.mwt.Widget;

/**
 * Dispatches the events received on a desktop to its children.
 */
public abstract class EventDispatcher {

	private final Desktop desktop;
	private boolean isInitialized;

	/**
	 * Creates an event dispatcher.
	 *
	 * @param desktop
	 *            the desktop to dispatch in.
	 */
	protected EventDispatcher(Desktop desktop) {
		this.desktop = desktop;
	}

	/**
	 * Returns the desktop managed by this dispatcher.
	 *
	 * @return the desktop managed by this dispatcher.
	 */
	public Desktop getDesktop() {
		return this.desktop;
	}

	/**
	 * Dispatches an event.
	 *
	 * @param event
	 *            the event to dispatch.
	 * @return <code>true</code> if the event dispatcher has dealt with the event, <code>false</code> otherwise.
	 */
	public abstract boolean dispatchEvent(int event);

	/**
	 * Initializes this event dispatcher.
	 * <p>
	 * For example, this method can be used to create an event generator.
	 */
	public void initialize() {
		this.isInitialized = true;
	}

	/**
	 * Disposes this event dispatcher.
	 * <p>
	 * After this call, all that has been allocated or started in the {@link #initialize()} method must be disposed or
	 * stopped.
	 */
	public void dispose() {
		this.isInitialized = false;
	}

	/**
	 * Gets whether this event dispatcher has been initialized.
	 * <p>
	 * It is set as not initialized when disposed.
	 * 
	 * @return <code>true</code> if this event dispatcher has been initialized, <code>false</code> otherwise
	 */
	public boolean isInitialized() {
		return this.isInitialized;
	}

	/**
	 * Sends the given event to the given widget.
	 *
	 * @param widget
	 *            the widget to which the event should be sent.
	 * @param event
	 *            the event to send.
	 * @return <code>true</code> if the widget has consumed the event, <code>false</code> otherwise.
	 */
	protected boolean sendEventToWidget(Widget widget, int event) {
		try {
			return widget.handleEvent(event);
		} catch (Exception e) {
			ThreadUtils.handleUncaughtException(e);
			return false;
		}
	}

	/**
	 * Sends the given event to the given widget and to each of its ancestors.
	 * <p>
	 * If the event is consumed by one of the widgets, it is not sent to its ancestors.
	 *
	 * @param hierarchyLeaf
	 *            the leaf of the hierarchy to which the event should be sent.
	 * @param event
	 *            the event to send.
	 * @return the widget which has consumed the event, or <code>null</code> if the event has not been consumed.
	 */
	@Nullable
	protected Widget sendEventToWidgetHierarchy(Widget hierarchyLeaf, int event) {
		Widget widget = hierarchyLeaf;
		do {
			// send event and return widget if event is consumed
			if (sendEventToWidget(widget, event)) {
				return widget;
			}

			// go to next ancestor
			widget = widget.getParent();
			// stop loop if the event dispatcher has been disposed during an event handling
		} while (widget != null && this.isInitialized);
		return null;
	}
}
