/*
 * Copyright 2009-2025 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;

import ej.annotation.Nullable;
import ej.basictool.ArrayTools;
import ej.basictool.BitFieldHelper;
import ej.basictool.ThreadUtils;
import ej.bon.Constants;
import ej.microui.MicroUI;
import ej.microui.display.Font;
import ej.microui.display.GraphicsContext;
import ej.microui.display.Painter;
import ej.mwt.style.DefaultStyle;
import ej.mwt.style.Style;
import ej.mwt.style.background.Background;
import ej.mwt.style.dimension.Dimension;
import ej.mwt.style.outline.Outline;
import ej.mwt.util.OutlineHelper;
import ej.mwt.util.Rectangle;
import ej.mwt.util.Size;
import ej.trace.Tracer;

/**
 * Widget is the superclass of all the user interface objects.
 * <p>
 * There are a number of important concepts involving widgets:
 * <ul>
 * <li>Lay out.
 * <p>
 * Lay out is the process of positioning and setting the size of the widgets on a desktop.
 * <p>
 * This is performed by evaluating the optimal size of each widget and container in the hierarchy then, considering the
 * layout of the containers and the optimal size of their children, setting the position and actual size of all widgets.
 * <p>
 * The optimal size is the minimal size that allow to show correctly the content of a widget. For instance the optimal
 * size of a label that displays a string will be the size of the string with the font defined in the style.
 * <p>
 * The optimal size is computed by {@link #computeContentOptimalSize(Size)} method. On this size are applied the boxes
 * and dimension defined in the style, and the size of the widget is set. This size is then used by the parent container
 * to lay out (set position and size) the widget along with its siblings.
 * <p>
 * Whenever the state of a widget changes in a way that may affect its optimal size and the lay out of the desktop of
 * which it is a part then the hierarchy of the widget must be ask to perform a new lay out. This can be achieved by
 * invoking {@link #requestLayOut()} on one of the parents of the widget or the desktop depending on the lay out
 * modification.
 * <p>
 * An application will normally invoke {@link #requestLayOut()} after making a set of changes to widgets.
 * <li>Rendering.
 * <p>
 * Any widget can be asked to render itself by invoking {@link #requestRender()}. If a widget has children it will ask
 * them to render. If the widget is transparent it will cause the relevant area of its parent to be rendered. Note that
 * a render request does not trigger a new lay out, and the scope of the rendering that results from a call to
 * {@link #requestRender()} will never exceed the widget itself, its children (recursively), and, if it is transparent,
 * its parent (recursively if the parent is also transparent).</li>
 * </ul>
 * <p>
 * For heap optimization the position and size are stored as a <code>short</code> and therefore are limited between
 * <code>-32768</code> and <code>32767</code>.
 */
public abstract class Widget {

	/**
	 * A width or height hint equal to <code>NO_CONSTRAINT</code> means that there is no constraint on this dimension.
	 *
	 * @see Widget#computeContentOptimalSize(Size)
	 */
	public static final int NO_CONSTRAINT = 0;

	private static final Rectangle SHARED_RECTANGLE = new Rectangle(0, 0, 0, 0);
	private static final int[] EMPTY_INT_ARRAY = new int[0];

	// Widget characteristics flags.
	private static final int ENABLED_SHIFT = 0;
	private static final int ENABLED_MASK = 0x1 << ENABLED_SHIFT;
	private static final int ATTACHED_SHIFT = 1;
	private static final int ATTACHED_MASK = 0x1 << ATTACHED_SHIFT;
	private static final int SHOWN_SHIFT = 2;
	private static final int SHOWN_MASK = 0x1 << SHOWN_SHIFT;

	private static final String SHOW_WIDGETS_BOUNDS = "ej.mwt.debug.bounds.enabled";
	private static final String SHOW_WIDGETS_BOUNDS_COLOR = "ej.mwt.debug.bounds.color";

