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

#ifndef FENNEC_LANG_TYPE_SEQUENCES_H
#define FENNEC_LANG_TYPE_SEQUENCES_H

///
/// \page fennec_lang_type_sequences Type Sequences
///
/// \brief This header is part of the metaprogramming library. It defines structures for sequences of types, used during compile time.
///
/// \code #include <fennec/lang/type_sequences.h> \endcode
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_type_sequences">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::first_element "typename first_element<ValueT, Values...>::type"<br>
///         \ref fennec::first_element_t "first_element_t<ValueT, Values...>"
///     <td width="50%" style="vertical-align: top">
///         \copydoc fennec::first_element
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::replace_first_element "typename replace_first_element<ClassT, SubT, OriginT, RestT...>::type"<br>
///     <td width="50%" style="vertical-align: top">
///         \copydoc fennec::replace_first_element
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::is_unique "is_unique<TypesT...>::value"<br>
///         \ref fennec::is_unique_v "is_unique_v<TypesT...>"
///     <td width="50%" style="vertical-align: top">
///         \copydoc fennec::is_unique
/// </table>
///

#include <fennec/lang/detail/_type_sequences.h>

namespace fennec
{


template<typename...TypesT> struct type_sequence {};


// fennec::first_element ===============================================================================================

///
/// \brief Get the first element of a template parameter pack
/// \tparam TypesT the Parameter Pack
template<typename...TypesT> struct first_element : detail::_first_element<TypesT...> {};

///
/// \brief alias for `first_element<TypesT...>::type`
/// \tparam TypesT the Parameter Pack
template<typename...TypesT> using first_element_t = typename first_element<TypesT...>::type;



// fennec::nth_element =================================================================================================

///
/// \brief Gets the type of the nth element of the type sequence `TypesT...`
/// \tparam n The index in the type sequence
/// \tparam TypesT The type sequence
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};

///
/// \brief alias for nth_element<n, TypesT>::type
/// \tparam n The index in the type sequence
/// \tparam TypesT the Parameter Pack
template<size_t n, typename...TypesT> using nth_element_t = nth_element<n, TypesT...>::type;



// fennec::replace_first_element =======================================================================================

///
/// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first \emph{ArgT} of \emph{ArgsT...} with \emph{SubT}
template<typename ClassT, typename SubT> struct replace_first_element { };

#ifndef FENNEC_DOXYGEN
// Implementation
template<
	template<typename, typename...> class ClassT              // The Base Template
,	typename SubT, typename OriginT                           // The Original Type and Type to Substitute
,	typename... RestT>                                        // The Rest of the Parameter Pack
struct replace_first_element<ClassT<OriginT, RestT...>, SubT> // Specialization
	{ using type = ClassT<SubT, RestT...>; };                 // Definition
#endif



// fennec::max_element_size ============================================================================================

///
/// \brief Gets the max value of the size of each type in the sequence, i.e. `max(sizeof(Ts)...)`
/// \tparam Ts The type sequence to check
template<typename...Ts> struct max_element_size : detail::_max_element_size<0, Ts...> {};

///
/// \brief Shorthand for `max_element_size<Ts...>::value`
/// \tparam Ts The type sequence to check
template<typename...Ts> constexpr size_t max_element_size_v = max_element_size<Ts...>::value;



// fennec::find_element ================================================================================================

///
/// \brief Finds the index of \emph{T} in \emph{Ts}, if \emph{T} is not found, results in `sizeof...(Ts)`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> struct find_element : detail::_find_element<0, T, Ts...> {};

///
/// \brief Shorthand for `find_element<T, Ts...>::value`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> constexpr size_t find_element_v = find_element<T, Ts...>::value;



// fennec::search_element ==============================================================================================


///
/// \brief Find the first element in `TypesT...` that satisfies `SearchT<T>`
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
/// \tparam TypesT The type sequence to search
template<template<typename> typename SearchT, typename...TypesT> struct search_element : detail::_search_element<SearchT, TypesT...> {};

///
/// \brief Shorthand for `search_element_t<T, Ts...>::type`
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
/// \tparam TypesT The type sequence to search
template<template<typename> typename SearchT, typename...TypesT> using search_element_t = search_element<SearchT, TypesT...>::type;

template<template<typename, typename...> typename, typename, typename...> struct search_element_args;

///
/// \brief Find the first element `T` in `TypesT...` that satisfies `SearchT<T, ArgsT...>`
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
/// \tparam TypesT The type sequence to search
/// \tparam ArgsT The arguments for the search
template<template<typename, typename...> typename SearchT, typename...TypesT, typename...ArgsT>
struct search_element_args<SearchT, type_sequence<ArgsT...>, TypesT...>
	: detail::_search_element_args<SearchT, detail::_type_sequence<ArgsT...>, TypesT...> {
};




// fennec::contains_element ============================================================================================

///
/// \brief Checks if the type sequence \emph{Ts...} contains \emph{T}
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> struct contains_element : bool_constant<(is_same_v<T, Ts> or ...)> {};

///
/// \brief Shorthand for `contains_element_v<T, Ts...>::value`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> constexpr bool contains_element_v = contains_element<T, Ts...>::value;



// fennec::is_unique ===================================================================================================

///
/// \brief Checks if all types in a type sequence are unique
/// \tparam Ts The type sequence to check
template<typename...Ts> struct is_unique : false_type {};

#ifndef FENNEC_DOXYGEN
// Single type case
template<typename T>  struct is_unique<T> : true_type {};

// Recursion case
template<typename T, typename...Ts> requires(not is_same_v<T, Ts> && ...)
struct is_unique<T, Ts...> : is_unique<Ts...> {};
#endif

///
/// \brief Shorthand for `is_unique<Ts...>::value`
/// \tparam Ts The type sequence to check
template<typename...Ts> constexpr bool is_unique_v = is_unique<Ts...>::value;

}


#endif // FENNEC_LANG_VARIADICS_H
