/*
 * 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.mwt.Widget;
import ej.mwt.style.Style;
import ej.mwt.util.OutlineHelper;
import ej.mwt.util.Rectangle;
import ej.mwt.util.Size;

/**
 * Dimension with fixed width and/or height.
 * <p>
 * The width or height are stored as a <code>char</code> for heap optimization and therefore cannot exceed
 * <code>65535</code>.
 */
public class FixedDimension implements Dimension {

    /**
     * Creates a fixed dimension with constraints.
     * <p>
     * {@link Widget#NO_CONSTRAINT} can be used to relax constraint on one dimension (width or height).
     * <p>
     * The given width and height are clamped between <code>0</code> and <code>Character.MAX_VALUE</code>.
     *
     * @param width
     *            the width.
     * @param height
     *            the height.
     */
    public FixedDimension(int width, int height) {
        this.width = (char) XMath.limit(width, 0, Character.MAX_VALUE);
        this.height = (char) XMath.limit(height, 0, Character.MAX_VALUE);
    }

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

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

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

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

    @Override
    public void layOut(Widget widget, Rectangle bounds) {
        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.width != Widget.NO_CONSTRAINT, this.height != Widget.NO_CONSTRAINT, this.width, this.height);
        bounds.addOutline(leftOutlines, topOutlines, rightOutlines, bottomOutlines);
    }

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

    @Override
    public int hashCode() {
        return 3 * this.width + 5 * this.height;
    }
}