	private static final Style LAZY_DEFAULT_STYLE = new Style() {

		@Override
		public Dimension getDimension() {
			return DefaultStyle.DIMENSION;
		}

		@Override
		public int getHorizontalAlignment() {
			return DefaultStyle.HORIZONTAL_ALIGNMENT;
		}

		@Override
		public int getVerticalAlignment() {
			return DefaultStyle.VERTICAL_ALIGNMENT;
		}

		@Override
		public Outline getMargin() {
			return DefaultStyle.MARGIN;
		}

		@Override
		public Outline getBorder() {
			return DefaultStyle.BORDER;
		}

		@Override
		public Outline getPadding() {
			return DefaultStyle.PADDING;
		}

		@Override
		public Background getBackground() {
			return DefaultStyle.BACKGROUND;
		}

		@Override
		public int getColor() {
			return DefaultStyle.COLOR;
		}

		@Override
		public Font getFont() {
			return Font.getDefaultFont();
		}

		@Override
		public <T> T getExtraObject(int fieldId, Class<T> clazz, T defaultValue) {
			return defaultValue;
		}

		@Override
		public int getExtraInt(int fieldId, int defaultValue) {
			return defaultValue;
		}

		@Override
		public float getExtraFloat(int fieldId, float defaultValue) {
			return defaultValue;
		}

	};
	// Widget position and size.
	/* package */short x;
	/* package */short y;
	/* package */short width;
	/* package */short height;

	// Widget parent: either a Container or a Desktop.
	@Nullable
	/* package */Object parent;

	// Widget characteristics.
	private byte flags;

	/**
	 * The cached style.
	 */
	/* package */ Style style;
	/**
	 * The class selectors.
	 */
	private int[] classSelectors;

	/**
	 * Creates a widget.
	 * <p>
	 * Once created, the widget is disabled. It may be enabled later by calling {@link #setEnabled(boolean)}. Enabled
	 * widgets can handle events by overriding {@link #handleEvent(int)}.
	 */
	protected Widget() {
		this(false);
	}

