#include <fsfw/container/ArrayList.h>
#include <fsfw/returnvalues/returnvalue.h>

#include <catch2/catch_test_macros.hpp>

#include "CatchDefinitions.h"

/**
 * @brief 	Array List test
 */
TEST_CASE("Array List", "[containers]") {
  // perform set-up here
  ArrayList<uint16_t> list(20);
  struct TestClass {
   public:
    TestClass() {};
    TestClass(uint32_t number1, uint64_t number2) : number1(number1), number2(number2) {};
    uint32_t number1 = -1;
    uint64_t number2 = -1;
    bool operator==(const TestClass& other) {
      return ((this->number1 == other.number1) and (this->number2 == other.number2));
    };
  };
  ArrayList<TestClass> complexList(20);
  SECTION("SimpleTest") {
    REQUIRE(list.maxSize() == 20);
    REQUIRE(list.size == 0);
    REQUIRE(list.insert(10) == static_cast<int>(returnvalue::OK));
    REQUIRE(list[0] == 10);
    REQUIRE(list.front() != nullptr);
    REQUIRE((*list.front()) == 10);
    REQUIRE(list.back() != nullptr);
    REQUIRE((*list.back()) == 10);
    // Need to test the const version of back as well
    const uint16_t* number = const_cast<const ArrayList<uint16_t>*>(&list)->back();
    REQUIRE(*number == 10);
    list.clear();
    REQUIRE(list.size == 0);
  }
  SECTION("Fill and check") {
    // This is an invalid element but its not a nullptr
    REQUIRE(list.back() != nullptr);
    for (auto i = 0; i < 20; i++) {
      REQUIRE(list.insert(i) == static_cast<int>(returnvalue::OK));
    }
    REQUIRE(list.insert(20) == static_cast<int>(containers::LIST_FULL));
    ArrayList<uint16_t>::Iterator it = list.begin();
    REQUIRE((*it) == 0);
    it++;
    REQUIRE((*it) == 1);
    it--;
    REQUIRE((*it) == 0);
    it++;
    for (auto it2 = list.begin(); it2 != list.end(); it2++) {
      if (it == it2) {
        REQUIRE((*it) == (*it2));
        break;
      } else {
        REQUIRE((*it2) == 0);
        REQUIRE(it2 != it);
      }
    }
  }
  SECTION("Const Iterator") {
    ArrayList<uint16_t>::Iterator it = list.begin();
    for (auto i = 0; i < 10; i++) {
      REQUIRE(list.insert(i) == static_cast<int>(returnvalue::OK));
    }
    it++;
    const uint16_t* number = it.value;
    REQUIRE(*number == 1);
  }

  SECTION("Const Iterator") {
    ArrayList<TestClass>::Iterator it = complexList.begin();
    for (auto i = 0; i < 10; i++) {
      REQUIRE(complexList.insert(TestClass(i, i + 1)) == static_cast<int>(returnvalue::OK));
    }
    it++;
    const TestClass* secondTest = it.value;
    bool compare = TestClass(1, 2) == *secondTest;
    REQUIRE(compare);
    it++;
    REQUIRE(it->number1 == 2);
    REQUIRE(it->number2 == 3);
    const ArrayList<TestClass>::Iterator it4(&(complexList[2]));
    REQUIRE(it4->number1 == 2);
    REQUIRE((*it4).number2 == 3);
    REQUIRE(complexList.remaining() == 10);
  }
}