// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025  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 type_traits.h
/// \brief \ref fennec_lang_type_traits
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_LANG_TYPE_TRAITS_H
#define FENNEC_LANG_TYPE_TRAITS_H

///
/// \page fennec_lang_type_traits Type Traits
///
/// \brief Part of the fennec metaprogramming library. This header defines structures for accessing traits of types
///        at compile time.
///
/// \code #include <fennec/lang/type_traits.h> \endcode
///
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_type_traits">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_void "is_void<TypeT>::value"<br>
///         \ref fennec::is_void_v "is_void_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_void
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_bool "is_bool<TypeT>::value"<br>
///         \ref fennec::is_bool_v "is_bool_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_bool
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_integral "is_integral<TypeT>::value"<br>
///         \ref fennec::is_integral_v "is_integral_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_integral
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_signed "is_signed<TypeT>::value"<br>
///         \ref fennec::is_signed_v "is_signed_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_signed
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_unsigned "is_unsigned<TypeT>::value"<br>
///         \ref fennec::is_unsigned_v "is_unsigned_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_unsigned
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_floating_point "is_floating_point<TypeT>::value"<br>
///         \ref fennec::is_floating_point_v "is_floating_point_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_floating_point
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_arithmetic "is_arithmetic<TypeT>::value"<br>
///         \ref fennec::is_arithmetic_v "is_arithmetic_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_arithmetic
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_same "is_same<TypeT>::value"<br>
///         \ref fennec::is_same_v "is_same_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_same
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_convertible "is_convertible<TypeT0, TypeT1>::value"<br>
///         \ref fennec::is_convertible_v "is_convertible_v<TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::is_convertible
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_constructible "is_constructible<ClassT, ArgsT...>::value"<br>
///         \ref fennec::is_constructible_v "is_constructible_v<ClassT, ArgsT...>"
///     <td width="50%" style="vertical-align: top">
///         \copydoc fennec::is_constructible
///
/// </table>
///

#include <fennec/lang/type_transforms.h>
#include <fennec/lang/detail/__type_traits.h>

