/// \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_VIEW_COUNTED_HPP
#define RANGES_V3_VIEW_COUNTED_HPP

#include <utility>

#include <range/v3/range_fwd.hpp>

#include <range/v3/iterator/concepts.hpp>
#include <range/v3/iterator/counted_iterator.hpp>
#include <range/v3/iterator/default_sentinel.hpp>
#include <range/v3/iterator/traits.hpp>
#include <range/v3/utility/static_const.hpp>
#include <range/v3/view/interface.hpp>
#include <range/v3/view/subrange.hpp>

namespace ranges
{
    /// \addtogroup group-views
    /// @{
    template<typename I>
    struct counted_view : view_interface<counted_view<I>, finite>
    {
    private:
        friend range_access;
        I it_;
        iter_difference_t<I> n_;

    public:
        counted_view() = default;
        counted_view(I it, iter_difference_t<I> n)
          : it_(it)
          , n_(n)
        {
            RANGES_EXPECT(0 <= n_);
        }
        counted_iterator<I> begin() const
        {
            return make_counted_iterator(it_, n_);
        }
        default_sentinel_t end() const
        {
            return {};
        }
        auto size() const
        {
            return static_cast<detail::iter_size_t<I>>(n_);
        }
    };

#if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
    template<typename I>
    counted_view(I, iter_difference_t<I>)->counted_view<I>;
#endif

    namespace views
    {
        struct cpp20_counted_fn
        {
            template<typename I>
            auto operator()(I it, iter_difference_t<I> n) const
                -> CPP_ret(subrange<counted_iterator<I>, default_sentinel_t>)( //
                    requires input_or_output_iterator<I> && (!random_access_iterator<I>))
            {
                return {make_counted_iterator(std::move(it), n), default_sentinel};
            }
            template<typename I>
            auto operator()(I it, iter_difference_t<I> n) const
                -> CPP_ret(subrange<I>)( //
                    requires random_access_iterator<I>)
            {
                return {it, it + n};
            }
        };

        struct counted_fn
        {
            template<typename I>
            auto operator()(I it, iter_difference_t<I> n) const
                -> CPP_ret(counted_view<I>)( //
                    requires input_or_output_iterator<I> && (!random_access_iterator<I>))
            {
                return {std::move(it), n};
            }
            template<typename I>
            auto operator()(I it, iter_difference_t<I> n) const
                -> CPP_ret(subrange<I>)( //
                    requires random_access_iterator<I>)
            {
                return {it, it + n};
            }
        };

        /// \relates counted_fn
        /// \ingroup group-views
        RANGES_INLINE_VARIABLE(counted_fn, counted)
    } // namespace views

    namespace cpp20
    {
        namespace views
        {
            RANGES_INLINE_VARIABLE(ranges::views::cpp20_counted_fn, counted)
        }
    } // namespace cpp20
    /// @}
} // namespace ranges

#include <range/v3/detail/satisfy_boost_range.hpp>
RANGES_SATISFY_BOOST_RANGE(::ranges::counted_view)

#endif