/*
 * Copyright 2016-2020 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.container;

import ej.basictool.map.PackedMap;
import ej.mwt.Container;
import ej.mwt.Widget;
import ej.mwt.util.Rectangle;
import ej.mwt.util.Size;

/**
 * Lays out any number of children freely.
 * <p>
 * Each child is laid out at the position it has been given.
 * <p>
 * In a canvas, each child will have the size it has been given, or if desired, its optimal size.
 */
public class Canvas extends Container {

	private final PackedMap<Widget, Rectangle> widgetBounds;

	/**
	 * Creates a canvas.
	 */
	public Canvas() {
		this.widgetBounds = new PackedMap<>();
	}

	/**
	 * Adds the specified widget to this canvas. The widget is laid out with the given bounds.
	 * <p>
	 * If the given width or height is {@link Widget#NO_CONSTRAINT}, the widget is fitted to its optimal width/height.
	 *
	 * @param child
	 *            the widget to add.
	 * @param x
	 *            the x coordinate of the widget.
	 * @param y
	 *            the y coordinate of the widget.
	 * @param width
	 *            the width of the widget, or {@link Widget#NO_CONSTRAINT} to use the widget optimal width.
	 * @param height
	 *            the height of the widget, or {@link Widget#NO_CONSTRAINT} to use the widget optimal height.
	 * @throws NullPointerException
	 *             if the specified widget is <code>null</code>.
	 * @throws IllegalArgumentException
	 *             if the specified widget is already in a hierarchy (already contained in a container or desktop).
	 * @see Container#addChild(Widget)
	 */
	public void addChild(Widget child, int x, int y, int width, int height) {
		super.addChild(child);
		this.widgetBounds.put(child, new Rectangle(x, y, width, height));
	}

	@Override
	public void removeChild(Widget child) {
		super.removeChild(child);
		this.widgetBounds.remove(child);
	}

	@Override
	public void removeAllChildren() {
		super.removeAllChildren();
		this.widgetBounds.clear();
	}

	@Override
	public void changeChildIndex(Widget child, int index) {
		super.changeChildIndex(child, index);
	}

	@Override
	protected void computeContentOptimalSize(Size size) {
		int widthHint = size.getWidth();
		int heightHint = size.getHeight();
		PackedMap<Widget, Rectangle> widgetBounds = this.widgetBounds;

		int width = 0;
		int height = 0;
		for (Widget child : getChildren()) {
			assert (child != null);

			// get child desired bounds
			Rectangle bounds = widgetBounds.get(child);
			int childX = bounds.getX();
			int childY = bounds.getY();
			int childWidth = bounds.getWidth();
			int childHeight = bounds.getHeight();

			// compute child optimal size
			int childWidthHint = getChildSize(childWidth, widthHint);
			int childHeightHint = getChildSize(childHeight, heightHint);
			computeChildOptimalSize(child, childWidthHint, childHeightHint);

			// update container optimal size
			childWidth = getChildSize(childWidth, child.getWidth());
			childHeight = getChildSize(childHeight, child.getHeight());
			width = Math.max(width, childX + childWidth);
			height = Math.max(height, childY + childHeight);
		}

		// set container optimal size
		size.setSize(width, height);
	}

	@Override
	protected void layOutChildren(int contentWidth, int contentHeight) {
		for (Widget child : getChildren()) {
			assert (child != null);

			// compute widget bounds
			Rectangle bounds = this.widgetBounds.get(child);
			int childWidth = getChildSize(bounds.getWidth(), child.getWidth());
			int childHeight = getChildSize(bounds.getHeight(), child.getHeight());

			// lay out child
			layOutChild(child, bounds.getX(), bounds.getY(), childWidth, childHeight);
		}
	}

	private static int getChildSize(int sizeHint, int optimalSize) {
		return (sizeHint == Widget.NO_CONSTRAINT ? optimalSize : sizeHint);
	}
}
