// Copyright (C) 2019 T. Zachary Laine
//
// 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)
#ifndef BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP
#define BOOST_STL_INTERFACES_REVERSE_ITERATOR_HPP

#include <boost/stl_interfaces/iterator_interface.hpp>


namespace boost { namespace stl_interfaces { inline namespace v1 {

    namespace v1_dtl {
        template<typename Iter>
        constexpr auto ce_dist(Iter f, Iter l, std::random_access_iterator_tag)
            -> decltype(l - f)
        {
            return l - f;
        }
        template<typename Iter, typename Tag>
        constexpr auto ce_dist(Iter f, Iter l, Tag)
            -> decltype(std::distance(f, l))
        {
            decltype(std::distance(f, l)) retval = 0;
            for (; f != l; ++f) {
                ++retval;
            }
            return retval;
        }

        template<typename Iter>
        constexpr Iter ce_prev(Iter it)
        {
            return --it;
        }

        template<typename Iter, typename Offset>
        constexpr void
        ce_adv(Iter & f, Offset n, std::random_access_iterator_tag)
        {
            f += n;
        }
        template<typename Iter, typename Offset, typename Tag>
        constexpr void ce_adv(Iter & f, Offset n, Tag)
        {
            if (0 < n) {
                for (Offset i = 0; i < n; ++i) {
                    ++f;
                }
            } else {
                for (Offset i = 0; i < -n; ++i) {
                    --f;
                }
            }
        }
    }

    /** This type is very similar to the C++20 version of
        `std::reverse_iterator`; it is `constexpr`-, `noexcept`-, and
        proxy-friendly. */
    template<typename BidiIter>
    struct reverse_iterator
        : iterator_interface<
              reverse_iterator<BidiIter>,
#if 201703L < __cplusplus && defined(__cpp_lib_ranges)
              typename v2::detail::iter_concept_t<BidiIter>,
#else
              typename std::iterator_traits<BidiIter>::iterator_category,
#endif
              typename std::iterator_traits<BidiIter>::value_type,
              typename std::iterator_traits<BidiIter>::reference,
              typename std::iterator_traits<BidiIter>::pointer,
              typename std::iterator_traits<BidiIter>::difference_type>
    {
        constexpr reverse_iterator() noexcept(noexcept(BidiIter())) : it_() {}
        constexpr reverse_iterator(BidiIter it) noexcept(
            noexcept(BidiIter(it))) :
            it_(it)
        {}
        template<
            typename BidiIter2,
            typename E = std::enable_if_t<
                std::is_convertible<BidiIter2, BidiIter>::value>>
        reverse_iterator(reverse_iterator<BidiIter2> const & it) : it_(it.it_)
        {}

        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR auto
        operator-(reverse_iterator lhs, reverse_iterator rhs) noexcept(
            noexcept(v1_dtl::ce_dist(
                lhs.it_,
                rhs.it_,
                typename std::iterator_traits<BidiIter>::iterator_category{})))
        {
            return -v1_dtl::ce_dist(
                rhs.it_,
                lhs.it_,
                typename std::iterator_traits<BidiIter>::iterator_category{});
        }

        constexpr typename std::iterator_traits<BidiIter>::reference
        operator*() const noexcept(
            noexcept(std::prev(v1_dtl::ce_prev(std::declval<BidiIter &>()))))
        {
            return *v1_dtl::ce_prev(it_);
        }

        constexpr reverse_iterator & operator+=(
            typename std::iterator_traits<BidiIter>::difference_type
                n) noexcept(noexcept(v1_dtl::
                                         ce_adv(
                                             std::declval<BidiIter &>(),
                                             -n,
                                             typename std::iterator_traits<
                                                 BidiIter>::
                                                 iterator_category{})))
        {
            v1_dtl::ce_adv(
                it_,
                -n,
                typename std::iterator_traits<BidiIter>::iterator_category{});
            return *this;
        }

        constexpr BidiIter base() const noexcept { return it_; }

    private:
        friend access;
        constexpr BidiIter & base_reference() noexcept { return it_; }
        constexpr BidiIter const & base_reference() const noexcept
        {
            return it_;
        }

        template<typename BidiIter2>
        friend struct reverse_iterator;

        BidiIter it_;
    };

    template<typename BidiIter>
    constexpr auto operator==(
        reverse_iterator<BidiIter> lhs,
        reverse_iterator<BidiIter>
            rhs) noexcept(noexcept(lhs.base() == rhs.base()))
        -> decltype(rhs.base() == lhs.base())
    {
        return lhs.base() == rhs.base();
    }

    template<typename BidiIter1, typename BidiIter2>
    constexpr auto operator==(
        reverse_iterator<BidiIter1> lhs,
        reverse_iterator<BidiIter2>
            rhs) noexcept(noexcept(lhs.base() == rhs.base()))
        -> decltype(rhs.base() == lhs.base())
    {
        return lhs.base() == rhs.base();
    }

    /** Makes a `reverse_iterator<BidiIter>` from an iterator of type
        `Bidiiter`. */
    template<typename BidiIter>
    auto make_reverse_iterator(BidiIter it)
    {
        return reverse_iterator<BidiIter>(it);
    }

}}}


#if 201703L < __cplusplus && defined(__cpp_lib_concepts) ||                    \
    defined(BOOST_STL_INTERFACES_DOXYGEN)

namespace boost { namespace stl_interfaces { namespace v2 {

    /** A template alias for `std::reverse_iterator`.  This only exists to
        make migration from Boost.STLInterfaces to C++20 easier; switch to the
        one in `std` as soon as you can. */
    template<typename BidiIter>
    using reverse_iterator = std::reverse_iterator<BidiIter>;


    /** Makes a `reverse_iterator<BidiIter>` from an iterator of type
        `Bidiiter`.  This only exists to make migration from
        Boost.STLInterfaces to C++20 easier; switch to the one in `std` as
        soon as you can. */
    template<typename BidiIter>
    auto make_reverse_iterator(BidiIter it)
    {
        return reverse_iterator<BidiIter>(it);
    }

}}}

#endif

#endif
