// Protocol Buffers - Google's data interchange format
// Copyright 2024 Google LLC.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include <iterator>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "hpb_generator/tests/child_model.hpb.h"
#include "hpb_generator/tests/test_extension.hpb.h"
#include "hpb_generator/tests/test_model.hpb.h"
#include "hpb/arena.h"
#include "hpb/ptr.h"
#include "hpb/requires.h"

namespace {

using ::hpb::internal::Requires;
using ::hpb_unittest::protos::ChildModel1;
using ::hpb_unittest::protos::TestModel;
using ::testing::ElementsAre;

const char kTestStr1[] = "abcdefg";
const char kTestStr2[] = "just another test string";

TEST(CppGeneratedCode, RepeatedMessages) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.child_models_size());
  // Should be able to clear repeated field when empty.
  test_model.mutable_child_models()->clear();
  EXPECT_EQ(0, test_model.child_models_size());
  // Add 2 children.
  auto new_child = test_model.add_child_models();
  EXPECT_EQ(true, new_child.ok());
  new_child.value()->set_child_str1(kTestStr1);
  new_child = test_model.add_child_models();
  EXPECT_EQ(true, new_child.ok());
  new_child.value()->set_child_str1(kTestStr2);
  EXPECT_EQ(2, test_model.child_models_size());
  // Mutable access.
  auto mutable_first = test_model.mutable_child_models(0);
  EXPECT_EQ(mutable_first->child_str1(), kTestStr1);
  mutable_first->set_child_str1("change1");
  auto mutable_second = test_model.mutable_child_models(1);
  EXPECT_EQ(mutable_second->child_str1(), kTestStr2);
  mutable_second->set_child_str1("change2");
  // Check mutations using views.
  auto view_first = test_model.child_models(0);
  EXPECT_EQ(view_first->child_str1(), "change1");
  auto view_second = test_model.child_models(1);
  EXPECT_EQ(view_second->child_str1(), "change2");
}

TEST(CppGeneratedCode, RepeatedScalar) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.value_array_size());
  // Should be able to clear repeated field when empty.
  test_model.mutable_value_array()->clear();
  EXPECT_EQ(0, test_model.value_array_size());
  // Add 2 children.
  EXPECT_EQ(true, test_model.add_value_array(5));
  EXPECT_EQ(true, test_model.add_value_array(6));
  EXPECT_EQ(2, test_model.value_array_size());
  EXPECT_EQ(5, test_model.value_array(0));
  EXPECT_EQ(6, test_model.value_array(1));
  EXPECT_EQ(true, test_model.resize_value_array(3));
  EXPECT_EQ(3, test_model.value_array_size());
  test_model.set_value_array(2, 7);
  EXPECT_EQ(5, test_model.value_array(0));
  EXPECT_EQ(6, test_model.value_array(1));
  EXPECT_EQ(7, test_model.value_array(2));
}

TEST(CppGeneratedCode, RepeatedFieldClear) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  test_model.mutable_value_array()->push_back(5);
  test_model.mutable_value_array()->push_back(16);
  test_model.mutable_value_array()->push_back(27);
  ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
  test_model.mutable_value_array()->clear();
  EXPECT_EQ(test_model.mutable_value_array()->size(), 0);
}

TEST(CppGeneratedCode, RepeatedFieldProxyForScalars) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.value_array().size());
  EXPECT_EQ(0, test_model.mutable_value_array()->size());

  test_model.mutable_value_array()->push_back(5);
  test_model.mutable_value_array()->push_back(16);
  test_model.mutable_value_array()->push_back(27);

  ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
  EXPECT_EQ((*test_model.mutable_value_array())[0], 5);
  EXPECT_EQ((*test_model.mutable_value_array())[1], 16);
  EXPECT_EQ((*test_model.mutable_value_array())[2], 27);

  const auto value_array = test_model.value_array();
  ASSERT_EQ(value_array.size(), 3);
  EXPECT_EQ(value_array[0], 5);
  EXPECT_EQ(value_array[1], 16);
  EXPECT_EQ(value_array[2], 27);

  EXPECT_THAT(value_array, ElementsAre(5, 16, 27));

  EXPECT_THAT(std::vector(value_array.begin(), value_array.end()),
              ElementsAre(5, 16, 27));
  EXPECT_THAT(std::vector(value_array.cbegin(), value_array.cend()),
              ElementsAre(5, 16, 27));
  EXPECT_THAT(std::vector(value_array.rbegin(), value_array.rend()),
              ElementsAre(27, 16, 5));
  EXPECT_THAT(std::vector(value_array.crbegin(), value_array.crend()),
              ElementsAre(27, 16, 5));
}

