// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_RANGES_RANGES_H_
#define BASE_RANGES_RANGES_H_

#include <array>
#include <iterator>
#include <type_traits>
#include <utility>

#include "base/template_util.h"

namespace base {

namespace internal {

// Overload for C array.
template <typename T, size_t N>
constexpr T* begin(T (&array)[N], priority_tag<2>) {
  return array;
}

// Overload for mutable std::array. Required since std::array::begin is not
// constexpr prior to C++17. Needs to dispatch to the const overload since only
// const operator[] is constexpr in C++14.
#ifdef _MSC_VER
template <typename T, size_t N>
constexpr typename std::array<T, N>::iterator begin(std::array<T, N>& array, priority_tag<2> tag) {
  return array.begin();
}
#else
template <typename T, size_t N>
constexpr T* begin(std::array<T, N>& array, priority_tag<2> tag) {
  return const_cast<T*>(begin(const_cast<const std::array<T, N>&>(array), tag));
}
#endif
// Overload for const std::array. Required since std::array::begin is not
// constexpr prior to C++17.
#ifdef _MSC_VER
template <typename T, size_t N>
constexpr typename std::array<T, N>::const_iterator begin(const std::array<T, N>& array, priority_tag<2>) {
  return array.cbegin();
}
#else
template <typename T, size_t N>
constexpr const T* begin(const std::array<T, N>& array, priority_tag<2>) {
  return N != 0 ? &array[0] : nullptr;
}
#endif

// Generic container overload.
template <typename Range>
constexpr auto begin(Range&& range, priority_tag<1>)
    -> decltype(std::forward<Range>(range).begin()) {
  return std::forward<Range>(range).begin();
}

// Overload for free begin() function.
template <typename Range>
constexpr auto begin(Range&& range, priority_tag<0>)
    -> decltype(begin(std::forward<Range>(range))) {
  return begin(std::forward<Range>(range));
}

// Overload for C array.
template <typename T, size_t N>
constexpr T* end(T (&array)[N], priority_tag<2>) {
  return array + N;
}

// Overload for mutable std::array. Required since std::array::end is not
// constexpr prior to C++17. Needs to dispatch to the const overload since only
// const operator[] is constexpr in C++14.
#ifdef _MSC_VER
template <typename T, size_t N>
constexpr typename std::array<T, N>::iterator end(std::array<T, N>& array, priority_tag<2> tag) {
  return array.end();
}
#else
template <typename T, size_t N>
constexpr T* end(std::array<T, N>& array, priority_tag<2> tag) {
  return const_cast<T*>(end(const_cast<const std::array<T, N>&>(array), tag));
}
#endif

// Overload for const std::array. Required since std::array::end is not
// constexpr prior to C++17.
#ifdef _MSC_VER
template <typename T, size_t N>
constexpr typename std::array<T, N>::const_iterator end(const std::array<T, N>& array, priority_tag<2>) {
  return array.cend();
}
#else
template <typename T, size_t N>
constexpr const T* end(const std::array<T, N>& array, priority_tag<2>) {
  return N != 0 ? (&array[0]) + N : nullptr;
}
#endif

// Generic container overload.
template <typename Range>
constexpr auto end(Range&& range, priority_tag<1>)
    -> decltype(std::forward<Range>(range).end()) {
  return std::forward<Range>(range).end();
}

// Overload for free end() function.
template <typename Range>
constexpr auto end(Range&& range, priority_tag<0>)
    -> decltype(end(std::forward<Range>(range))) {
  return end(std::forward<Range>(range));
}

}  // namespace internal

namespace ranges {

// Simplified implementation of C++20's std::ranges::begin.
// As opposed to std::ranges::begin, this implementation does does not check
// whether begin() returns an iterator and does not inhibit ADL.
//
// The trailing return type and dispatch to the internal implementation is
// necessary to be SFINAE friendly.
//
// Reference: https://wg21.link/range.access.begin
template <typename Range>
constexpr auto begin(Range&& range) noexcept
    -> decltype(internal::begin(std::forward<Range>(range),
                                internal::priority_tag<2>())) {
  return internal::begin(std::forward<Range>(range),
                         internal::priority_tag<2>());
}

// Simplified implementation of C++20's std::ranges::end.
// As opposed to std::ranges::end, this implementation does does not check
// whether end() returns an iterator and does not inhibit ADL.
//
// The trailing return type and dispatch to the internal implementation is
// necessary to be SFINAE friendly.
//
// Reference: - https://wg21.link/range.access.end
template <typename Range>
constexpr auto end(Range&& range) noexcept
    -> decltype(internal::end(std::forward<Range>(range),
                              internal::priority_tag<2>())) {
  return internal::end(std::forward<Range>(range), internal::priority_tag<2>());
}

// Implementation of C++20's std::ranges::iterator_t.
//
// Reference: https://wg21.link/ranges.syn#:~:text=iterator_t
template <typename Range>
using iterator_t = decltype(ranges::begin(std::declval<Range&>()));

// Implementation of C++20's std::ranges::range_value_t.
//
// Reference: https://wg21.link/ranges.syn#:~:text=range_value_t
template <typename Range>
using range_value_t = std::iter_value_t<iterator_t<Range>>;

}  // namespace ranges

}  // namespace base

#endif  // BASE_RANGES_RANGES_H_
