/// \file
// Range v3 library
//
//  Copyright Eric Niebler 2014-present
//
//  Use, modification and distribution is subject to 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)
//
// Project home: https://github.com/ericniebler/range-v3
//
#ifndef RANGES_V3_ITERATOR_MOVE_ITERATORS_HPP
#define RANGES_V3_ITERATOR_MOVE_ITERATORS_HPP

#include <cstddef>
#include <utility>

#include <range/v3/range_fwd.hpp>

#include <range/v3/iterator/basic_iterator.hpp>
#include <range/v3/iterator/concepts.hpp>
#include <range/v3/iterator/traits.hpp>

#include <range/v3/detail/prologue.hpp>

namespace ranges
{
    /// \addtogroup group-iterator
    /// @{
    template<typename I>
    struct move_iterator
    {
    private:
        CPP_assert(input_iterator<I>);
        I current_ = I{};

    public:
        using iterator_type = I;
        using difference_type = iter_difference_t<I>;
        using value_type = iter_value_t<I>;
        using iterator_category = std::input_iterator_tag;
        using reference = iter_rvalue_reference_t<I>;

        constexpr move_iterator() = default;
        explicit move_iterator(I i)
          : current_(i)
        {}
        template(typename O)(
            /// \pre
            requires convertible_to<O, I>)
        move_iterator(move_iterator<O> const & i)
          : current_(i.base())
        {}
        template(typename O)(
            /// \pre
            requires convertible_to<O, I>)
        move_iterator & operator=(move_iterator<O> const & i)
        {
            current_ = i.base();
            return *this;
        }
        I base() const
        {
            return current_;
        }
        // clang-format off
        auto CPP_auto_fun(operator*)()(const)
        (
            return iter_move(current_)
        )
            // clang-format on
            move_iterator &
            operator++()
        {
            ++current_;
            return *this;
        }
        CPP_member
        auto operator++(int) //
            -> CPP_ret(void)(
                /// \pre
                requires (!forward_iterator<I>))
        {
            ++current_;
        }
        CPP_member
        auto operator++(int) //
            -> CPP_ret(move_iterator)(
                /// \pre
                requires forward_iterator<I>)
        {
            return move_iterator(current_++);
        }
        CPP_member
        auto operator--() //
            -> CPP_ret(move_iterator &)(
                /// \pre
                requires forward_iterator<I>)
        {
            --current_;
            return *this;
        }
        CPP_member
        auto operator--(int) //
            -> CPP_ret(move_iterator)(
                /// \pre
                requires bidirectional_iterator<I>)
        {
            return move_iterator(current_--);
        }
        CPP_member
        auto operator+(difference_type n) const //
            -> CPP_ret(move_iterator)(
                /// \pre
                requires random_access_iterator<I>)
        {
            return move_iterator(current_ + n);
        }
        CPP_member
        auto operator+=(difference_type n)
            -> CPP_ret(move_iterator &)(
                /// \pre
                requires random_access_iterator<I>)
        {
            current_ += n;
            return *this;
        }
        CPP_member
        auto operator-(difference_type n) const //
            -> CPP_ret(move_iterator)(
                /// \pre
                requires random_access_iterator<I>)
        {
            return move_iterator(current_ - n);
        }
        CPP_member
        auto operator-=(difference_type n) //
            -> CPP_ret(move_iterator &)(
                /// \pre
                requires random_access_iterator<I>)
        {
            current_ -= n;
            return *this;
        }
        CPP_member
        auto operator[](difference_type n) const //
            -> CPP_ret(reference)(
                /// \pre
                requires random_access_iterator<I>)
        {
            return iter_move(current_ + n);
        }

