/*=============================================================================
    Copyright (c) 2011 Eric Niebler

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#if !defined(BOOST_FUSION_SEGMENTED_ITERATOR_NEXT_IMPL_HPP_INCLUDED)
#define BOOST_FUSION_SEGMENTED_ITERATOR_NEXT_IMPL_HPP_INCLUDED

#include <boost/fusion/support/config.hpp>
#include <boost/type_traits/add_const.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/fusion/iterator/equal_to.hpp>
#include <boost/fusion/container/list/cons_fwd.hpp>
#include <boost/fusion/iterator/next.hpp>
#include <boost/fusion/iterator/deref.hpp>

namespace boost {
namespace fusion {
template <typename First, typename Second>
struct iterator_range;

template <typename Context>
struct segmented_iterator;

namespace detail {
template <typename Sequence, typename Stack>
struct segmented_begin_impl;

//bool is_invalid(stack)
//{
//  return empty(car(stack));
//}

template <typename Stack>
struct is_invalid
    : result_of::equal_to<
      typename Stack::car_type::begin_type,
      typename Stack::car_type::end_type
      >
{};

////Advance the first iterator in the seq at the
////top of a stack of iterator ranges. Return the
////new stack.
//auto pop_front_car(stack)
//{
//  return cons(iterator_range(next(begin(car(stack))), end(car(stack))), cdr(stack));
//}

template <typename Stack>
struct pop_front_car
{
    typedef
    iterator_range<
    typename result_of::next<
    typename Stack::car_type::begin_type
    >::type
    , typename Stack::car_type::end_type
    >
    car_type;

    typedef
    cons<car_type, typename Stack::cdr_type>
    type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return type(
                   car_type(fusion::next(stack.car.first), stack.car.last),
                   stack.cdr);
    }
};

template <
    typename Stack,
    typename Next   = typename pop_front_car<Stack>::type,
    bool IsInvalid  = is_invalid<Next>::value,
    int StackSize   = Stack::size::value>
struct segmented_next_impl_recurse;

// Handle the case where the top of the stack has no usable
//auto segmented_next_impl_recurse3(stack)
//{
//  if (size(stack) == 1)
//    return cons(iterator_range(end(car(stack)), end(car(stack))), nil_);
//  else
//    return segmented_next_impl_recurse(stack.cdr);
//}

template <
    typename Stack,
    int StackSize = Stack::size::value>
struct segmented_next_impl_recurse3
{
    typedef segmented_next_impl_recurse<typename Stack::cdr_type> impl;
    typedef typename impl::type type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return impl::call(stack.cdr);
    }
};

template <typename Stack>
struct segmented_next_impl_recurse3<Stack, 1>
{
    typedef typename Stack::car_type::end_type end_type;
    typedef iterator_range<end_type, end_type> range_type;
    typedef cons<range_type> type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return type(range_type(stack.car.last, stack.car.last));
    }
};

//auto segmented_next_impl_recurse2(stack)
//{
//  auto res = segmented_begin_impl(front(car(stack)), stack);
//  if (is_invalid(res))
//    return segmented_next_impl_recurse3(stack);
//  else
//    return res;
//}

template <
    typename Stack,
    typename Sequence  =
    typename remove_reference<
        typename add_const<
            typename result_of::deref<
                typename Stack::car_type::begin_type
                >::type
            >::type
        >::type,
    typename Result =
    typename segmented_begin_impl<Sequence, Stack>::type,
    bool IsInvalid  =
    is_invalid<Result>::value>
struct segmented_next_impl_recurse2
{
    typedef segmented_next_impl_recurse3<Stack> impl;
    typedef typename impl::type type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return impl::call(stack);
    }
};

template <typename Stack, typename Sequence, typename Result>
struct segmented_next_impl_recurse2<Stack, Sequence, Result, false>
{
    typedef Result type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return segmented_begin_impl<Sequence, Stack>::call(*stack.car.first, stack);
    }
};

//auto segmented_next_impl_recurse(stack)
//{
//  auto next = pop_front_car(stack);
//  if (is_invalid(next))
//    if (1 == size(stack))
//      return next;
//    else
//      return segmented_next_impl_recurse(cdr(stack));
//  else
//    return segmented_next_impl_recurse2(next)
//}

template <typename Stack, typename Next, bool IsInvalid, int StackSize>
struct segmented_next_impl_recurse
{
    typedef
    typename segmented_next_impl_recurse<typename Stack::cdr_type>::type
    type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const& stack)
    {
        return segmented_next_impl_recurse<typename Stack::cdr_type>::call(stack.cdr);
    }
};

template <typename Stack, typename Next>
struct segmented_next_impl_recurse<Stack, Next, true, 1>
{
    typedef Next type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return pop_front_car<Stack>::call(stack);
    }
};

template <typename Stack, typename Next, int StackSize>
struct segmented_next_impl_recurse<Stack, Next, false, StackSize>
{
    typedef segmented_next_impl_recurse2<Next> impl;
    typedef typename impl::type type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return impl::call(pop_front_car<Stack>::call(stack));
    }
};

//auto segmented_next_impl(stack)
//{
//  // car(stack) is a seq of values, not a seq of segments
//  auto next = pop_front_car(stack);
//  if (is_invalid(next))
//    return segmented_next_impl_recurse(cdr(next));
//  else
//    return next;
//}

template <
    typename Stack,
    typename Next   = typename pop_front_car<Stack>::type,
    bool IsInvalid  = is_invalid<Next>::value>
struct segmented_next_impl_aux
{
    typedef segmented_next_impl_recurse<typename Stack::cdr_type> impl;
    typedef typename impl::type type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return impl::call(stack.cdr);
    }
};

template <typename Stack, typename Next>
struct segmented_next_impl_aux<Stack, Next, false>
{
    typedef Next type;

    BOOST_CONSTEXPR BOOST_FUSION_GPU_ENABLED
    static type
    call(Stack const & stack)
    {
        return pop_front_car<Stack>::call(stack);
    }
};

template <typename Stack>
struct segmented_next_impl
    : segmented_next_impl_aux<Stack>
{};
}
}
}

#endif
