// =====================================================================================================================
//  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 conditional_types.h
/// \brief \ref fennec_lang_conditional_types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

///
/// \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>
///
///

#include <fennec/lang/type_transforms.h>

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 `conditional::type`.
/// \tparam B the value of the condition
/// \tparam TrueT type to use when \f$B == true\f$
/// \tparam FalseT type to use when \f$B == false\f$
template<bool B, typename TrueT, typename FalseT>
struct conditional;


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


// specialization of fennec::conditional for `true` case
template<typename T, typename F>
struct conditional<true, T, F> : type_transform<T>{};


// specialization of fennec::conditional for `false` case
template<typename T, typename F>
struct conditional<false, T, F> : type_transform<F>{};


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

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

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


// 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;
};


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

///
/// \brief Leverage SFINAE to conditionally enable a function or class at compile-time
///
/// \details If `B` is `true`, define a public member type `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 enable_if<B, T>::type```
template<bool B, typename T = void>
using enable_if_t = typename enable_if<B, T>::type;

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

}

#endif // FENNEC_LANG_CONDITIONAL_TYPES_H
