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

#ifndef FENNEC_LANG_SEQUENCES_H
#define FENNEC_LANG_SEQUENCES_H

///
/// \page fennec_lang_metasequences Metasequences
///
/// \brief This header is part of the metaprogramming library. It defines structures for metasequences of values, used during compile time.
///
/// \code #include <fennec/lang/metasequences.h> \endcode
///
/// <table width="100%" class="fieldtable" id="table_fennec_lang_metasequences">
/// <tr><th style="vertical-align: top">Syntax
///     <th style="vertical-align: top">Description
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::metasequence "metasequence<ValueT, Values...>"<br>
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::metasequence
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::integer_metasequence "integer_metasequence<IntT, Values...>"<br>
///         \ref fennec::make_integer_metasequence "typename make_integer_metasequence<IntT, N>::type"<br>
///         \ref fennec::make_integer_metasequence_t "make_integer_metasequence_t<IntT, N>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::integer_metasequence
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::index_metasequence "index_metasequence<Indices...>"<br>
///         \ref fennec::make_index_metasequence "typename make_index_metasequence<N>::type"<br>
///         \ref fennec::make_index_metasequence_t "make_index_metasequence_t<N>"
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::index_metasequence
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
///         \ref fennec::concat_metasequence "typename concat_metasequence<metasequenceT0, metasequenceT1>::type"<br>
///         \ref fennec::concat_metasequence_t "concat_metasequence_t<metasequenceT0, metasequenceT1>"<br>
///     <td width="50%" style="vertical-align: top">
///         \copydetails fennec::concat_metasequence
///
/// </table>
///

#include <fennec/lang/type_traits.h>

namespace fennec
{

// fennec::metasequence ====================================================================================================

///
/// \brief metaprogramming sequence
///
/// \details Stores a sequence of values of type \emph{ValueT} as a template pack.
///          You can access the parameter pack in another template function, i.e.
///          \code{cpp}
///          template<typename TypeT, TypeT...Values>
///          constexpr TypeT summation(metasequence<TypeT, Values...>)
///          {
///              return (Values + ...);
///          }
///          \endcode
/// \tparam ValueT type of the values
/// \tparam Values sequence values
template<typename ValueT, ValueT...Values> struct metasequence
{
	/// \brief type of the metasequence
	using value_type = ValueT;

	/// \brief self-referential type
	using type = metasequence;

	///
	/// \brief returns the number of elements
	///
	/// \return number of elements in the array
	inline static constexpr size_t size() noexcept {
		return sizeof...(Values);
	}
};



// fennec::integer_metasequence ============================================================================================

///
/// \brief metaprogramming integral metasequence
///
/// \details A `fennec::metasequence` specialized integer types.
/// \tparam IntT type of the values, must satisfy `fennec::is_integral<T>`
/// \tparam Values sequence values
template<typename IntT, IntT...Values> requires(is_integral_v<IntT>)
struct integer_metasequence : metasequence<IntT, Values...>
{
	/// \brief type of the sequence
	using value_type = IntT;

	/// \brief self-referential type
	using type = integer_metasequence;

	///
	/// \brief returns the number of elements
	///
	/// \return number of elements in the array
	inline static constexpr size_t size() noexcept {
		return sizeof...(Values);
	}
};


///
/// \brief generate a fennec::integer_metasequence \math{\left[\,0\,\ldots\,N\,\right)}
///
/// \details
/// \tparam IntT type of the values, must satisfy `fennec::is_integral<T>`
/// \tparam N size of the metasequence to generate
template<typename IntT, size_t N> struct make_integer_metasequence;

///
/// \brief shorthand for `typename fennec::make_integer_sequence<T, N>::type`
template<typename IntT, size_t N> using  make_integer_metasequence_t = typename make_integer_metasequence<IntT, N>::type;



// fennec::index_metasequence ==============================================================================================

///
/// \brief metaprogramming integral metasequence
///
/// \details A `fennec::integer_metasequence` specialized for sequences of `size_t` indices.
/// \tparam Indices sequence values
template<size_t...Indices> struct index_metasequence : integer_metasequence<size_t, Indices...>
{
	/// \brief type of the sequence
	using value_type = size_t;

	/// \brief self-referential type
	using type = index_metasequence;

	///
	/// \brief returns the number of elements
	///
	/// \return number of elements in the array
	inline static constexpr size_t size() noexcept {
		return sizeof...(Indices);
	}
};


///
/// \brief generate a fennec::index_metasequence \math{\left[\,0\,\ldots\,N\,\right)}
///
/// \details
/// \tparam T type of the values, must satisfy `fennec::is_integral<T>`
/// \tparam N size of the sequence to generate
template<size_t N> struct make_index_metasequence;

///
/// \brief shorthand for `typename fennec::make_index_metasequence<N>::type`
template<size_t N> using  make_index_metasequence_t = typename make_index_metasequence<N>::type;



// fennec::concat_metasequence =============================================================================================

///
/// \brief concatenate two metasequences
///
/// \details A tool for concatenating two `fennec::metasequence` types.
/// \tparam SequenceT0 lhs
/// \tparam SequenceT1 rhs
template<typename SequenceT0, typename SequenceT1> struct concat_metasequence;

///
/// \brief shorthand for `typename fennec::concat_metasequence<SequenceT0, SequenceT1>::type`
template<typename SequenceT0, typename SequenceT1> using concat_metasequence_t
	= typename concat_metasequence<SequenceT0, SequenceT1>::type;



// Internal ============================================================================================================

// Implementation for Generating an integer_sequence
template<typename T, size_t N> struct make_integer_metasequence : concat_metasequence_t<make_integer_metasequence_t<T, N / 2>, make_integer_metasequence_t<T, N - N / 2>>{};

// Base Case of N=0
template<typename T> struct make_integer_metasequence<T, 0> : integer_metasequence<T> {};

// Base Case of N=1
template<typename T> struct make_integer_metasequence<T, 1> : integer_metasequence<T, 0>{};


// Implementation for Generating an index_sequence
template<size_t N> struct make_index_metasequence : concat_metasequence_t<make_index_metasequence_t<N / 2>, make_index_metasequence_t<N - N / 2>>{};

// Base Case of N=0
template<> struct make_index_metasequence<0> : index_metasequence<> {};

// Base Case of N=1
template<> struct make_index_metasequence<1> : index_metasequence<0>{};


// Specialization for integer sequences
template<typename T, T...SequenceV0, T...SequenceV1>
struct concat_metasequence<integer_metasequence<T, SequenceV0...>, integer_metasequence<T, SequenceV1...>>
	: integer_metasequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};

// Specialization for index sequences
template<size_t...SequenceV0, size_t...SequenceV1>
struct concat_metasequence<index_metasequence<SequenceV0...>, index_metasequence<SequenceV1...>>
	: index_metasequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};



}

#endif // FENNEC_LANG_SEQUENCES_H
