// =====================================================================================================================
//  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/lang/conditional_types.h
/// \brief \ref fennec_lang_conditional_types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///


#ifndef FENNEC_LANG_CONDITIONAL_TYPES_H
#define FENNEC_LANG_CONDITIONAL_TYPES_H

#include <fennec/lang/type_identity.h>

///
/// \page fennec_lang_conditional_types Conditional Types
///
/// \code #include <fennec/lang/conditional_types.h> \endcode
///
/// This header contains various compile-time functions for conditionally setting types, detecting types, or
/// conditionally enabling functions. <br><br>
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_conditional_types">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::conditional "typename conditional<bool B, TrueT, FalseT>::type"<br>
///         \ref fennec::conditional_t "conditional_t<bool B, TrueT, FalseT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::conditional
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::detect "typename detect<DefaultT, DetectT<...>, ArgsT...>::type"<br>
///         \ref fennec::detect_t "detect_t<DefaultT, DetectT<...>, ArgsT...>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::detect
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::enable_if "typename enable_if<bool B, TypeT>::type"<br>
///         \ref fennec::enable_if_t "enable_if_t<bool B, TypeT>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::enable_if
/// </table>
///
///

namespace fennec
{

// fennec::conditional =================================================================================================

///
/// \brief select between two types based on a condition
///
/// \details Selects between \p TrueT and \p FalseT based on the boolean value \p b.
///	         The chosen type is stored in `fennec::conditional::type`.
/// \tparam B the value of the condition
/// \tparam TrueT type to use when \math{\textbf{B} = \textbf{true}}
/// \tparam FalseT type to use when \math{\textbf{B} = \textbf{false}}
template<bool B, typename TrueT, typename FalseT>
struct conditional;


///
/// \brief Shorthand for `typename fennec::conditional<ConditionV, TrueT, FalseT>::type`
template<bool B, typename TrueT, typename FalseT>
using conditional_t
	= typename conditional<B, TrueT, FalseT>::type;

#ifndef FENNEC_DOXYGEN
// specialization of fennec::conditional for \emph{true} case
template<typename T, typename F>
struct conditional<true, T, F> : type_identity<T>{};


// specialization of fennec::conditional for \emph{false} case
template<typename T, typename F>
struct conditional<false, T, F> : type_identity<F>{};
#endif

// fennec::detect ======================================================================================================

///
/// \brief Detect whether \emph{DetectT<ArgsT...>} is a valid type
///
/// \details Selects \emph{DetectT<ArgsT...>} if it exists, otherwise selects \emph{DefaultT} The chosen type is stored in `fennec::detect::type` and
///          a boolean value is stored in `fennec::detect::is_detected` representing whether \emph{DetectT<ArgsT...>} is found.
/// \tparam DefaultT Default type
/// \tparam DetectT Type to detect
/// \tparam ArgsT Any template arguments for \emph{DetectT<ArgsT>}
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
struct detect
{
	using type = DefaultT;                     //!< the detected type
	static constexpr bool is_detected = false; //!< whether it was detected
};

///
/// \brief Shorthand for `typename fennec::detect<DefaultT, DetectT, ArgsT...>::type`
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;


#ifndef FENNEC_DOXYGEN
// true case
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
	requires requires { typename DetectT<ArgsT...>; }
struct detect<DefaultT, DetectT, ArgsT...>
{
	using type = DetectT<ArgsT...>;
	static constexpr bool is_detected = true;
};
#endif


// fennec::enable_if ===================================================================================================

///
/// \brief Leverage SFINAE to conditionally enable a function or class at compile-time
///
/// \details If \emph{B} is \emph{true}, define a public member type \emph{type}. Otherwise, there is no member. <br>
///	         **Example Usage**
///	         \code{.cpp}
///          template<typename TypeT,
///                   enable_if_t<is_integral_v<TypeT>, bool> = true>
///          TypeT conditional_func() { return 0; }
///	         \endcode
///
/// \tparam B A boolean value
/// \tparam T The type to conditionally define
template<bool B, typename T = void>
struct enable_if {};

///
/// \brief Shorthand for `typename fennec::enable_if<B, T>::type`
template<bool B, typename T = void>
using enable_if_t = typename enable_if<B, T>::type;

#ifndef FENNEC_DOXYGEN
// true case
template<typename T>
struct enable_if<true, T> { using type = T; };
#endif

}

#endif // FENNEC_LANG_CONDITIONAL_TYPES_H
