#pragma once

#include "engine/point.hpp"
#include "engine/size.hpp"

namespace devilution {

template <typename CoordT, typename SizeT = CoordT>
struct RectangleOf {
	PointOf<CoordT> position;
	SizeOf<SizeT> size;

	RectangleOf() = default;

	constexpr RectangleOf(PointOf<CoordT> position, SizeOf<SizeT> size)
	    : position(position)
	    , size(size)
	{
	}

	/**
	 * @brief Constructs a rectangle centered on the given point and including all tiles within the given radius.
	 *
	 * The resulting rectangle will be square with an odd size equal to 2*radius + 1.
	 *
	 * @param center center point of the target rectangle
	 * @param radius a non-negative value indicating how many tiles to include around the center
	 */
	explicit constexpr RectangleOf(PointOf<CoordT> center, SizeT radius)
	    : position(center - DisplacementOf<SizeT> { radius })
	    , size(static_cast<SizeT>(2 * radius + 1))
	{
	}

	/**
	 * @brief Whether this rectangle contains the given point.
	 * Works correctly even if the point uses a different underlying numeric type
	 */
	template <typename PointCoordT>
	constexpr bool contains(PointOf<PointCoordT> point) const
	{
		return contains(point.x, point.y);
	}

	template <typename T>
	constexpr bool contains(T x, T y) const
	{
		return x >= this->position.x
		    && x < (this->position.x + this->size.width)
		    && y >= this->position.y
		    && y < (this->position.y + this->size.height);
	}

	/**
	 * @brief Computes the center of this rectangle in integer coordinates. Values are truncated towards zero.
	 */
	constexpr PointOf<CoordT> Center() const
	{
		return position + DisplacementOf<SizeT>(size / 2);
	}

	/**
	 * @brief Returns a rectangle with all sides shrunk according to the given displacement
	 *
	 * Effectively moves the left/right edges in by deltaX, and the top/bottom edges in by deltaY
	 */
	constexpr RectangleOf<CoordT, SizeT> inset(DisplacementOf<SizeT> factor) const
	{
		return {
			position + factor,
			SizeOf<SizeT>(size.width - factor.deltaX * 2, size.height - factor.deltaY * 2)
		};
	}
};

using Rectangle = RectangleOf<int, int>;

} // namespace devilution
