/*
 * Copyright 2023 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.widget.event;

import ej.microui.event.Event;
import ej.microui.event.EventHandler;
import ej.microui.event.generator.Buttons;
import ej.microui.event.generator.Pointer;
import ej.mwt.Widget;
import ej.mwt.event.PointerEventDispatcher;

/**
 * Abstract pointer event handler.
 * <p>
 * It can be used as base class for event handlers compatible with {@link PointerEventDispatcher} semantic.
 *
 * @since 3.5.0
 */
public abstract class PointerEventHandler implements EventHandler {

	private final Widget widget;

	/**
	 * Instantiates the handler for the given widget.
	 *
	 * @param widget
	 *            the attached widget.
	 */
	protected PointerEventHandler(Widget widget) {
		this.widget = widget;
	}

	@Override
	public final boolean handleEvent(int event) {
		if (Event.getType(event) == Pointer.EVENT_TYPE) {
			Pointer pointer = (Pointer) Event.getGenerator(event);
			Widget widget = this.widget;
			int pointerX = pointer.getX() - widget.getAbsoluteX();
			int pointerY = pointer.getY() - widget.getAbsoluteY();
			int action = Buttons.getAction(event);

			if (action == Buttons.PRESSED) {
				return onPressed(pointerX, pointerY);
			} else if (action == Pointer.DRAGGED) {
				return onDragged(pointerX, pointerY);
			} else if (action == Buttons.RELEASED) {
				return onReleased(pointerX, pointerY);
			}
		} else if (PointerEventDispatcher.isExited(event)) {
			onExited();
		}

		return false;
	}

	/**
	 * Returns {@code false} by default.
	 * <p>
	 * Override this method to react on {@link Buttons#PRESSED PRESSED} events.<br>
	 * Returns {@code true} to get event focus. Other widgets will no longer receive pointer events until next
	 * {@link Buttons#RELEASED RELEASED}.
	 *
	 * @param pointerX
	 *            the {@link Pointer} event X, relative to the attached widget.
	 * @param pointerY
	 *            the {@link Pointer} event Y, relative to the attached widget.
	 * @return {@code true} if the event has been processed, {@code false} if it should be dispatched to other handlers.
	 */
	protected boolean onPressed(int pointerX, int pointerY) {
		return false;
	}

	/**
	 * Returns {@code false} by default.
	 * <p>
	 * Override this method to react on {@link Pointer#DRAGGED DRAGGED} events.<br>
	 * Returns {@code true} to get event focus. Other widgets will no longer receive pointer events until next
	 * {@link Buttons#RELEASED RELEASED}.
	 *
	 * @param pointerX
	 *            the {@link Pointer} event X, relative to the attached widget.
	 * @param pointerY
	 *            the {@link Pointer} event Y, relative to the attached widget.
	 * @return {@code true} if the event has been processed, {@code false} if it should be dispatched to other handlers.
	 */
	protected boolean onDragged(int pointerX, int pointerY) {
		return false;
	}

	/**
	 * Returns {@code false} by default.
	 * <p>
	 * Override this method to react on {@link Buttons#RELEASED RELEASED} events.<br>
	 * Returns {@code true} to prevent other widgets to also handle this event.
	 *
	 * @param pointerX
	 *            the {@link Pointer} event X, relative to the attached widget.
	 * @param pointerY
	 *            the {@link Pointer} event Y, relative to the attached widget.
	 * @return {@code true} if the event has been processed, {@code false} if it should be dispatched to other handlers.
	 */
	protected boolean onReleased(int pointerX, int pointerY) {
		return false;
	}

	/**
	 * Does nothing by default.
	 * <p>
	 * Override this method to react on focus loss events.
	 */
	protected void onExited() {
		// does nothing
	}

}
