//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// Some basic examples of how split_view might be used in the wild. This is a general
// collection of sample algorithms and functions that try to mock general usage of
// this view.

// These test check the output `split_view` produces for a variety of inputs, including many corner cases, with no
// restrictions on which member functions can be called.

#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <map>
#include <ranges>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "test_macros.h"

template <std::ranges::view View, std::ranges::range Expected>
constexpr bool is_equal(View& view, const Expected& expected) {
  return std::ranges::equal(view, expected, std::ranges::equal);
}

template <class T, class Separator, class U, size_t M>
constexpr bool test_function_call(T&& input, Separator&& separator, std::array<U, M> expected) {
  std::ranges::split_view v(input, separator);
  return is_equal(v, expected);
}

template <class T, class Separator, class U, size_t M>
constexpr bool test_with_piping(T&& input, Separator&& separator, std::array<U, M> expected) {
  auto expected_it = expected.begin();
  for (auto e : input | std::ranges::views::split(separator)) {
    if (expected_it == expected.end())
      return false;
    if (!std::ranges::equal(e, *expected_it))
      return false;

    ++expected_it;
  }

  return expected_it == expected.end();
}

constexpr bool test_l_r_values() {
  using namespace std::string_view_literals;

  // Both lvalues and rvalues can be used as input.
  {
    // Lvalues.
    {
      auto input = "abc"sv;
      auto sep   = " "sv;
      [[maybe_unused]] std::ranges::split_view v(input, sep);
    }

    // Const lvalues.
    {
      const auto input = "abc"sv;
      const auto sep   = " "sv;
      [[maybe_unused]] std::ranges::split_view v(input, sep);
    }

    // Rvalues.
    {
      auto input = "abc"sv;
      auto sep   = " "sv;
      [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep));
    }

    // Const rvalues.
    {
      const auto input = "abc"sv;
      const auto sep   = " "sv;
      [[maybe_unused]] std::ranges::split_view v(std::move(input), std::move(sep));
    }
  }

  return true;
}

constexpr bool test_string_literal_separator() {
  using namespace std::string_view_literals;

  // Splitting works as expected when the separator is a single character literal.
  {
    std::ranges::split_view v("abc def"sv, ' ');
    assert(is_equal(v, std::array{"abc"sv, "def"sv}));
  }

  // Counterintuitively, a seemingly equivalent separator expressed as a string literal doesn't match anything. This is
  // because of the implicit terminating null in the literal.
  {
    std::ranges::split_view v("abc def"sv, " ");
    assert(is_equal(v, std::array{"abc def"sv}));
  }

  // To illustrate the previous point further, the separator is actually a two-character string literal: `{' ', '\0'}`.
  // Should the input string contain that two-character sequence, the separator would match.
  {
    std::ranges::split_view v("abc \0def"sv, " ");
    assert(is_equal(v, std::array{"abc"sv, "def"sv}));
  }

  return true;
}

// Make sure that a string literal and a `string_view` produce the same results (which isn't always the case, see
// below).
template <class T>
constexpr std::string_view sv(T&& str) {
  return std::string_view(str);
};

template <class T, class Separator, class U, size_t M>
constexpr void test_one(T&& input, Separator&& separator, std::array<U, M> expected) {
  assert(test_function_call(input, separator, expected));
  assert(test_with_piping(input, separator, expected));
}

constexpr bool test_string_literals() {
  // These tests show characteristic examples of how using string literals with `split_view` produces unexpected
  // results due to the implicit terminating null that is treated as part of the range.

  using namespace std::string_view_literals;

  char short_sep = ' ';
  auto long_sep  = "12"sv;

  // When splitting a string literal, only the last segment will be null-terminated (getting the terminating null from
  // the original range).
  {
    std::array expected = {"abc"sv, std::string_view("def", sizeof("def"))};

    assert(test_function_call("abc def", short_sep, expected));
    assert(test_with_piping("abc def", short_sep, expected));
    assert(test_function_call("abc12def", long_sep, expected));
    assert(test_with_piping("abc12def", long_sep, expected));
  }

  // Empty string.
  {
    // Because an empty string literal contains an implicit terminating null, the output will contain one segment.
    std::array expected = {std::string_view("", 1)};

    assert(test_function_call("", short_sep, expected));
    assert(test_with_piping("", short_sep, expected));
    assert(test_function_call("", long_sep, expected));
    assert(test_with_piping("", long_sep, expected));
  }

  // Terminating null in the separator -- the character literal `' '` and the seemingly equivalent string literal `" "`
  // are treated differently due to the presence of an implicit `\0` in the latter.
  {
    const char input[]          = "abc def";
    std::array expected_unsplit = {std::string_view(input, sizeof(input))};
    std::array expected_split   = {"abc"sv, std::string_view("def", sizeof("def"))};

    assert(test_function_call(input, " ", expected_unsplit));
    assert(test_function_call("abc \0def", " ", expected_split));
    // Note: string literals don't work with piping because arrays decay to pointers, and pointers don't model `range`.
  }

  // Empty separator.
  {
    auto empty_sep      = ""sv;
    std::array expected = {"a"sv, "b"sv, "c"sv, "\0"sv};

    assert(test_function_call("abc", empty_sep, expected));
    assert(test_with_piping("abc", empty_sep, expected));
  }

  return true;
}

