/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#include "Math.hpp"
#include "Range.hpp"

namespace OpenVulkano::Math
{
	/**
	 * \brief A class that represents an axis aligned bounding box
	 */
	class AABB final : public Range<Math::Vector3f>
	{
	public:
		AABB() : Range(Math::Vector3f(INFINITY), Math::Vector3f(-INFINITY)) {}

		AABB(const Math::Vector3f& min, const Math::Vector3f& max) : Range(min, max) {}
		
		AABB(const Vector3f& point) : Range(point, point)
		{}
		
		AABB(const Vector3f& point, float radius) : Range(point - radius, point + radius)
		{}

		/**
		 * \brief Initiates the AABB to a single point (min=max=point)
		 * \param point The point that should be used as min and max of the AABB
		 */
		void Init(const Math::Vector3f& point)
		{
			min = max = point;
		}

		void Init(const Math::Vector3f& min, const Math::Vector3f& max)
		{
			this->min = min;
			this->max = max;
		}
		
		void Init(const Math::Vector3f& point, float radius)
		{
			min = point - radius;
			max = point + radius;
		}

		/**
		 * \brief Initiates the AABB from some other AABB
		 * \param other The other AABB that should be copied
		 */
		void Init(const AABB& other)
		{
			min = other.GetMin();
			max = other.GetMax();
		}

		void Grow(const Math::Vector3f& point)
		{
			min = Math::Utils::min(min, point);
			max = Math::Utils::max(max, point);
		}

		void Grow(const AABB& otherAABB)
		{
			min = Math::Utils::min(min, otherAABB.GetMin());
			max = Math::Utils::max(max, otherAABB.GetMax());
		}

		void Grow(const AABB& otherAABB, Math::Matrix4f transformation)
		{
			//TODO
		}

		[[nodiscard]] Math::Vector3f GetDiagonal() const
		{
			return GetSize();
		}

		[[nodiscard]] Math::Vector3f GetCenter() const
		{
			return min + (GetDiagonal() * 0.5f);
		}

		/**
		 * \brief Checks if the AABB overlaps with an other AABB
		 * \param other The other AABB that should be checked
		 * \return true if the AABB overlaps with the other, false if not
		 */
		[[nodiscard]] bool IsOverlapping(const AABB& other) const
		{
			return !(other.min.x > max.x || other.max.x < min.x || other.min.y > max.y ||
					 other.max.y < min.y || other.min.z > max.z || other.max.z < min.z);
		}
		
		[[nodiscard]] bool InBounds(const Math::Vector3f& position) const
		{
			return Math::Utils::all(Math::Utils::lessThanEqual(min, position)) && Math::Utils::all(Math::Utils::lessThanEqual(position, max));
		}
		
		[[nodiscard]] bool Inside(const Math::Vector3f& position) const
		{
			return Math::Utils::all(Math::Utils::lessThan(min, position)) && Math::Utils::all(Math::Utils::lessThan(position, max));
		}

		[[nodiscard]] bool IsEmpty() const
		{
			return min == Math::Vector3f(INFINITY) && max == Math::Vector3f(-INFINITY);
		}

		/**
		 * \brief Resets the AABB to min=Inf, max=-Inf, same as Init()
		 */
		void Reset()
		{
			min = Math::Vector3f(INFINITY);
			max = Math::Vector3f(-INFINITY);
		}
	};
}
