// =====================================================================================================================
//  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_data.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_DATA_H
#define FENNEC_RTTI_TYPE_DATA_H

#include <fennec/containers/bitfield.h>
#include <fennec/containers/dynarray.h>
#include <fennec/string/string.h>
#include <fennec/memory/pointers.h>
#include <fennec/rtti/typeid.h>
#include <fennec/rtti/forward.h>
#include <fennec/rtti/detail/_type_name.h>

namespace fennec
{

// Type Data ===========================================================================================================

///
/// \brief Structure holding information about a type
struct type_data {

	///
	/// \brief Enum representing flags in a bitset for the presence of type traits
	enum property_ {
		property_complete = 0, //!< Is the type complete
		property_iterable,     //!< Is the type iterable, i.e. implements \emph{begin()} and \emph{end()}
		property_indexable,    //!< Is the type indexable, i.e. implements `operator[]`
		property_mappable,     //!< Is the type mappable, i.e. implements `operator[]` and defines \emph{key_t}

		property_count //!< The number of property flags
	};

	using properties_t = bitfield<property_count>; //!< Bitfield representing the presence of type properties

	uint64_t uuid; //!< The Unique Universal Identifier for the type
	string   name; //!< A string representation of the type name

	type_data*           raw_type; //!< Raw type, i.e. \emph{remove_cvrefptr_t<T>}
	dynarray<type_data*> supers;   //!< The super types of the class
	dynarray<type_data*> subs;     //!< The sub types of the class

	properties_t properties; //!< The property flags

	type_data* key_type; //!< The key type for mappable types.
};


// Type Storage ========================================================================================================

///
/// \brief Structure holding all types acquired at runtime.
struct type_storage {

// Definitions =========================================================================================================
private:

	template<typename ClassT> using _super_class_list = typename ClassT::super_class_list;
	template<typename ClassT> using _key_t            = typename ClassT::key_t;


// Private Helpers =====================================================================================================
private:

	static dynarray<unique_ptr<type_data>>& _typelist() {
		static dynarray<unique_ptr<type_data>> typelist;
		return typelist;
	}

	template<typename T>
	static unique_ptr<type_data> _make_data() {
		using raw_t    = remove_cvrefptr_t<T>;
		using supers_t = detect_t<typelist<>, _super_class_list, T>;

		auto res = fennec::unique_ptr<type_data>(new type_data{
			.uuid = typeuuid<T>(),
			.name = detail::type_name<T>(),

			.raw_type = typeuuid<raw_t>() == typeuuid<T>() ? nullptr : type_storage::get_data<raw_t>(),
			.supers   = type_storage::get_data(supers_t{}),
			.subs     = {},

			.properties = { is_complete_v<T>, is_iterable_v<T>, is_indexable_v<T>, is_mappable_v<T> },

			.key_type = type_storage::get_data<detect_t<void, _key_t, T>>(),
		});

		for (type_data* t : res->supers) {
			t->subs.push_back(res.get());
		}

		return res;
	}


// Run-Time Type Deduction =============================================================================================
public:

	/// \name Run-Time Type Deduction
	/// @{

	///
	/// \tparam T Type to deduce
	/// \returns A pointer to the data for \emph{T}.
	template<typename T>
	static type_data* get_data() {
		return nullptr;
	}

	///
	/// \brief Deduce information for the provided type
	/// \tparam T Type to deduce
	/// \returns A pointer to the data for \emph{T}.
	template<typename T>
	static type_data* get_data() requires(not is_void_v<T>) {
		auto&          typelist = _typelist();
		const uint64_t uuid     = typeuuid<T>();

		if (typelist.size() <= uuid) {
			typelist.resize(uuid * 2);
		}

		if (typelist[uuid].is_empty()) {
			typelist[uuid] = fennec::forward<unique_ptr<type_data>>(_make_data<T>());
		}

		return typelist[uuid].get();
	}

	///
	/// \brief Deduce information for a list of types.
	/// \tparam Ts Types to deduce
	/// \returns An array of pointers to the data for \emph{Ts...}
	template<typename...Ts>
	static dynarray<type_data*> get_data(typelist<Ts...>) {
		return {
			get_data<Ts>()...
		};
	}

	/// @}
};

}

#endif // FENNEC_RTTI_TYPE_DATA_H