/*
 * Copyright 2015-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.style.dimension;

import ej.annotation.Nullable;
import ej.bon.XMath;
import ej.microui.display.Display;
import ej.mwt.Container;
import ej.mwt.Widget;
import ej.mwt.style.Style;
import ej.mwt.util.OutlineHelper;
import ej.mwt.util.Rectangle;
import ej.mwt.util.Size;

/**
 * Dimension with constraint relative to the parent.
 */
public class RelativeDimension implements Dimension {

	private final float widthRatio;
	private final float heightRatio;

	/**
	 * Creates a relative dimension with constraints.
	 * <p>
	 * {@link Widget#NO_CONSTRAINT} can be used to relax constraint on one dimension (width or height).
	 * <p>
	 * The given width ratio and height ratio are clamped between <code>0.0f</code> and <code>1.0f</code>.
	 *
	 * @param widthRatio
	 *            the width ratio.
	 * @param heightRatio
	 *            the height ratio.
	 */
	public RelativeDimension(float widthRatio, float heightRatio) {
		this.widthRatio = XMath.limit(widthRatio, 0.0f, 1.0f);
		this.heightRatio = XMath.limit(heightRatio, 0.0f, 1.0f);
	}

	/**
	 * Returns the width ratio.
	 *
	 * @return the width ratio, or {@link Widget#NO_CONSTRAINT} if there is no width constraint.
	 */
	public float getWidthRatio() {
		return this.widthRatio;
	}

	/**
	 * Returns the height ratio.
	 *
	 * @return the height ratio, or {@link Widget#NO_CONSTRAINT} if there is no height constraint.
	 */
	public float getHeightRatio() {
		return this.heightRatio;
	}

	@Override
	public void getAvailableSize(Widget widget, int availableWidth, int availableHeight, Size availableSize) {
		if (this.widthRatio != Widget.NO_CONSTRAINT) {
			availableSize.setWidth((int) (this.widthRatio * availableWidth));
		}
		if (this.heightRatio != Widget.NO_CONSTRAINT) {
			availableSize.setHeight((int) (this.heightRatio * availableHeight));
		}
	}

	@Override
	public void computeOptimalSize(Widget widget, int availableWidth, int availableHeight, Size optimalSize) {
		if (this.widthRatio != Widget.NO_CONSTRAINT) {
			optimalSize.setWidth((int) (this.widthRatio * availableWidth));
		}
		if (this.heightRatio != Widget.NO_CONSTRAINT) {
			optimalSize.setHeight((int) (this.heightRatio * availableHeight));
		}
	}

	@Override
	public void layOut(Widget widget, Rectangle bounds) {
		int parentWidth;
		int parentHeight;
		Container parent = widget.getParent();
		if (parent == null) {
			Display display = Display.getDisplay();
			parentWidth = display.getWidth();
			parentHeight = display.getHeight();
		} else {
			parentWidth = parent.getWidth();
			parentHeight = parent.getHeight();
		}

		Style style = widget.getStyle();

		int leftOutlines = -bounds.getX();
		int topOutlines = -bounds.getY();
		int rightOutlines = bounds.getWidth();
		int bottomOutlines = bounds.getHeight();

		OutlineHelper.applyOutlines(bounds, style);

		leftOutlines += bounds.getX();
		topOutlines += bounds.getY();
		rightOutlines -= bounds.getWidth() + leftOutlines;
		bottomOutlines -= bounds.getHeight() + topOutlines;

		DimensionHelper.layOut(widget, bounds, this.widthRatio != Widget.NO_CONSTRAINT,
				this.heightRatio != Widget.NO_CONSTRAINT, ((int) (this.widthRatio * parentWidth)),
				((int) (this.heightRatio * parentHeight)));

		bounds.addOutline(leftOutlines, topOutlines, rightOutlines, bottomOutlines);
	}

	@Override
	public boolean equals(@Nullable Object obj) {
		if (obj != null && getClass() == obj.getClass()) {
			RelativeDimension dimension = (RelativeDimension) obj;
			return this.widthRatio == dimension.widthRatio && this.heightRatio == dimension.heightRatio;
		}
		return false;
	}

	@Override
	public int hashCode() {
		return (int) (this.widthRatio * 300 + this.heightRatio * 500);
	}

}