bool test_nontrivial_characters() {
  // Try a deliberately heavyweight "character" type to see if it triggers any corner cases.

  using Map = std::map<std::string, int>;
  using Vec = std::vector<Map>;

  Map sep = {{"yyy", 999}};
  Map m1  = {
      {"a", 1},
      {"bc", 2},
  };
  Map m2 = {
      {"def", 3},
  };
  Map m3 = {
      {"g", 4},
      {"hijk", 5},
  };

  Vec expected1 = {m1, m2};
  Vec expected2 = {m3};

  std::ranges::split_view v(Vec{m1, m2, sep, m3}, sep);

  // Segment 1: {m1, m2}
  auto outer = v.begin();
  assert(outer != v.end());
  auto inner = (*outer).begin();
  assert(*inner++ == m1);
  assert(*inner++ == m2);
  assert(inner == (*outer).end());

  // Segment 2: {m3}
  ++outer;
  assert(outer != v.end());
  inner = (*outer).begin();
  assert(*inner++ == m3);
  assert(inner == (*outer).end());

  ++outer;
  assert(outer == v.end());

  return true;
}

constexpr bool main_test() {
  using namespace std::string_view_literals;

  char short_sep = ' ';
  auto long_sep  = "12"sv;

  // One separator.
  {
    std::array expected = {"abc"sv, "def"sv};
    test_one("abc def"sv, short_sep, expected);
    test_one("abc12def"sv, long_sep, expected);
  }

  // Several separators in a row.
  {
    std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv};
    test_one("abc    def"sv, short_sep, expected);
    test_one("abc12121212def"sv, long_sep, expected);
  }

  // Trailing separator.
  {
    std::array expected = {"abc"sv, "def"sv, ""sv};
    test_one("abc def "sv, short_sep, expected);
    test_one("abc12def12"sv, long_sep, expected);
  }

  // Leading separator.
  {
    std::array expected = {""sv, "abc"sv, "def"sv};
    test_one(" abc def"sv, short_sep, expected);
    test_one("12abc12def"sv, long_sep, expected);
  }

  // No separator.
  {
    std::array expected = {"abc"sv};
    test_one("abc"sv, short_sep, expected);
    test_one("abc"sv, long_sep, expected);
  }

  // Input consisting of a single separator.
  {
    std::array expected = {""sv, ""sv};
    test_one(" "sv, short_sep, expected);
    test_one("12"sv, long_sep, expected);
  }

  // Input consisting of only separators.
  {
    std::array expected = {""sv, ""sv, ""sv, ""sv};
    test_one("   "sv, short_sep, expected);
    test_one("121212"sv, long_sep, expected);
  }

  // The separator and the string use the same character only.
  {
    auto overlapping_sep = "aaa"sv;
    std::array expected  = {""sv, "aa"sv};
    test_one("aaaaa"sv, overlapping_sep, expected);
  }

  // Many redundant separators.
  {
    std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv};
    test_one("  abc   def  "sv, short_sep, expected);
    test_one("1212abc121212def1212"sv, long_sep, expected);
  }

  // Separators after every character.
  {
    std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv};
    test_one(" a b c "sv, short_sep, expected);
    test_one("12a12b12c12"sv, long_sep, expected);
  }

  // Overlap between the separator and the string (see https://wg21.link/lwg3505).
  {
    auto overlapping_sep = "ab"sv;
    std::array expected  = {"a"sv, "aa"sv, ""sv, "b"sv};
    test_one("aabaaababb"sv, overlapping_sep, expected);
  }

  // Empty input.
  {
    std::array<std::string_view, 0> expected = {};
    test_one(""sv, short_sep, expected);
    test_one(""sv, long_sep, expected);
  }

  // Empty separator.
  {
    auto empty_sep      = ""sv;
    std::array expected = {"a"sv, "b"sv, "c"sv};
    test_one("abc"sv, empty_sep, expected);
    test_one("abc"sv, empty_sep, expected);
  }

  // Terminating null as a separator.
  {
    std::array expected = {"abc"sv, "def"sv};
    test_one("abc\0def"sv, '\0', expected);
    test_one("abc\0\0def"sv, "\0\0"sv, expected);
  }

  // Different character types.
  {
    // `char`.
    test_function_call("abc def", ' ', std::array{"abc"sv, "def"sv});
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
    // `wchar_t`.
    test_function_call(L"abc def", L' ', std::array{L"abc"sv, L"def"sv});
#endif
    // `char8_t`.
    test_function_call(u8"abc def", u8' ', std::array{u8"abc"sv, u8"def"sv});
    // `char16_t`.
    test_function_call(u"abc def", u' ', std::array{u"abc"sv, u"def"sv});
    // `char32_t`.
    test_function_call(U"abc def", U' ', std::array{U"abc"sv, U"def"sv});
  }

  // Non-character input.
  {
    std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}};
    test_one(std::array{1, 2, 3, 0, 4, 5, 6}, 0, expected);
    test_one(std::array{1, 2, 3, 0, 0, 0, 4, 5, 6}, std::array{0, 0, 0}, expected);
  }

  return true;
}

constexpr bool example_test() {
  // example code in the spec
  std::string str{"the quick brown fox"};
  std::vector<std::string_view> result;
  for (auto r : std::views::split(str, ' ')) {
    result.emplace_back(r.begin(), r.end());
  }
  using namespace std::string_view_literals;
  auto expected = {"the"sv, "quick"sv, "brown"sv, "fox"sv};
  assert(std::ranges::equal(result, expected));

  return true;
}

int main(int, char**) {
  example_test();
  static_assert(example_test());

  test_string_literals();
  static_assert(test_string_literals());

  test_l_r_values();
  static_assert(test_l_r_values());

  test_string_literal_separator();
  static_assert(test_string_literal_separator());

  // Note: map is not `constexpr`, so this test is runtime-only.
  test_nontrivial_characters();

  return 0;
}
