/*
 * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#pragma once

#include <ox/model/def.hpp>
#include <ox/std/error.hpp>
#include <ox/std/new.hpp>

#include "point.hpp"

namespace nostalgia::geo {

class Bounds {

	public:
		static constexpr auto TypeName = "net.drinkingtea.nostalgia.geo.Bounds";
		static constexpr auto TypeVersion = 1;
		int x = 0;
		int y = 0;
		int width = 0;
		int height = 0;

		constexpr Bounds() noexcept = default;

		constexpr Bounds(int x, int y, int w, int h) noexcept;

		constexpr Bounds(const Point &pt1, const Point &pt2) noexcept;

		[[nodiscard]]
		constexpr bool intersects(const Bounds &other) const noexcept;

		[[nodiscard]]
		constexpr bool contains(int x, int y) const noexcept;

		[[nodiscard]]
		constexpr bool contains(const Point &pt) const noexcept;

		[[nodiscard]]
		constexpr int x2() const noexcept;

		[[nodiscard]]
		constexpr int y2() const noexcept;

		[[nodiscard]]
		constexpr Point pt1() const noexcept;

		[[nodiscard]]
		constexpr Point pt2() const noexcept;

		constexpr void setPt2(const Point &pt) noexcept;

	private:
		constexpr void set(const Point &pt1, const Point &pt2) noexcept;

};

constexpr Bounds::Bounds(int x, int y, int w, int h) noexcept {
	this->x = x;
	this->y = y;
	this->width = w;
	this->height = h;
}

constexpr Bounds::Bounds(const Point &pt1, const Point &pt2) noexcept {
	set(pt1, pt2);
}

constexpr bool Bounds::intersects(const Bounds &o) const noexcept {
	return o.x2() >= x && x2() >= o.x && o.y2() >= y && y2() >= o.y;
}

constexpr bool Bounds::contains(int x, int y) const noexcept {
	return x >= this->x && y >= this->y && x <= x2() && y <= y2();
}

constexpr bool Bounds::contains(const Point &pt) const noexcept {
	return contains(pt.x, pt.y);
}

constexpr int Bounds::x2() const noexcept {
	return x + width;
}

constexpr int Bounds::y2() const noexcept {
	return y + height;
}

constexpr Point Bounds::pt1() const noexcept {
	return {x, y};
}

constexpr Point Bounds::pt2() const noexcept {
	return {x2(), y2()};
}

constexpr void Bounds::setPt2(const Point &pt) noexcept {
	set(pt1(), pt);
}

constexpr void Bounds::set(const Point &pt1, const Point &pt2) noexcept {
	int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
	if (pt1.x <= pt2.x) {
		x1 = pt1.x;
		x2 = pt2.x;
	} else {
		x1 = pt2.x;
		x2 = pt1.x;
	}
	if (pt1.y <= pt2.y) {
		y1 = pt1.y;
		y2 = pt2.y;
	} else {
		y1 = pt2.y;
		y2 = pt1.y;
	}
	this->x = x1;
	this->y = y1;
	this->width = x2 - x1;
	this->height = y2 - y1;
}

oxModelBegin(Bounds)
	oxModelField(x)
	oxModelField(y)
	oxModelField(width)
	oxModelField(height)
oxModelEnd()

}