        template<typename I2>
        friend auto operator==(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires equality_comparable_with<I, I2>)
        {
            return x.base() == y.base();
        }
        template<typename I2>
        friend auto operator!=(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires equality_comparable_with<I, I2>)
        {
            return !(x == y);
        }
        template<typename I2>
        friend auto operator<(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires totally_ordered_with<I, I2>)
        {
            return x.base() < y.base();
        }
        template<typename I2>
        friend auto operator<=(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires totally_ordered_with<I, I2>)
        {
            return !(y < x);
        }
        template<typename I2>
        friend auto operator>(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires totally_ordered_with<I, I2>)
        {
            return y < x;
        }
        template<typename I2>
        friend auto operator>=(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires totally_ordered_with<I, I2>)
        {
            return !(x < y);
        }

        template<typename I2>
        friend auto operator-(move_iterator const & x, move_iterator<I2> const & y)
            -> CPP_broken_friend_ret(iter_difference_t<I2>)(
                /// \pre
                requires sized_sentinel_for<I, I2>)
        {
            return x.base() - y.base();
        }
        CPP_broken_friend_member
        friend auto operator+(iter_difference_t<I> n,
                              move_iterator const & x)
            -> CPP_broken_friend_ret(move_iterator)(
                /// \pre
                requires random_access_iterator<I>)
        {
            return x + n;
        }
    };

    struct make_move_iterator_fn
    {
        template(typename I)(
            /// \pre
            requires input_iterator<I>)
        constexpr move_iterator<I> operator()(I it) const
        {
            return move_iterator<I>{detail::move(it)};
        }
    };

    RANGES_INLINE_VARIABLE(make_move_iterator_fn, make_move_iterator)

    template<typename S>
    struct move_sentinel
    {
    private:
        S sent_;

    public:
        constexpr move_sentinel()
          : sent_{}
        {}
        constexpr explicit move_sentinel(S s)
          : sent_(detail::move(s))
        {}
        template(typename OS)(
            /// \pre
            requires convertible_to<OS, S>)
        constexpr explicit move_sentinel(move_sentinel<OS> const & that)
          : sent_(that.base())
        {}
        template(typename OS)(
            /// \pre
            requires convertible_to<OS, S>)
        move_sentinel & operator=(move_sentinel<OS> const & that)
        {
            sent_ = that.base();
            return *this;
        }
        S base() const
        {
            return sent_;
        }

