/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_CTL_TUPLE_H
#define ECXX_CTL_TUPLE_H


#include <eCxx/CTL/Utility.h>


namespace eCxx {


//
// tuple<>
//
template <typename ValueT, typename... VariadicT>
struct tuple: public tuple<VariadicT...> {
    ValueT Value;

    constexpr tuple(ValueT value, VariadicT... variadic)
    : tuple<VariadicT...>(variadic...)
    , Value(value)
    {}
};

template <typename ValueT>
struct tuple<ValueT> {
    ValueT Value;

    constexpr tuple(ValueT value)
    : Value(value)
    {}

};


//
// tupleElement<>
//
template <size_t Index, typename ValueT>
struct tupleElement;

template <size_t Index, typename HeadT, typename... TailT>
struct tupleElement< Index, eCxx::tuple<HeadT, TailT...> > : eCxx::tupleElement< Index - 1 , eCxx::tuple<TailT...> > {};

template <typename HeadT, typename... TailT>
struct tupleElement< 0, eCxx::tuple<HeadT, TailT...> > { typedef HeadT Type; };


//
// makeTuple<>()
//
namespace internal {

    template <typename ValueT>
    struct UnwrapReferenceWrapper { typedef ValueT Type; };

    template <typename ValueT>
    struct UnwrapReferenceWrapper< eCxx::referenceWrapper<ValueT> > { typedef ValueT& Type; };

    template <typename ValueT>
    using URWDecay = UnwrapReferenceWrapper<typename eCxx::decay<ValueT>::Type>;

} // namespace internal

template <typename... ArgsT>
constexpr auto makeTuple(ArgsT&&... args)
    -> decltype( eCxx::tuple<typename internal::URWDecay<ArgsT>::Type...>( eCxx::forward<ArgsT>(args)... ) )
{ return eCxx::tuple<typename internal::URWDecay<ArgsT>::Type...>( eCxx::forward<ArgsT>(args)... ); }


//
// get<>()
//
namespace internal {

    template <size_t IndexN, typename ValueT, typename... VariadicT>
    struct get {
        static constexpr auto Value(const eCxx::tuple<ValueT, VariadicT...>* tuple) -> decltype( get<IndexN - 1, VariadicT...>::Value(tuple) )
        { return get<IndexN - 1, VariadicT...>::Value(tuple); }
    };

    template <typename ValueT, typename... VariadicT>
    struct get<0, ValueT, VariadicT...> {
        static constexpr ValueT Value(const eCxx::tuple<ValueT, VariadicT...>* tuple)
        { return tuple->Value; }
    };

} // namespace internal

template <size_t IndexN, typename ValueT, typename... VariadicT>
constexpr auto get(const eCxx::tuple<ValueT, VariadicT...>& tuple) -> decltype( internal::get<IndexN, ValueT, VariadicT...>::Value(&tuple) )
{ return internal::get<IndexN, ValueT, VariadicT...>::Value(&tuple); }


//
// templateArgNthIs<>
//
template <size_t N, typename ValueT, typename TemplateT>
struct templateArgNthIs : eCxx::falseType {};

template <size_t N, typename ValueT, template<typename...> class TemplateT, typename... TemplateArgs>
struct templateArgNthIs< N, ValueT, TemplateT<TemplateArgs...> > : eCxx::isSame< ValueT, typename eCxx::tupleElement< N, eCxx::tuple<TemplateArgs...> >::Type > {};


} // namespace eCxx


#endif