TEST(CppGeneratedCode, RepeatedScalarIterator) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  test_model.mutable_value_array()->push_back(5);
  test_model.mutable_value_array()->push_back(16);
  test_model.mutable_value_array()->push_back(27);
  int sum = 0;
  // Access by value.
  const ::hpb::RepeatedField<int32_t>::CProxy rep1 = test_model.value_array();
  for (auto i : rep1) {
    sum += i;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
  // Access by const reference.
  sum = 0;
  for (const auto& i : *test_model.mutable_value_array()) {
    sum += i;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
  // Access by forwarding reference.
  sum = 0;
  for (auto&& i : *test_model.mutable_value_array()) {
    sum += i;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
  // Test iterator operators.
  auto begin = test_model.value_array().begin();
  auto end = test_model.value_array().end();
  sum = 0;
  for (auto it = begin; it != end; ++it) {
    sum += *it;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
  auto it = begin;
  ++it;
  EXPECT_TRUE(begin < it);
  EXPECT_TRUE(begin <= it);
  it = end;
  EXPECT_TRUE(it == end);
  EXPECT_TRUE(it > begin);
  EXPECT_TRUE(it >= begin);
  EXPECT_TRUE(it != begin);
  // difference type
  it = end;
  --it;
  --it;
  EXPECT_EQ(end - it, 2);
  it = begin;
  EXPECT_EQ(it[0], 5);
  EXPECT_EQ(it[1], 16);
  EXPECT_EQ(it[2], 27);
  // ValueProxy.
  sum = 0;
  for (::hpb::RepeatedField<int32_t>::ValueCProxy c :
       test_model.value_array()) {
    sum += c;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
  sum = 0;
  for (::hpb::RepeatedField<int32_t>::ValueProxy c :
       *test_model.mutable_value_array()) {
    sum += c;
  }
  EXPECT_EQ(sum, 5 + 16 + 27);
}

TEST(CppGeneratedCode, RepeatedFieldProxyForStrings) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.repeated_string().size());
  EXPECT_EQ(0, test_model.mutable_repeated_string()->size());

  test_model.mutable_repeated_string()->push_back("a");
  test_model.mutable_repeated_string()->push_back("b");
  test_model.mutable_repeated_string()->push_back("c");

  ASSERT_EQ(test_model.repeated_string().size(), 3);
  EXPECT_EQ(test_model.repeated_string()[0], "a");
  EXPECT_EQ(test_model.repeated_string()[1], "b");
  EXPECT_EQ(test_model.repeated_string()[2], "c");

  EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "b", "c"));
  EXPECT_THAT(*test_model.mutable_repeated_string(),
              ElementsAre("a", "b", "c"));

  ASSERT_EQ(test_model.mutable_repeated_string()->size(), 3);
  EXPECT_EQ((*test_model.mutable_repeated_string())[0], "a");
  EXPECT_EQ((*test_model.mutable_repeated_string())[1], "b");
  EXPECT_EQ((*test_model.mutable_repeated_string())[2], "c");

  // The const accessor can't be used to modify the element
  EXPECT_FALSE((std::is_assignable<decltype(test_model.repeated_string()[1]),
                                   absl::string_view>::value));
  // But the mutable one is fine.
  (*test_model.mutable_repeated_string())[1] = "other";
  EXPECT_THAT(test_model.repeated_string(), ElementsAre("a", "other", "c"));

  test_model.mutable_repeated_string()->clear();
  EXPECT_EQ(test_model.mutable_repeated_string()->size(), 0);
}

