/*
 * 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/Math.hpp"
#include "Math/AABB.hpp"
#include <optional>

namespace OpenVulkano::Scene
{

	struct RayHit
	{
		Math::Vector3f point; 
		Math::Vector3f normal;
		float distance;
		bool operator==(const RayHit& other) const = default;
		bool operator!=(const RayHit& other) const = default;
	};

	class Ray
	{
	public:
		Ray() = default;
		Ray(const Math::Vector3f& origin, const Math::Vector3f& dir)
		    : m_origin(origin), m_dir(Math::Utils::normalize(dir))
		{
		}
		void SetPosition(const Math::Vector3f& origin) { m_origin = origin; }
		void SetDir(const Math::Vector3f& dir) { m_dir = dir; }
		[[nodiscard]] std::optional<RayHit> IntersectSphere(const Math::Vector3f& center, float radius) const;
		int IntersectSphere(const Math::Vector3f& center, float radius, RayHit& p1, RayHit& p2) const;
		[[nodiscard]] std::optional<RayHit> IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1,
		                                        const Math::Vector3f& v2) const;
		[[nodiscard]] std::optional<RayHit> IntersectTriangle(const Math::Vector3f& v0, const Math::Vector3f& v1,
		                                                      const Math::Vector3f& v2, const Math::Vector3f& n0,
		                                                      const Math::Vector3f& n1, const Math::Vector3f& n2) const;
		[[nodiscard]] std::optional<RayHit> IntersectQuad(const Math::Vector3f& v0, const Math::Vector3f& v1,
		                                    const Math::Vector3f& v2, const Math::Vector3f& v3) const;
		[[nodiscard]] std::optional<RayHit> IntersectAABB(const Math::AABB& bbox) const;
		int IntersectAABB(const Math::AABB& bbox, RayHit& p1, RayHit& p2) const;
		[[nodiscard]] std::optional<RayHit> IntersectPlane(const Math::Vector3f& pOrigin,
		                                                   const Math::Vector3f& pNorm) const;
		[[nodiscard]] Math::Vector3f& GetOrigin() { return m_origin; }
		[[nodiscard]] Math::Vector3f& GetDir() { return m_dir; }
		[[nodiscard]] const Math::Vector3f& GetOrigin() const { return m_origin; }
		[[nodiscard]] const Math::Vector3f& GetDir() const { return m_dir; }
	private:
		Math::Vector3f m_origin;
		Math::Vector3f m_dir;
		mutable Math::Vector2f m_baryPos;
	};
}
