//  Copyright (c) 2007-2016 Hartmut Kaiser
//  Copyright (c) 2016 Agustin Berge
//  Copyright (c) 2017 Anton Bikineev
//
//  SPDX-License-Identifier: BSL-1.0
//  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)

#pragma once

#include <pika/config.hpp>
#include <pika/concepts/has_member_xxx.hpp>
#include <pika/iterator_support/range.hpp>
#include <pika/iterator_support/traits/is_iterator.hpp>
#include <pika/iterator_support/traits/is_range.hpp>

#include <algorithm>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <vector>

namespace pika::traits::detail {

    ///////////////////////////////////////////////////////////////////////
    // not every random access sequence is reservable
    // so we need an explicit trait to determine this
    PIKA_HAS_MEMBER_XXX_TRAIT_DEF(reserve)

    template <typename Range>
    using is_reservable = std::integral_constant<bool,
        is_range_v<std::decay_t<Range>> && has_reserve_v<std::decay_t<Range>>>;

    template <typename Range>
    inline constexpr bool is_reservable_v = is_reservable<Range>::value;

    ///////////////////////////////////////////////////////////////////////
    template <typename Container>
    PIKA_FORCEINLINE void reserve_if_reservable(Container& v, std::size_t n)
    {
        if constexpr (is_reservable_v<Container>) { v.reserve(n); }
    }

    ///////////////////////////////////////////////////////////////////////
    // Reserve sufficient space in the given vector if the underlying
    // iterator type of the given range allow calculating the size in O(1).
    template <typename Container, typename Range>
    PIKA_FORCEINLINE void reserve_if_random_access_by_range(Container& v, Range const& r)
    {
        using iterator_type = typename range_traits<Range>::iterator_type;
        if constexpr (is_random_access_iterator_v<iterator_type> && is_reservable_v<Container>)
        {
            v.reserve(pika::util::size(r));
        }
    }

    template <typename Container, typename Iterator>
    PIKA_FORCEINLINE void
    reserve_if_random_access_by_range(Container& v, Iterator begin, Iterator end)
    {
        if constexpr (is_random_access_iterator_v<Iterator> && is_reservable_v<Container>)
        {
            v.reserve(std::distance(begin, end));
        }
    }
}    // namespace pika::traits::detail
