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

#include "base/containers/adapters.h"

#include <array>
#include <ranges>
#include <utility>
#include <vector>

#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class UnsizedVector {
 public:
  UnsizedVector() = default;
  UnsizedVector(std::initializer_list<int> il) : v_(il) {}

  // Sentinel iterator type that wraps `end()` to ensure this type doesn't
  // satisfy the `sized_range` concept.
  template <typename Iterator>
  class Sentinel {
   public:
    Sentinel() = default;
    explicit Sentinel(Iterator it) : wrapped_it_(it) {}

    bool operator==(Iterator it) const { return wrapped_it_ == it; }

   private:
    Iterator wrapped_it_;
  };

  auto begin() const { return v_.begin(); }
  auto end() const { return Sentinel(v_.end()); }
  auto rbegin() const { return v_.rbegin(); }
  auto rend() const { return Sentinel(v_.rend()); }
  auto begin() { return v_.begin(); }
  auto end() { return Sentinel(v_.end()); }
  auto rbegin() { return v_.rbegin(); }
  auto rend() { return Sentinel(v_.rend()); }

  auto operator[](size_t pos) const { return v_[pos]; }
  auto operator[](size_t pos) { return v_[pos]; }

 private:
  std::vector<int> v_;
};

[[maybe_unused]] void StaticAsserts() {
  {
    // Named local variables are more readable than std::declval<T>().
    std::vector<int> v;
    static_assert(std::ranges::range<decltype(base::Reversed(v))>);
    static_assert(std::ranges::sized_range<decltype(base::Reversed(v))>);
    // `base::Reversed()` takes a const ref to the vector, which is, by
    // definition, a borrowed range.
    static_assert(std::ranges::borrowed_range<decltype(base::Reversed(v))>);

    auto make_vector = [] { return std::vector<int>(); };
    static_assert(std::ranges::range<decltype(base::Reversed(make_vector()))>);
    static_assert(
        std::ranges::sized_range<decltype(base::Reversed(make_vector()))>);
    static_assert(
        !std::ranges::borrowed_range<decltype(base::Reversed(make_vector()))>);
  }

  {
    base::span<int> s;
    static_assert(std::ranges::range<decltype(base::Reversed(s))>);
    static_assert(std::ranges::sized_range<decltype(base::Reversed(s))>);
    static_assert(std::ranges::borrowed_range<decltype(base::Reversed(s))>);

    auto rvalue_span = [] { return base::span<int>(); };
    static_assert(std::ranges::range<decltype(base::Reversed(rvalue_span()))>);
    static_assert(
        std::ranges::sized_range<decltype(base::Reversed(rvalue_span()))>);
    static_assert(
        std::ranges::borrowed_range<decltype(base::Reversed(rvalue_span()))>);
  }

  {
    // A named local variable is more readable than std::declval<T>().
    UnsizedVector v;
    static_assert(std::ranges::range<decltype(v)>);
    static_assert(!std::ranges::sized_range<decltype(v)>);

    static_assert(std::ranges::range<decltype(base::Reversed(v))>);
    static_assert(!std::ranges::sized_range<decltype(base::Reversed(v))>);
    // `base::Reversed()` takes a const ref to the vector, which is, by
    // definition, a borrowed range.
    static_assert(std::ranges::borrowed_range<decltype(base::Reversed(v))>);

    auto make_vector = [] { return UnsizedVector(); };
    static_assert(std::ranges::range<decltype(base::Reversed(make_vector()))>);
    static_assert(
        !std::ranges::sized_range<decltype(base::Reversed(make_vector()))>);
    static_assert(!std::ranges::borrowed_range<decltype(make_vector())>);
  }
}

TEST(AdaptersTest, Reversed) {
  std::vector<int> v = {3, 2, 1};
  int j = 0;
  for (int& i : base::Reversed(v)) {
    EXPECT_EQ(++j, i);
    i += 100;
  }
  EXPECT_EQ(103, v[0]);
  EXPECT_EQ(102, v[1]);
  EXPECT_EQ(101, v[2]);
}

TEST(AdaptersTest, ReversedUnsized) {
  UnsizedVector v = {3, 2, 1};
  int j = 0;
  for (int& i : base::Reversed(v)) {
    EXPECT_EQ(++j, i);
    i += 100;
  }
  EXPECT_EQ(103, v[0]);
  EXPECT_EQ(102, v[1]);
  EXPECT_EQ(101, v[2]);
}

TEST(AdaptersTest, ReversedArray) {
  std::array<int, 3> v = {3, 2, 1};
  int j = 0;
  for (int& i : base::Reversed(v)) {
    EXPECT_EQ(++j, i);
    i += 100;
  }
  EXPECT_EQ(103, v[0]);
  EXPECT_EQ(102, v[1]);
  EXPECT_EQ(101, v[2]);
}

TEST(AdaptersTest, ReversedConst) {
  std::vector<int> v = {3, 2, 1};
  const std::vector<int>& cv = v;
  int j = 0;
  for (int i : base::Reversed(cv)) {
    EXPECT_EQ(++j, i);
  }
}

TEST(AdaptersTest, RangeAsRvalues) {
  std::vector<std::unique_ptr<int>> v;
  v.push_back(std::make_unique<int>(1));
  v.push_back(std::make_unique<int>(2));
  v.push_back(std::make_unique<int>(3));

  auto v2 = base::ToVector(base::RangeAsRvalues(std::move(v)));
  EXPECT_EQ(1, *v2[0]);
  EXPECT_EQ(2, *v2[1]);
  EXPECT_EQ(3, *v2[2]);

  // The old vector should be consumed. The standard guarantees that a
  // moved-from std::unique_ptr will be null.
  EXPECT_EQ(nullptr, v[0]);  // NOLINT(bugprone-use-after-move)
  EXPECT_EQ(nullptr, v[1]);  // NOLINT(bugprone-use-after-move)
  EXPECT_EQ(nullptr, v[2]);  // NOLINT(bugprone-use-after-move)
}

}  // namespace
