/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2018 Danny Robson <danny@nerdcruft.net>
 */

#ifndef CRUFT_UTIL_TUPLE_INDEX_HPP
#define CRUFT_UTIL_TUPLE_INDEX_HPP

#include <cstddef>

///////////////////////////////////////////////////////////////////////////
// the implementation of all the below must be standalone as they are a
// potential building block in other algorithms and we can't be sure we
// won't create a dependency loop somewhere.
namespace cruft::tuple::index {
    ///////////////////////////////////////////////////////////////////////////
    template <size_t ...Indices>
    struct indices {};


    ///////////////////////////////////////////////////////////////////////////
    template <typename A, typename B>
    struct cat;

    //-------------------------------------------------------------------------
    template <
        size_t ...A,
        size_t ...B
    >
    struct cat<
        indices<A...>,
        indices<B...>
    > {
        using type = indices<A..., B...>;
    };


    //-------------------------------------------------------------------------
    template <typename A, typename B>
    using cat_t = typename cat<A,B>::type;


    ///////////////////////////////////////////////////////////////////////////
    namespace detail {
        template <size_t N>
        struct make_indices {
            using type = cat_t<
                typename make_indices<N-1>::type,
                indices<N-1>
            >;
        };


        template <>
        struct make_indices<0> {
            using type = indices<>;
        };
    };

    //-------------------------------------------------------------------------
    // create a monotonically increasing index sequence over the range [0,N)
    template <size_t N>
    struct make_indices {
        using type = typename detail::make_indices<N>::type;
    };

    //-------------------------------------------------------------------------
    template <size_t N>
    using make_indices_t = typename make_indices<N>::type;


    ///////////////////////////////////////////////////////////////////////////
    namespace detail {
        template <typename Indices>
        struct make_reverse;

        template <size_t Head, size_t ...Tail>
        struct make_reverse<
            indices<Head,Tail...>
        > {
            using type = cat_t<
                typename make_reverse<indices<Tail...>>::type,
                indices<Head>
            >;
        };


        template <>
        struct make_reverse<
            indices<>
        > {
            using type = indices<>;
        };
    };


    //-------------------------------------------------------------------------
    // create a montonically decreasing index sequence over the range (N-1, 0].
    //
    // eg, make_reverse<3> is indices<3,2,1,0>
    template <size_t N>
    struct make_reverse {
        using type = typename detail::make_reverse<
            make_indices_t<N>
        >::type;
    };


    //-------------------------------------------------------------------------
    template <size_t N>
    using make_reverse_t = typename make_reverse<N>::type;
};


#endif