// =====================================================================================================================
//  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.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_H
#define FENNEC_RTTI_TYPE_H

#include <fennec/lang/types.h>
#include <fennec/rtti/type_data.h>

namespace fennec
{

///
/// \brief A struct representing traits of a type, deduced at run-time.
struct type {

// Constructors & Destructor ===========================================================================================
public:

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

	///
	/// \brief Default Constructor
	/// \details Initializes with null backing data.
	type()
		: type(nullptr) {
	}

	///
	/// \brief Data Constructor
	/// \param d Type data to reference
	///
	/// \note This function is only meant to be used internally.
	///       If you're using it, you're probably doing something wrong.
	type(type_data* d)
		: _data(d) {
	}

	///
	/// \brief Copy Constructor
	/// \param t Type to copy
	type(const type& t) = default;

	///
	/// \brief Move Constructor
	/// \param t Type to move.
	type(type&& t) noexcept = default;

	///
	/// \brief Trivial Destructor
	~type() = default;

	/// @}


// Assignment ==========================================================================================================
public:

	/// \name Assignment
	/// @{

	///
	/// \brief Copy Assignment
	/// \param t Type to copy
	/// \returns A reference to \emph{this} after copying \emph{t}
	type& operator=(const type& t) = default;

	///
	/// \brief Move Assignment
	/// \param t Type to move
	/// \returns A reference to \emph{this} after copying \emph{t}
	type& operator=(type&& t) noexcept = default;

	/// @}


// Properties ==========================================================================================================
public:

	/// \name Properties
	/// @{

	///
	/// \returns A const-qualified reference to a string containing the name of the type
	const string& name() const {
		static const string nullname = string("[none]");
		return _data ? _data->name : nullname;
	}

	///
	/// \returns An integer value containing the unique universal identifier for the type
	uint64_t id() const {
		return _data ? _data->uuid : 0;
	}

	///
	/// \returns A dynarray of all the super (base) types of the type
	dynarray<type> supertypes() const {
		return _data ? _data->supers : dynarray<type_data*>{};
	}

	///
	/// \returns A dynarray of all the sub (child) types of the type
	dynarray<type> subtypes() const {
		return _data ? _data->subs : dynarray<type_data*>{};
	}

	///
	/// \returns \emph{true} if this is a complete type, false otherwise
	bool is_complete() const {
		return _data ? _data->properties.test(type_data::property_complete) : false;
	}

	///
	/// \returns \emph{true} if this type fulfills the [C++11 range-initializer](https://en.cppreference.com/w/cpp/language/range-for.html), false otherwise
	bool is_iterable() const {
		return _data ? _data->properties.test(type_data::property_iterable) : false;
	}

	///
	/// \returns \emph{true} if this type implements `operator[]` with a single parameter of integral type, false otherwise
	bool is_indexable() const {
		return _data ? _data->properties.test(type_data::property_indexable) : false;
	}

	///
	/// \returns \emph{true} if this type implements `operator[]` with a single parameter of type `type::key_t`
	bool is_mappable() const {
		return _data ? _data->properties.test(type_data::property_mappable) : false;
	}

	///
	/// \returns The type used as a key for mappable types.
	type key_type() const {
		return _data ? _data->key_type : nullptr;
	}

	/// @}


// Comparison ==========================================================================================================
public:

	/// \name Comparison
	/// @{

	///
	/// \brief Type Equality Operator
	/// \param c The type to check against
	/// \returns True if the types are identical, false otherwise.
	bool operator==(const type& c) const {
		return _data == c._data;
	}

	///
	/// \brief Type Inequality Operator
	/// \param c The type to check against
	/// \returns True if the types are identical, false otherwise.
	bool operator!=(const type& c) const {
		return _data != c._data;
	}

	/// @}


// Type Retrieval ======================================================================================================
public:

	/// \name Type Retrieval
	/// @{

	///
	/// \brief Get the type info of the specified type
	/// \tparam TypeT The type to check
	/// \returns A type struct containing information about TypeT
	template<typename TypeT>
	static type get() {
		return type(static_cast<TypeT*>(nullptr));
	}

	///
	/// \brief Get the type info of the provided object.
	/// \tparam TypeT The type, automatically deduced
	/// \param t A pointer to an object
	/// \returns The type info of the provided type
	template<typename TypeT>
	static type get_from_instance(TypeT* t) {
		return type(t);
	}

	///
	/// \brief Get the type info of the provided object.
	/// \tparam TypeT The type, automatically deduced
	/// \param t A pointer to an object
	/// \returns The type info of the provided type
	template<typename TypeT>
	static type get_from_instance(const TypeT*) {
		return type(static_cast<TypeT*>(nullptr));
	}

	/// @}


// Private Member Variables ============================================================================================
private:
	const type_data* _data;


// Private Helpers =====================================================================================================
private:
	template<typename TypeT>
	type(TypeT*) : _data(type_storage::get_data<TypeT>()) {
	}


};


}

#endif // FENNEC_RTTI_TYPE_H