/*
 * Copyright 2015-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.mwt.util;

/**
 * Provides constants and utility methods in order to position a graphical object considering its alignment within an
 * area or with an anchor point.
 */
public class Alignment {

	/**
	 * Constant for positioning on the left.
	 */
	public static final int LEFT = 0;

	/**
	 * Constant for centering horizontally.
	 */
	public static final int HCENTER = 1;

	/**
	 * Constant for positioning on the right.
	 */
	public static final int RIGHT = 2;

	/**
	 * Constant for positioning on the top.
	 */
	public static final int TOP = 3;

	/**
	 * Constant for centering vertically.
	 */
	public static final int VCENTER = 4;

	/**
	 * Constant for positioning on the bottom.
	 */
	public static final int BOTTOM = 5;

	// Prevents initialization.
	private Alignment() {
	}

	/**
	 * Validates that the given value represents a valid horizontal alignment.
	 * <p>
	 * This method is equivalent to {@link #checkHorizontalAlignment(int)} except that it throws an exception rather
	 * than return a boolean.
	 *
	 * @param horizontalAlignment
	 *            the horizontal alignment to check.
	 * @throws IllegalArgumentException
	 *             if the given horizontal alignment is not valid.
	 */
	public static void validateHorizontalAlignment(int horizontalAlignment) {
		if (!checkHorizontalAlignment(horizontalAlignment)) {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * Validates that the given value represents a valid vertical alignment.
	 * <p>
	 * This method is equivalent to {@link #checkVerticalAlignment(int)} except that it throws an exception rather than
	 * return a boolean.
	 *
	 * @param verticalAlignment
	 *            the vertical alignment to check.
	 * @throws IllegalArgumentException
	 *             if the given vertical alignment is not valid.
	 */
	public static void validateVerticalAlignment(int verticalAlignment) {
		if (!checkVerticalAlignment(verticalAlignment)) {
			throw new IllegalArgumentException();
		}
	}

	/**
	 * Checks whether the given value represents a valid horizontal alignment.
	 * <p>
	 * A value represents a valid horizontal alignment if it is equal to either {@link #LEFT}, {@link #HCENTER} or
	 * {@link #RIGHT}.
	 *
	 * @param horizontalAlignment
	 *            the horizontal alignment to check.
	 * @return <code>true</code> if the given horizontal alignment is valid, <code>false</code> otherwise.
	 */
	public static boolean checkHorizontalAlignment(int horizontalAlignment) {
		switch (horizontalAlignment) {
		case RIGHT:
		case HCENTER:
		case LEFT:
			return true;
		default:
			return false;
		}
	}

	/**
	 * Checks whether the given value represents a valid vertical alignment.
	 * <p>
	 * A value represents a valid vertical alignment if it is equal to either {@link #TOP}, {@link #VCENTER} or
	 * {@link #BOTTOM}.
	 *
	 * @param verticalAlignment
	 *            the vertical alignment to check.
	 * @return <code>true</code> if the given vertical alignment is valid, <code>false</code> otherwise.
	 */
	public static boolean checkVerticalAlignment(int verticalAlignment) {
		switch (verticalAlignment) {
		case BOTTOM:
		case VCENTER:
		case TOP:
			return true;
		default:
			return false;
		}
	}

	/**
	 * Computes the x coordinate of the left edge of an object aligned in an area.
	 * <p>
	 * If the horizontal alignment is invalid or not set, the object is aligned on the left.
	 *
	 * @param width
	 *            the object width.
	 * @param areaX
	 *            the area x coordinate.
	 * @param areaWidth
	 *            the area width.
	 * @param horizontalAlignment
	 *            the horizontal alignment.
	 * @return the x coordinate of the left of the object.
	 * @see #checkHorizontalAlignment(int)
	 */
	public static int computeLeftX(int width, int areaX, int areaWidth, int horizontalAlignment) {
		return computeLeftX(-(areaWidth - width), areaX, horizontalAlignment);
	}

	/**
	 * Computes the y coordinate of the top edge of an object aligned in an area.
	 * <p>
	 * If the vertical alignment is invalid or not set, the object is aligned on the top.
	 *
	 * @param height
	 *            the object height.
	 * @param areaY
	 *            the area y coordinate.
	 * @param areaHeight
	 *            the area height.
	 * @param verticalAlignment
	 *            the vertical alignment.
	 * @return the y coordinate of the top of the object.
	 * @see #checkVerticalAlignment(int)
	 */
	public static int computeTopY(int height, int areaY, int areaHeight, int verticalAlignment) {
		return computeTopY(-(areaHeight - height), areaY, verticalAlignment);
	}

	/**
	 * Computes the x coordinate of the left edge of an object aligned on an anchor point.
	 * <p>
	 * If the horizontal alignment is invalid, the object is aligned on the left.
	 *
	 * @param width
	 *            the object width.
	 * @param anchorX
	 *            the anchor x coordinate.
	 * @param horizontalAlignment
	 *            the horizontal alignment.
	 * @return the x coordinate of the left of the object.
	 */
	public static int computeLeftX(int width, int anchorX, int horizontalAlignment) {
		switch (horizontalAlignment) {
		case RIGHT:
			return anchorX - width;
		case HCENTER:
			return anchorX - (width >> 1);
		case LEFT:
		default:
			return anchorX;
		}
	}

	/**
	 * Computes the y coordinate of the top edge of an object aligned on an anchor point.
	 * <p>
	 * If the vertical alignment is invalid, the object is aligned on the top.
	 *
	 * @param height
	 *            the object height.
	 * @param anchorY
	 *            the anchor y coordinate.
	 * @param verticalAlignment
	 *            the vertical alignment.
	 * @return the y coordinate of the top of the object.
	 */
	public static int computeTopY(int height, int anchorY, int verticalAlignment) {
		switch (verticalAlignment) {
		case BOTTOM:
			return anchorY - height;
		case VCENTER:
			return anchorY - (height >> 1);
		case TOP:
		default:
			return anchorY;
		}
	}
}
