// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/rtti/type_registry.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_RTTI_TYPE_REGISTRY_H
#define FENNEC_RTTI_TYPE_REGISTRY_H

#include <fennec/containers/priority_queue.h>

#include <fennec/lang/type_traits.h>

#include <fennec/rtti/type.h>

namespace fennec
{

///
/// \brief Interface for defining a registry of types.
/// \tparam BaseT The base type of the registry
/// \tparam ArgsT Arguments for constructing the type
template<typename BaseT, typename...ArgsT>
class type_registry {

// Definitions =========================================================================================================
public:

	/// \name Definitions
	/// @{

	using ctor_t = BaseT* (*)(ArgsT...); //!< Signature for a function that calls the constructor.

	///
	/// \brief Struct holding a type entry.
	struct entry {

	// Public Member Variables -----------------------------------------------------------------------------------------
	public:
		size_t priority; //!< A priority value associated with the type
		type   type;     //!< Type information for the entry
		ctor_t ctor;     //!< A constructor for the type


	// Constructors & Destructor ---------------------------------------------------------------------------------------
	public:

		/// \name Constructors & Destructor
		/// @{

		///
		/// \brief Default Constructor
		///
		/// \details Holds a priority of \math{0}, null type, and null constructor.
		entry()
			: priority(0)
			, type()
			, ctor(nullptr) {
		}

		///
		/// \brief Copy Constructor
		/// \param e The entry to copy
		entry(const entry& e)
			: priority(e.priority)
			, type(e.type)
			, ctor(e.ctor) {
		}

		///
		/// \brief Copy Constructor
		/// \param e The entry to move
		entry(entry&& e) noexcept
			: priority(e.priority)
			, type(e.type)
			, ctor(e.ctor) {
		}

		///
		/// \brief Entry Constructor
		/// \param p The priority of the entry
		/// \param type The type info
		/// \param ctor A function that constructs the type
		entry(size_t p, fennec::type type, ctor_t ctor)
			: priority(p), type(type), ctor(ctor) {
		}

		///
		/// \brief Destructor
		~entry() = default;

		/// @}


	// Assignment ------------------------------------------------------------------------------------------------------
	public:

		/// \name Assignment
		/// @{

		///
		/// \brief Copy Assignment
		/// \param e The entry to copy
		/// \returns A reference to \emph{this} after copying \emph{e}.
		entry& operator=(const entry& e) = default;

		///
		/// \brief Move Assignment
		/// \param e The entry to move
		/// \returns A reference of self after moving \emph{e}.
		entry& operator=(entry&& e) noexcept = default;

		/// @}
	};

	///
	/// \brief Comparison for entries
	struct compare {
		bool operator()(const entry& a, const entry& b) const {
#if FENNEC_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wordered-compare-function-pointers"
#endif
			return a.priority > b.priority and a.ctor < b.ctor;
#if FENNEC_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
		}
	};

	using entrylist_t = priority_queue<entry, compare>; //!< A list of type entries.


// Type Registration ===================================================================================================

	/// \name Type Registration
	/// @{

	///
	/// \brief Type Registration Function
	/// \tparam DerivedT The derived type to register
	/// \param priority The priority of the type
	template<typename DerivedT> requires(is_base_of_v<BaseT, DerivedT>)
	static void register_type(size_t priority = 0) {
		_global_list().emplace(
			priority,
			type::get<DerivedT>(),
			_constructor_helper<DerivedT>
		);
	}

	///
	/// \brief Get the list of registered types
	/// \returns A reference to the list of types.
	static const entrylist_t& get_type_list() {
		return _global_list();
	}

	/// @}


// Private Helpers =====================================================================================================
private:
	static entrylist_t& _global_list() {
		static entrylist_t list;
		return list;
	}

	template<typename T>
	static BaseT* _constructor_helper(ArgsT...args) {
		return new T(args...);
	}
};

}

#endif // FENNEC_RTTI_TYPE_REGISTRY_H