/*
 * 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 {

    /**
     * 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;
    }
}
