// =====================================================================================================================
//  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/containers/tuple.h
/// \brief A header containing the definition for a container with multiple values of differing types
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_TUPLE_H
#define FENNEC_CONTAINERS_TUPLE_H

#include <fennec/containers/detail/_tuple.h>
#include <fennec/lang/type_sequences.h>

namespace fennec
{

// TODO: Document

///
/// \brief Tuple, holds a collection of values of different types
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ⛔     |
/// | dynamic     |      ✅     |
/// | homogeneous |      ⛔     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ✅     |
/// | space       | \emph{O(N)} |
/// | access      | \emph{O(1)} |
/// | find        | \emph{O(1)} |
/// | insertion   |      ⛔     |
/// | deletion    |      ⛔     |
///
/// \tparam TypesT The types to store
template<typename...TypesT> struct tuple;

///
/// \brief tuple get
/// \tparam i the index
/// \tparam TypesT the types held in the tuple
/// \param x the tuple
/// \returns the \emph{i}th element of the tuple
template<size_t i, typename...TypesT>
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
	using elem_t = typename tuple<TypesT...>::template elem_t<i>;
	auto& it = *static_cast<detail::_tuple_leaf<i, elem_t>*>(&x);
	return it.value;
}

///
/// \tparam i the index
/// \tparam TypesT the types held in the tuple
/// \param x the tuple
/// \returns the \emph{i}th element of the tuple
template<size_t i, typename...TypesT>
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<TypesT...>& x) {
	using elem_t = typename tuple<TypesT...>::template elem_t<i>;
	const auto& it = *static_cast<const detail::_tuple_leaf<i, elem_t>*>(&x);
	return it.value;
}


template<typename ...TypesT>
struct tuple : public detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...> {

// Definitions =========================================================================================================
public:

	/// \name Definitions
	/// @{

	using base_t = detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...>; //!< the base type

	template<size_t i>
	using elem_t = typename nth_element<i, TypesT...>::type; //!< helper for getting the \emph{i}th element

	static constexpr size_t size = sizeof...(TypesT); //!< the number of elements held by the tuple

	/// @}


// Constructors & Destructor ===========================================================================================
public:

	///
	/// \brief tuple constructor
	/// \tparam ArgsT The element types
	/// \param args The arguments to initialize the tuple with
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	template<typename...ArgsT>
    tuple(ArgsT&&...args)
		: base_t(fennec::forward<ArgsT>(args)...) {
    }

	///
	/// \brief copy constructor
	/// \param cpy the tuple to copy
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	tuple(const tuple& cpy)
		: base_t(cpy) {
	}

	///
	/// \brief move constructor
	/// \param mov the tuple to move
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	tuple(tuple&& mov)
		: base_t(fennec::forward<tuple>(mov)) {
	}
};

///
/// \brief Helper for deducing the tuple constructor
/// \tparam TypesT the types of the tuple
/// \returns A new tuple containing the passed values
template<typename...TypesT>
tuple(TypesT...) -> tuple<TypesT...>;

}

#endif // FENNEC_CONTAINERS_TUPLE_H