TEST(CppGeneratedCode, RepeatedFieldProxyForMessages) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.child_models().size());
  ChildModel1 child1;
  child1.set_child_str1(kTestStr1);
  test_model.mutable_child_models()->push_back(child1);
  ChildModel1 child2;
  child2.set_child_str1(kTestStr2);
  test_model.mutable_child_models()->push_back(std::move(child2));

  int i = 0;
  for (auto child : test_model.child_models()) {
    EXPECT_FALSE(Requires<decltype(child)>(
        [](auto&& x) -> decltype(x.set_child_str1("")) {}));
    if (i++ == 0) {
      EXPECT_EQ(child.child_str1(), kTestStr1);
    } else {
      EXPECT_EQ(child.child_str1(), kTestStr2);
    }
  }

  i = 0;
  for (const auto& child : *test_model.mutable_child_models()) {
    if (i++ == 0) {
      EXPECT_EQ(child.child_str1(), kTestStr1);
    } else {
      EXPECT_EQ(child.child_str1(), kTestStr2);
    }
  }

  using testing::_;
  EXPECT_THAT(test_model.child_models(), ElementsAre(_, _));

  EXPECT_EQ(test_model.child_models().size(), 2);
  EXPECT_EQ(test_model.child_models()[0].child_str1(), kTestStr1);
  EXPECT_EQ(test_model.child_models()[1].child_str1(), kTestStr2);
  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), kTestStr1);
  EXPECT_EQ((*test_model.mutable_child_models())[1].child_str1(), kTestStr2);
  (*test_model.mutable_child_models())[0].set_child_str1("change1");
  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
  test_model.mutable_child_models()->clear();
  EXPECT_EQ(test_model.mutable_child_models()->size(), 0);
}

TEST(CppGeneratedCode, EmptyRepeatedFieldProxyForMessages) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.child_models().size());
  ChildModel1 child1;
  child1.set_child_str1(kTestStr1);

  EXPECT_EQ(test_model.child_models().size(), 0);
  EXPECT_EQ(std::distance(test_model.child_models().begin(),
                          test_model.child_models().end()),
            0);
}

TEST(CppGeneratedCode, EmptyRepeatedFieldScalarProxy) {
  hpb::Arena arena;
  hpb::Ptr<TestModel> test_model = hpb::CreateMessage<TestModel>(arena);

  EXPECT_EQ(std::distance(test_model->repeated_int64().begin(),
                          test_model->repeated_int64().end()),
            0);
}

TEST(CppGeneratedCode, RepeatedFieldProxyForMessagesIndexOperator) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.child_models().size());
  ChildModel1 child1;
  child1.set_child_str1(kTestStr1);
  test_model.mutable_child_models()->push_back(child1);
  ChildModel1 child2;

  child2.set_child_str1(kTestStr2);
  test_model.mutable_child_models()->push_back(std::move(child2));
  ASSERT_EQ(test_model.child_models().size(), 2);

  // test_model.child_models()[0].set_child_str1("change1");
  (*test_model.mutable_child_models())[0].set_child_str1("change1");
  EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
}

TEST(CppGeneratedCode, RepeatedStrings) {
  ::hpb::Arena arena;
  auto test_model = ::hpb::CreateMessage<TestModel>(arena);
  EXPECT_EQ(0, test_model.repeated_string_size());
  // Should be able to clear repeated field when empty.
  test_model.mutable_repeated_string()->clear();
  EXPECT_EQ(0, test_model.repeated_string_size());
  // Add 2 children.
  EXPECT_EQ(true, test_model.add_repeated_string("Hello"));
  EXPECT_EQ(true, test_model.add_repeated_string("World"));
  EXPECT_EQ(2, test_model.repeated_string_size());
  EXPECT_EQ("Hello", test_model.repeated_string(0));
  EXPECT_EQ("World", test_model.repeated_string(1));
  EXPECT_EQ(true, test_model.resize_repeated_string(3));
  EXPECT_EQ(3, test_model.repeated_string_size());
  test_model.set_repeated_string(2, "Test");
  EXPECT_EQ("Hello", test_model.repeated_string(0));
  EXPECT_EQ("World", test_model.repeated_string(1));
  EXPECT_EQ("Test", test_model.repeated_string(2));
}


}  // namespace