        template<typename I>
        friend auto operator==(move_iterator<I> const & i, move_sentinel const & s)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires sentinel_for<S, I>)
        {
            return i.base() == s.base();
        }
        template<typename I>
        friend auto operator==(move_sentinel const & s, move_iterator<I> const & i)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires sentinel_for<S, I>)
        {
            return s.base() == i.base();
        }
        template<typename I>
        friend auto operator!=(move_iterator<I> const & i, move_sentinel const & s)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires sentinel_for<S, I>)
        {
            return i.base() != s.base();
        }
        template<typename I>
        friend auto operator!=(move_sentinel const & s, move_iterator<I> const & i)
            -> CPP_broken_friend_ret(bool)(
                /// \pre
                requires sentinel_for<S, I>)
        {
            return s.base() != i.base();
        }
    };

    struct make_move_sentinel_fn
    {
        template(typename I)(
            /// \pre
            requires input_iterator<I>)
        constexpr move_iterator<I> operator()(I i) const
        {
            return move_iterator<I>{detail::move(i)};
        }

        template(typename S)(
            /// \pre
            requires semiregular<S> AND (!input_iterator<S>)) //
        constexpr move_sentinel<S> operator()(S s) const
        {
            return move_sentinel<S>{detail::move(s)};
        }
    };

    RANGES_INLINE_VARIABLE(make_move_sentinel_fn, make_move_sentinel)

    /// \cond
    namespace detail
    {
        template<typename I, bool IsReadable>
        struct move_into_cursor_types_
        {};

        template<typename I>
        struct move_into_cursor_types_<I, true>
        {
            using value_type = iter_value_t<I>;
            using single_pass = meta::bool_<(bool)single_pass_iterator_<I>>;
        };

        template<typename I>
        using move_into_cursor_types =
            move_into_cursor_types_<I, (bool)indirectly_readable<I>>;

        template<typename I>
        struct move_into_cursor : move_into_cursor_types<I>
        {
        private:
            friend range_access;
            struct mixin : basic_mixin<move_into_cursor>
            {
                mixin() = default;
                #ifndef _MSC_VER
                using basic_mixin<move_into_cursor>::basic_mixin;
                #else
                constexpr explicit mixin(move_into_cursor && cur)
                  : basic_mixin<move_into_cursor>(static_cast<move_into_cursor &&>(cur))
                {}
                constexpr explicit mixin(move_into_cursor const & cur)
                  : basic_mixin<move_into_cursor>(cur)
                {}
                #endif
                explicit mixin(I it)
                  : mixin{move_into_cursor{std::move(it)}}
                {}
                I base() const
                {
                    return this->get().it_;
                }
            };

            I it_ = I();

            explicit move_into_cursor(I it)
              : it_(std::move(it))
            {}
            void next()
            {
                ++it_;
            }
            template(typename T)(
                /// \pre
                requires indirectly_writable<I, aux::move_t<T>>)
            void write(T && t) noexcept(noexcept(*it_ = std::move(t)))
            {
                *it_ = std::move(t);
            }
            template(typename T)(
                /// \pre
                requires indirectly_writable<I, aux::move_t<T>>)
            void write(T && t) const noexcept(noexcept(*it_ = std::move(t)))
            {
                *it_ = std::move(t);
            }
            CPP_member
            auto read() const noexcept(noexcept(*std::declval<I const &>()))
                -> CPP_ret(iter_reference_t<I>)(
                    /// \pre
                    requires indirectly_readable<I>)
            {
                return *it_;
            }
            CPP_member
            auto equal(move_into_cursor const & that) const //
                -> CPP_ret(bool)(
                    /// \pre
                    requires input_iterator<I>)
            {
                return it_ == that.it_;
            }
            CPP_member
            auto prev() //
                -> CPP_ret(void)(
                    /// \pre
                    requires bidirectional_iterator<I>)
            {
                --it_;
            }
            CPP_member
            auto advance(iter_difference_t<I> n) //
                -> CPP_ret(void)(
                    /// \pre
                    requires random_access_iterator<I>)
            {
                it_ += n;
            }
            CPP_member
            auto distance_to(move_into_cursor const & that) const //
                -> CPP_ret(iter_difference_t<I>)(
                    /// \pre
                    requires sized_sentinel_for<I, I>)
            {
                return that.it_ - it_;
            }
            template(typename II = I const)(
                /// \pre
                requires same_as<I const, II> AND indirectly_readable<II>)
            constexpr iter_rvalue_reference_t<II> move() const //
                noexcept(has_nothrow_iter_move_v<II>)
            {
                return iter_move(it_);
            }

        public:
            constexpr move_into_cursor() = default;
        };
    } // namespace detail
    /// \endcond

    struct move_into_fn
    {
        template<typename I>
        constexpr move_into_iterator<I> operator()(I it) const
        {
            return move_into_iterator<I>{std::move(it)};
        }
    };

    /// \sa `move_into_fn`
    RANGES_INLINE_VARIABLE(move_into_fn, move_into)

    namespace cpp20
    {
        using ranges::make_move_iterator;
        using ranges::move_iterator;
        using ranges::move_sentinel;
    } // namespace cpp20
    /// @}
} // namespace ranges

/// \cond
RANGES_DIAGNOSTIC_PUSH
RANGES_DIAGNOSTIC_IGNORE_MISMATCHED_TAGS

namespace std
{
    template<typename I>
    struct iterator_traits<::ranges::move_iterator<I>>
    {
        using iterator_category = std::input_iterator_tag;
        using difference_type = typename ::ranges::move_iterator<I>::difference_type;
        using value_type = typename ::ranges::move_iterator<I>::value_type;
        using reference = typename ::ranges::move_iterator<I>::reference;
        using pointer = meta::_t<std::add_pointer<reference>>;
    };
} // namespace std

RANGES_DIAGNOSTIC_POP
/// \endcond

#include <range/v3/detail/epilogue.hpp>

#endif // RANGES_V3_ITERATOR_MOVE_ITERATORS_HPP