	/**
	 * Creates a widget specifying if its enabled or not.
	 * <p>
	 * Enabled widgets can handle events by overriding {@link #handleEvent(int)}.
	 *
	 * @param enabled
	 *            <code>true</code> if this widget is to be enabled, <code>false</code> otherwise.
	 */
	protected Widget(boolean enabled) {
		// Detached and hidden by default.
		this.flags = (byte) BitFieldHelper.setBooleanProperty(0, enabled, ENABLED_MASK);
		this.classSelectors = EMPTY_INT_ARRAY;
		this.style = LAZY_DEFAULT_STYLE;
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.createNewWidget(this);
		}
	}

	/**
	 * Gets the x coordinate of this widget, relative to its parent.
	 *
	 * @return the x coordinate.
	 */
	public int getX() {
		return this.x;
	}

	/**
	 * Gets the y coordinate of this widget, relative to its parent.
	 *
	 * @return the y coordinate.
	 */
	public int getY() {
		return this.y;
	}

	/**
	 * Gets the width of this widget.
	 *
	 * @return the width.
	 */
	public int getWidth() {
		return this.width;
	}

	/**
	 * Gets the height of this widget.
	 *
	 * @return the height.
	 */
	public int getHeight() {
		return this.height;
	}

	/**
	 * Sets the position of this widget.
	 *
	 * @param x
	 *            the x coordinate.
	 * @param y
	 *            the y coordinate.
	 */
	public void setPosition(int x, int y) {
		this.x = (short) x;
		this.y = (short) y;
	}

	/**
	 * Sets the size of this widget.
	 *
	 * @param width
	 *            the width.
	 * @param height
	 *            the height.
	 */
	private void setSize(int width, int height) {
		this.width = (short) Math.max(0, width);
		this.height = (short) Math.max(0, height);
	}

	/**
	 * Gets the content bounds of this widget (the bounds minus the outlines).
	 *
	 * @return the content bounds of this widget.
	 */
	public Rectangle getContentBounds() {
		Style style = this.style;
		Rectangle contentBounds = new Rectangle(0, 0, this.width, this.height);
		OutlineHelper.applyOutlines(contentBounds, style);
		return contentBounds;
	}

	/**
	 * Lays out this widget.
	 * <p>
	 * The dimension in the style is applied to modify the given bounds before applying to the actual bounds.
	 *
	 * @param x
	 *            the x coordinate.
	 * @param y
	 *            the y coordinate.
	 * @param width
	 *            the width.
	 * @param height
	 *            the height.
	 */
	/* package */ void layOut(int x, int y, int width, int height) {
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEvent(Trace.LAYOUT_EVENT, Trace.getWidgetId(this), x, y, width, height);
		}
		Style style = this.style;

		// delegate layout to dimension
		Dimension dimension = style.getDimension();
		Rectangle bounds = getSharedRectangle(x, y, width, height);
		dimension.layOut(this, bounds);
		setPosition(bounds.getX(), bounds.getY());
		setSize(bounds.getWidth(), bounds.getHeight());
		onLaidOut();
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			if (!(this instanceof Container)) { // NOSONAR do not merge ifs to let the constant optimisation.
				Trace.TRACER.recordEventEnd(Trace.LAYOUT_EVENT, Trace.getWidgetId(this));
			}
		}
	}

	/**
	 * This method is called as soon as the widget bounds are set.
	 */
	protected void onLaidOut() {
		// Do nothing by default.
	}

	/**
	 * This method is called as soon as:
	 * <ul>
	 * <li>the widget is attached to a desktop that is attached,</li>
	 * <li>the desktop of the widget is attached.</li>
	 * </ul>
	 * <p>
	 * After this call, the widget is ready to be rendered.
	 * <p>
	 * For example, the widget can allocate some resources useful to render it.
	 *
	 * @see Desktop#setAttached()
	 */
	protected void onAttached() {
		// Do nothing by default.
	}

	/**
	 * This method is called as soon as:
	 * <ul>
	 * <li>the widget is detached from a desktop that is attached,</li>
	 * <li>the desktop of the widget is detached.</li>
	 * </ul>
	 * <p>
	 * After this call, the resources allocated to render the widget must be disposed.
	 *
	 * @see Desktop#setDetached()
	 */
	protected void onDetached() {
		// Do nothing by default.
	}

	/**
	 * Sets the given container or desktop as parent of this widget.
	 * <p>
	 * Does not check that the parent field is set or not.
	 */
	/* package */void setParent(Object parent, boolean attach) {
		this.parent = parent;

		if (attach) {
			setAttached();
			if (this.style == LAZY_DEFAULT_STYLE) {
				updateStyle();
			}
		}
	}

	/**
	 * Resets widget's parent.
	 */
	/* package */void resetParent() {
		setDetached();
		this.parent = null;
	}

	/**
	 * Gets whether this widget is attached or not.
	 * <p>
	 * A widget is considered as attached if it belongs to the hierarchy of an attached desktop.
	 *
	 * @return <code>true</code> if this widget is attached, <code>false</code> otherwise.
	 */
	public boolean isAttached() {
		return BitFieldHelper.getBooleanProperty(this.flags, ATTACHED_MASK);
	}

	/**
	 * @throws IllegalArgumentException
	 *             if the widget is being attached whereas it is already attached.
	 */
	/* package */void setAttached() {
		if (isAttached()) {
			throw new IllegalArgumentException();
		} else {
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.ON_ATTACHED_EVENT, Trace.getWidgetId(this));
			}
			try {
				onAttached();
			} catch (Exception e) {
				ThreadUtils.handleUncaughtException(e);
			}
			this.flags = (byte) BitFieldHelper.setBooleanProperty(this.flags, ATTACHED_MASK);
		}
	}

	/* package */void setDetached() {
		if (isAttached()) {
			setHidden();
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.ON_DETACHED_EVENT, Trace.getWidgetId(this));
			}
			try {
				onDetached();
			} catch (Exception e) {
				ThreadUtils.handleUncaughtException(e);
			}
			this.flags = (byte) BitFieldHelper.unsetBooleanProperty(this.flags, ATTACHED_MASK);
		}
	}

	/**
	 * This method is called as soon as the widget is visible on the display.
	 * <p>
	 * For example, this method can be used to start a task that refreshes the widget periodically.
	 */
	protected void onShown() {
		// Do nothing by default.
	}

	/**
	 * This method is called as soon as the widget is no more visible on the display.
	 * <p>
	 * After this call, all that has been allocated or started in {@link #onShown()} must be disposed or stopped.
	 */
	protected void onHidden() {
		// Do nothing by default.
	}

	/**
	 * Gets whether this widget is shown or not.
	 * <p>
	 * This information is set by the parent of the widget and used to know if the widget can be drawn.
	 *
	 * @return <code>true</code> if this widget is shown, <code>false</code> otherwise.
	 * @see Container#setShownChildren()
	 */
	public boolean isShown() {
		return BitFieldHelper.getBooleanProperty(this.flags, SHOWN_MASK);
	}

	/* package */ void setShown() {
		if (!isShown()) {
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.ON_SHOWN_EVENT, Trace.getWidgetId(this));
			}
			try {
				onShown();
			} catch (Exception e) {
				ThreadUtils.handleUncaughtException(e);
			}
			this.flags = (byte) BitFieldHelper.setBooleanProperty(this.flags, SHOWN_MASK);
		}
	}

	/* package */ void setHidden() {
		if (isShown()) {
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.ON_HIDDEN_EVENT, Trace.getWidgetId(this));
			}
			try {
				onHidden();
			} catch (Exception e) {
				ThreadUtils.handleUncaughtException(e);
			}
			this.flags = (byte) BitFieldHelper.unsetBooleanProperty(this.flags, SHOWN_MASK);
		}
	}

	/**
	 * Gets the absolute x coordinate of the widget. That is, the x coordinate relative to the origin of the display.
	 *
	 * @return the absolute x coordinate of the widget.
	 */
	public int getAbsoluteX() {
		int absoluteX = this.x;
		Container parent = getParent();
		while (parent != null) {
			absoluteX += parent.x + parent.contentX;
			parent = parent.getParent();
		}
		return absoluteX;
	}

	/**
	 * Gets the absolute y coordinate of the widget. That is, the y coordinate relative to the origin of the display.
	 *
	 * @return the absolute y coordinate of the widget.
	 */
	public int getAbsoluteY() {
		int absoluteY = this.y;
		Container parent = getParent();
		while (parent != null) {
			absoluteY += parent.y + parent.contentY;
			parent = parent.getParent();
		}
		return absoluteY;
	}

	/**
	 * Tells whether or not this widget is transparent.
	 * <p>
	 * By default, a widget is transparent. A widget is considered as transparent if it does not draw every pixel of its
	 * bounds with maximal opacity when it is rendered. If a widget is transparent, its parent (recursively if also
	 * transparent) has to be rendered before the widget.
	 *
	 * @return <code>true</code> if this widget is transparent, <code>false</code> otherwise.
	 * @see #contains(int, int)
	 */
	public boolean isTransparent() {
		Style style = this.style;
		Rectangle bounds = getSharedRectangle(this.x, this.y, this.width, this.height);
		style.getMargin().apply(bounds);
		return style.getBackground().isTransparent(bounds.getWidth(), bounds.getHeight());
	}

	/**
	 * Returns whether or not this widget contains the given widget in its hierarchy.
	 * <p>
	 * A widget contains an other widget if one of the children of the former contains the latter or if they reference
	 * the same widget.
	 *
	 * @param widget
	 *            the widget to check.
	 * @return <code>true</code> if this widget contains the given widget, <code>false</code> otherwise.
	 */
	public boolean containsWidget(Widget widget) {
		Widget ancestor = widget;
		do {
			if (ancestor == this) {
				return true;
			}
			ancestor = ancestor.getParent();
		} while (ancestor != null);
		return false;
	}

	/**
	 * Gets whether a position (x,y) is in the widget's bounds.
	 * <p>
	 * The given position is considered here as a relative position to parent.
	 * <p>
	 * By default, the widget's bounds include the content, the padding & the border but not the margins.
	 * <p>
	 * Subclasses can override this method if the widget is not reactive to pointer events in its entire bounds. As long
	 * as this method is used to dispatch pointer events, its implementation should be as fast as possible (for example
	 * by simplifying the shape of the sensitive area).
	 * <p>
	 * If this method is overridden, it may be relevant to override the {@link #isTransparent()} method as well.
	 *
	 * @param x
	 *            x coordinate.
	 * @param y
	 *            y coordinate.
	 * @return {@code true} if the {@code (x,y)} position is in widget bounds, {@code false} otherwise.
	 * @see ej.mwt.event.PointerEventDispatcher
	 */
	public boolean contains(int x, int y) {
		Rectangle bounds = getSharedRectangle(this.x, this.y, this.width, this.height);
		Style style = this.style;
		style.getMargin().apply(bounds);

		int boundsX = bounds.getX();
		int boundsY = bounds.getY();
		return x >= boundsX && x < boundsX + bounds.getWidth() && y >= boundsY && y < boundsY + bounds.getHeight();
	}

	/**
	 * Gets the widget at the specified position.
	 * <p>
	 * If this widget does not <code>contains(x, y)</code>, <code>null</code> is returned, else this widget is returned.
	 * The position is considered here as a relative position to parent.
	 *
	 * @param x
	 *            x coordinate.
	 * @param y
	 *            y coordinate.
	 * @return this widget if it <code>contains(x, y)</code>, <code>null</code> otherwise.
	 */
	@Nullable
	public Widget getWidgetAt(int x, int y) {
		if (contains(x, y)) {
			return this;
		}
		return null;
	}

	/**
	 * Gets whether or not this widget is enabled.
	 *
	 * @return <code>true</code> if this widget is enabled, <code>false</code> otherwise.
	 */
	public boolean isEnabled() {
		return BitFieldHelper.getBooleanProperty(this.flags, ENABLED_MASK);
	}

	/**
	 * Sets this widget to be enabled or not.
	 *
	 * @param enabled
	 *            <code>true</code> if this widget is to be enabled, <code>false</code> otherwise.
	 */
	public void setEnabled(boolean enabled) {
		this.flags = (byte) BitFieldHelper.setBooleanProperty(this.flags, enabled, ENABLED_MASK);
	}

	/**
	 * Requests a lay out of all the widgets in the sub hierarchy of this widget.
	 * <p>
	 * This method returns immediately and the layout of the widget is performed asynchronously in the MicroUI thread.
	 * To execute some code just after the layout is done, this code could be wrapped in a {@link Runnable} and executed
	 * asynchronously with {@link MicroUI#callSerially(Runnable)}.
	 * <p>
	 * This method can only be called if this widget has been added to a desktop.
	 * <p>
	 * Nothing is done if the widget is not attached or if the widget has an empty size (width and height are equal to
	 * <code>0</code>).
	 * <p>
	 * If the widget was not already shown, it is shown as soon as its bounds are set.
	 * <p>
	 * The style of all the widgets in the hierarchy is set (or updated) during this phase.
	 * <p>
	 * After the widget is laid out, it will be rendered.
	 */
	public void requestLayOut() {
		if (!isAttached() || (this.width == 0 && this.height == 0)) {
			return;
		}
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEvent(Trace.REQUEST_LAYOUT_EVENT, Trace.getWidgetId(this));
		}
		MicroUI.callSerially(new Runnable() {
			@Override
			public void run() {
				if (!isAttached()) {
					return;
				}
				Widget widget = Widget.this;
				int width = widget.width;
				int height = widget.height;
				computeOptimalSize(width, height);
				layOut(widget.x, widget.y, width, height);
				setShown();
			}
		});
		// inline requestRender() without the isShown() check
		getDesktop().requestRender(this, 0, 0, this.width, this.height);
	}

	/**
	 * Computes the optimal size of this widget including the outlines defined in the style.
	 * <p>
	 * After this call the style is set and the optimal size will have been established.
	 * <p>
	 * The parameters define the maximum size available for this widget, or {@link Widget#NO_CONSTRAINT} if there is no
	 * constraint.
	 *
	 * @param availableWidth
	 *            the width available for this widget or {@link Widget#NO_CONSTRAINT}.
	 * @param availableHeight
	 *            the height available for this widget or {@link Widget#NO_CONSTRAINT}.
	 */
	/* package */void computeOptimalSize(int availableWidth, int availableHeight) {
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEvent(Trace.COMPUTE_SIZE_EVENT, Trace.getWidgetId(this), availableWidth,
					availableHeight);
		}
		Style style = this.style;

		Dimension dimension = style.getDimension();
		Size size = new Size(availableWidth, availableHeight);
		OutlineHelper.applyOutlines(size, style);

		int horizontalOutlines = availableWidth - size.getWidth();
		int verticalOutlines = availableHeight - size.getHeight();

		if (availableWidth == Widget.NO_CONSTRAINT) {
			size.setWidth(Widget.NO_CONSTRAINT);
		}
		if (availableHeight == Widget.NO_CONSTRAINT) {
			size.setHeight(Widget.NO_CONSTRAINT);
		}

		dimension.getAvailableSize(this, availableWidth, availableHeight, size);
		try {
			computeContentOptimalSize(size);
		} catch (Exception e) {
			ThreadUtils.handleUncaughtException(e);
		}
		dimension.computeOptimalSize(this, availableWidth, availableHeight, size);

		setSize(size.getWidth() + horizontalOutlines, size.getHeight() + verticalOutlines);
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEventEnd(Trace.COMPUTE_SIZE_EVENT, Trace.getWidgetId(this));
		}
	}

	/**
	 * Computes the optimal size of the widget.
	 * <p>
	 * This method does not consider the border, margin, padding and dimension specified in the style.
	 * <p>
	 * The given size is the available size for this widget in its parent. A width or a height equal to
	 * <code>Widget#NO_CONSTRAINT</code> means that there is no constraint on this dimension.
	 * <p>
	 * The given size is modified to set the optimal size.
	 *
	 * @param size
	 *            the size available for the content.
	 */
	protected abstract void computeContentOptimalSize(Size size);

	/**
	 * Requests a render of this entire widget on the display.
	 * <p>
	 * This method returns immediately and the rendering of the widget is performed asynchronously in the MicroUI
	 * thread. To execute some code just after the render is done, this code could be wrapped in a {@link Runnable} and
	 * executed asynchronously with {@link MicroUI#callSerially(Runnable)}.
	 * <p>
	 * If the widget is not shown, nothing is done.
	 *
	 * @see ej.mwt.render.RenderPolicy
	 */
	public void requestRender() {
		requestRender(0, 0, this.width, this.height);
	}

	/**
	 * Requests a render of a zone of this widget on the display.
	 * <p>
	 * This method returns immediately and the rendering of the widget is performed asynchronously in the MicroUI
	 * thread. To execute some code just after the render is done, this code could be wrapped in a {@link Runnable} and
	 * executed asynchronously with {@link MicroUI#callSerially(Runnable)}.
	 * <p>
	 * If the widget is not shown, nothing is done.
	 * <p>
	 * If the given area exceeds the bounds of the widget, only the intersection of the widget and the area will be
	 * rendered.
	 *
	 * @param x
	 *            the relative x coordinate of the area to render.
	 * @param y
	 *            the relative y coordinate of the area to render.
	 * @param width
	 *            the width of the area to render.
	 * @param height
	 *            the height of the area to render.
	 * @see ej.mwt.render.RenderPolicy
	 */
	public void requestRender(int x, int y, int width, int height) {
		if (isShown()) {
			getDesktop().requestRender(this, x, y, width, height);
		}
	}

	/**
	 * Handles the given event. Does nothing by default and returns <code>false</code> (does not consume event).
	 * <p>
	 * Called by the desktop event manager.
	 *
	 * @param event
	 *            the event to handle.
	 * @return <code>true</code> if the widget has consumed the event, <code>false</code> otherwise.
	 */
	public boolean handleEvent(int event) {
		return false;
	}

	/* package */ void paint(GraphicsContext g) {
		if (!isAttached()) {
			return;
		}
		g.translate(this.x, this.y);
		g.intersectClip(0, 0, this.width, this.height);
		if (Constants.getBoolean(SHOW_WIDGETS_BOUNDS)) {
			int clipWidth = g.getClipWidth();
			int clipHeight = g.getClipHeight();
			if (clipWidth == 0 || clipHeight == 0) {
				return; // Nothing to paint.
			}
			int translateX = g.getTranslationX();
			int translateY = g.getTranslationY();
			int clipX = g.getClipX();
			int clipY = g.getClipY();
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.RENDER_EVENT, Trace.getGraphicsContextId(g), Trace.getWidgetId(this),
						translateY + clipX, translateY + clipY, clipWidth, clipHeight);
			}

			render(g);

			g.setTranslation(translateX, translateY);
			g.setClip(clipX, clipY, clipWidth, clipHeight);
			g.setColor(Constants.getInt(SHOW_WIDGETS_BOUNDS_COLOR));
			Painter.drawRectangle(g, 0, 0, this.width, this.height);
		} else {
			if (g.getClipWidth() == 0 || g.getClipHeight() == 0) {
				return; // Nothing to paint.
			}
			if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
				Trace.TRACER.recordEvent(Trace.RENDER_EVENT, Trace.getGraphicsContextId(g), Trace.getWidgetId(this),
						g.getTranslationX() + g.getClipX(), g.getTranslationY() + g.getClipY(),
						g.getClipWidth(), g.getClipHeight());
			}
			render(g);
		}
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEventEnd(Trace.RENDER_EVENT, Trace.getWidgetId(this));
		}
	}

	/**
	 * Renders the widget on the given graphics context.
	 * <p>
	 * The given graphics context is translated to the origin of the widget and clipped to the area to draw.
	 * <p>
	 * First, the different outlines defined in the style are applied, then, the content is rendered.
	 *
	 * @param g
	 *            the graphics context to use to draw the widget.
	 * @see OutlineHelper#applyOutlinesAndBackground(GraphicsContext, Size, Style)
	 * @see #renderContent(GraphicsContext, int, int)
	 */
	public void render(GraphicsContext g) {
		Style style = this.style;
		Size contentSize = new Size(this.width, this.height);
		OutlineHelper.applyOutlinesAndBackground(g, contentSize, style);
		try {
			renderContent(g, contentSize.getWidth(), contentSize.getHeight());
		} catch (Exception e) {
			ThreadUtils.handleUncaughtException(e);
		}
	}

	/**
	 * Renders the content of the widget without the border, margin and padding specified in the style.
	 * <p>
	 * The given graphics context is translated and clipped according to the given bounds (the border, margin and
	 * padding are applied on this graphics context before).
	 *
	 * @param g
	 *            the graphics context where to render the content of the widget.
	 * @param contentWidth
	 *            the width of the content area.
	 * @param contentHeight
	 *            the height of the content area.
	 */
	protected abstract void renderContent(GraphicsContext g, int contentWidth, int contentHeight);

	/**
	 * Gets the parent of this widget or <code>null</code> if the widget is not in a hierarchy or if it is the root of
	 * its hierarchy.
	 *
	 * @return the parent of this widget or <code>null</code>.
	 */
	@Nullable
	public Container getParent() {
		Object parent = this.parent;
		if (parent instanceof Container) {
			return (Container) parent;
		} else {
			return null;
		}
	}

	/**
	 * Gets the desktop to which this widget has been added.
	 * <p>
	 * This method can only be called if this widget has been added to a desktop.
	 *
	 * @return the desktop to which this widgets has been added.
	 */
	public Desktop getDesktop() {
		Object parent = this.parent;
		assert (parent != null) : "This widget (" + printClassname(this) + ") is not attached to any desktop.";
		if (parent instanceof Desktop) {
			return (Desktop) parent;
		} else {
			return ((Container) parent).getDesktop();
		}
	}

	/* package */ static String printClassname(Widget widget) {
		return widget.getClass().getSimpleName();
	}

	@Nullable
	/* package */ Desktop getDesktopOrNull() {
		Object parent = this.parent;
		if (parent instanceof Desktop) {
			return (Desktop) parent;
		} else if (parent != null) {
			return ((Container) parent).getDesktopOrNull();
		} else {
			return null;
		}
	}

	/**
	 * Gets the current style of the widget.
	 * <p>
	 * This method should not be called before this widget is laid out.
	 *
	 * @return the current style of the widget.
	 * @see ej.mwt.stylesheet.Stylesheet#getStyle(Widget)
	 */
	public Style getStyle() {
		return this.style;
	}

	/**
	 * Sets the style of the widget.
	 *
	 * @param newStyle
	 *            the style.
	 */
	public void setStyle(Style newStyle) {
		this.style = newStyle;
	}

	/**
	 * Updates the style of this widget.
	 * <p>
	 * If the widget is not in a desktop, nothing is done.
	 */
	public void updateStyle() {
		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			Trace.TRACER.recordEvent(Trace.UPDATE_STYLE_EVENT, Trace.getWidgetId(this));
		}

		Desktop desktop = getDesktopOrNull();
		if (desktop != null) {
			Style newStyle = desktop.getStylesheet().getStyle(this);
			setStyle(newStyle);
		}

		if (Constants.getBoolean(Tracer.TRACE_ENABLED_CONSTANT_PROPERTY)) {
			if (!(this instanceof Container)) { // NOSONAR do not merge ifs to let the constant optimisation.
				Trace.TRACER.recordEventEnd(Trace.UPDATE_STYLE_EVENT, Trace.getWidgetId(this));
			}
		}

	}

	/**
	 * Gets whether or not the widget has the given class selector.
	 *
	 * @param classSelector
	 *            the class selector to check.
	 * @return <code>true</code> if the widget has the given class selector, <code>false</code> otherwise.
	 */
	public boolean hasClassSelector(int classSelector) {
		return ArrayTools.getIndex(this.classSelectors, classSelector) != -1;
	}

	/**
	 * Adds a class selector.
	 *
	 * @param classSelector
	 *            the class selector to add.
	 * @throws NullPointerException
	 *             if the given class selector is <code>null</code>.
	 */
	public void addClassSelector(int classSelector) {
		this.classSelectors = ArrayTools.add(this.classSelectors, classSelector);
	}

	/**
	 * Removes a class selector.
	 *
	 * @param classSelector
	 *            the class selector to remove.
	 */
	public void removeClassSelector(int classSelector) {
		this.classSelectors = ArrayTools.remove(this.classSelectors, classSelector);
	}

	/**
	 * Sets the class selectors.
	 * <p>
	 * If there is already some class selectors, they are removed.
	 *
	 * @param classSelectors
	 *            the class selectors list to split.
	 */
	public void setClassSelectors(int[] classSelectors) {
		this.classSelectors = classSelectors.clone();
	}

	/**
	 * Removes all the class selectors.
	 */
	public void removeAllClassSelectors() {
		this.classSelectors = EMPTY_INT_ARRAY;
	}

	/**
	 * Gets whether or not the widget is in the given state.
	 *
	 * @param state
	 *            the state to check.
	 * @return <code>true</code> if the widget is in the given state, <code>false</code> otherwise.
	 */
	public boolean isInState(int state) {
		return false;
	}

	/**
	 * Gets a unique instance of rectangle. It is shared to avoid creating new objects for small operations in the
	 * MicroUI thread.
	 *
	 * @param x
	 *            the x coordinate.
	 * @param y
	 *            the y coordinate.
	 * @param width
	 *            the width.
	 * @param height
	 *            the height.
	 * @return the shared rectangle.
	 */
	/* package */ static Rectangle getSharedRectangle(int x, int y, int width, int height) {
		SHARED_RECTANGLE.setBounds(x, y, width, height);
		return SHARED_RECTANGLE;
	}

}