namespace fennec
{

// fennec::is_void =====================================================================================================

///
/// \brief check if \p T is of type void
///
/// \details Stores a boolean value in `is_void::value`, representing whether the provided type is of base type void.
/// \tparam T type to check
template<typename T> struct is_void
	: detail::__is_void<remove_cvr_t<T>>{};

///
/// \brief shorthand for ```is_void<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_void_v = is_void<T>::value;



// fennec::is_bool =====================================================================================================

///
/// \brief check if \p T is of type bool
///
/// \details Stores a boolean value in `is_bool::value`, representing whether the provided type is of base type bool.
/// \tparam T type to check
template<typename T> struct is_bool
	: detail::__is_bool<remove_cvr_t<T>>{};

///
/// \brief shorthand for ```is_bool<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_bool_v = is_bool<T>::value;



// fennec::is_null_pointer =============================================================================================

///
/// \brief check if \p T is of type nullptr_t
///
/// \details Stores a boolean value in `is_null_pointer::value`, representing whether the provided type is of base type nullptr_t.
/// \tparam T type to check
template<typename T> struct is_null_pointer
	: detail::__is_null_pointer<remove_cvr_t<T>>{};

///
/// \brief shorthand for ```is_null_pointer<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_null_pointer_v = is_null_pointer<T>::value;



// fennec::is_array ====================================================================================================

#if FENNEC_HAS_BUILTIN_IS_ARRAY

///
/// \brief check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
	: bool_constant<FENNEC_BUILTIN_IS_ARRAY(T)> {};

#else

///
/// \brief check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
	: false_type {};

// overload for a sized array type
template<typename T, size_t N> struct is_array<T[N]>
	: true_type {};

// overload for a generic array type
template<typename T> struct is_array<T[]>
	: true_type {};

#endif

///
/// \brief shorthand for ```is_array<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_array_v = is_array<T>::value;

// fennec::is_class ====================================================================================================

///
/// \brief check if \p T is a class
/// \tparam T type to check
template<typename T> struct is_class
	: bool_constant<FENNEC_BUILTIN_IS_CLASS(T)> {};

///
/// \brief check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_class_v = is_class<T>::value;



// Integral Types ======================================================================================================

///
/// \brief check if \p T is of an integral
///
/// \details Stores a boolean value in `is_integral::value`, representing whether the provided type is of a base integer type.
/// \tparam T type to check
template<typename T> struct is_integral
	: detail::__is_integral<remove_cvr_t<T>> {};

///
/// \brief shorthand for ```is_integral<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_integral_v = is_integral<T>::value;


///
/// \brief check if \p T is of a signed integral
///
/// \details Checks if type `T` is a signed type i.e. `T(-1) < T(0)` and stores it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_signed
	: detail::__is_signed<remove_cvr_t<T>> {};

///
/// \brief shorthand for ```is_signed<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_signed_v = is_signed<T>::value;


///
/// \brief check if \p T is of an unsigned integral
///
/// \details Checks if type `T` is an unsigned type i.e. `T(-1) > T(0)` and stores it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_unsigned
	: detail::__is_unsigned<remove_cvr_t<T>> {};

///
/// \brief shorthand for ```is_unsigned<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_unsigned_v = is_unsigned<T>::value;



// Floating Point Types ================================================================================================

///
/// \brief check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_floating_point
	: detail::__is_floating_point<remove_cvr_t<T>>{};

///
/// \brief shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_floating_point_v = is_floating_point<T> {};



// Arithmetic Types ====================================================================================================

///
/// \brief check if \p T is an arithmetic type
///
/// \details Checks if type `T` is a built-in type with arithmetic operators and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_arithmetic
	: bool_constant<is_integral_v<T> or is_floating_point_v<T>>{};

///
/// \brief shorthand for ```is_arithmetic<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_arithmetic_v = is_arithmetic<T>::value;


// fennec::is_fundamental ==============================================================================================

///
/// \brief check if \p T is a fundamental type, i.e. arithmetic, void, or nullptr_t
/// \tparam T type to check
template<typename T> struct is_fundamental
	: bool_constant<is_arithmetic_v<T> || is_void_v<T> || is_null_pointer_v<T>>{};

///
/// \brief shorthand for ```is_fundamental<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_fundamental_v = is_fundamental<T>::value;


// fennec::is_same =====================================================================================================

///
/// \brief check if the two types are identical
///
/// \details Checks if `T0` and `T1` are identical and store it in `is_same::value`
/// \tparam T0 first type to check
/// \tparam T1 second type to check
template<typename T0, typename T1> struct is_same : false_type {};

// true case
template<typename T> struct is_same<T, T> : true_type {};

///
/// \brief shorthand for ```is_same<T0, T1>::value```
/// \tparam T type to check
template<typename T0, typename T1> constexpr bool_t is_same_v = is_same<T0, T1> {};

// fennec::is_convertible ==============================================================================================

///
/// \brief check if type `T0` can be converted `T1`
///
/// \details Checks if `TypeT0`
/// \tparam FromT First type
/// \tparam ToT Second type
template<typename FromT, typename ToT> struct is_convertible
	: bool_constant<FENNEC_BUILTIN_IS_CONVERTIBLE(FromT, ToT)> {};

///
/// \brief shorthand for `can_convert<TypeT0, TypeT1>::value`
/// \param FromT First type
/// \param ToT Second type
template<typename FromT, typename ToT> constexpr bool_t is_convertible_v = is_convertible<FromT, ToT>{};


// fennec::is_constructible ===============================================================================================

///
/// \brief Check if `ClassT` can be constructed with `ArgsT,` i.e. `ClassT(ArgsT...)`.
///        This may be read as "is `ClassT` constructible with `ArgsT`"
/// \tparam ClassT The class type to test
/// \tparam ArgsT The arguments for the specific constructor
template<typename ClassT, typename...ArgsT> struct is_constructible
	: bool_constant<FENNEC_BUILTIN_IS_CONSTRUCTIBLE(ClassT, ArgsT...)> {};

///
/// \brief Shorthand for `is_constructible<ClassT, ArgsT...>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_constructible_v = is_constructible<ClassT, ArgsT...>{};


// fennec::
	
//

}

#endif // FENNEC_LANG_TYPE_TRAITS_H
