/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

#include <catch2/catch_all.hpp>

#include "Base/Utils.hpp"

enum class AnEnum
{
	Zero = 0,
	One = 1,
	Max = 100
};

using namespace OpenVulkano;

TEST_CASE("EnumAsInt", "[Utils]")
{
	REQUIRE(Utils::EnumAsInt(AnEnum::Zero) == 0);
	REQUIRE(Utils::EnumAsInt(AnEnum::One) == 1);
	REQUIRE(Utils::EnumAsInt(AnEnum::Max) == 100);

	enum class EdgeEnum : int64_t
	{
		Min = INT64_MIN,
		Max = INT64_MAX
	};
	REQUIRE(Utils::EnumAsInt(EdgeEnum::Min) == INT64_MIN);
	REQUIRE(Utils::EnumAsInt(EdgeEnum::Max) == INT64_MAX);
}

TEST_CASE("Align", "[Utils]")
{
	REQUIRE(Utils::Align(1, 4) == 4);
	REQUIRE(Utils::Align(2, 4) == 4);
	REQUIRE(Utils::Align(3, 4) == 4);
	REQUIRE(Utils::Align(4, 4) == 4);
	REQUIRE(Utils::Align(5, 4) == 8);
	REQUIRE(Utils::Align(6, 4) == 8);
	REQUIRE(Utils::Align(7, 4) == 8);
	REQUIRE(Utils::Align(8, 4) == 8);

	REQUIRE(Utils::Align(0, 8) == 0);
	REQUIRE(Utils::Align(SIZE_MAX, 8) == 0); // it is natural for this condition due to overflow

	REQUIRE(Utils::Align(5, 1) == 5);
	REQUIRE(Utils::Align(123456789, 1) == 123456789);

	REQUIRE(Utils::Align(10, 16) == 16);
	REQUIRE(Utils::Align(15, 16) == 16);
}

TEST_CASE("AlignPage", "[Utils]")
{
	REQUIRE(Utils::AlignPage(1000) == 4096);
	REQUIRE(Utils::AlignPage(5000) == 8192);

	REQUIRE(Utils::AlignPage(0) == 0);
	REQUIRE(Utils::AlignPage(SIZE_MAX) == Utils::Align(SIZE_MAX, 4096));
}

TEST_CASE("IsPow2", "[Utils]")
{
	REQUIRE(Utils::IsPow2<uint32_t>(1));
	REQUIRE(Utils::IsPow2<uint32_t>(2));
	REQUIRE(Utils::IsPow2<uint32_t>(16));
	REQUIRE_FALSE(Utils::IsPow2<uint32_t>(6));

	REQUIRE(Utils::IsPow2<uint32_t>(0));
	REQUIRE(Utils::IsPow2<uint64_t>(1ULL << 63));
}

TEST_CASE("Log2OfPow2", "[Utils]")
{
	REQUIRE(Utils::Log2OfPow2<uint32_t>(1) == 0);
	REQUIRE(Utils::Log2OfPow2<uint32_t>(2) == 1);
	REQUIRE(Utils::Log2OfPow2<uint32_t>(16) == 4);

	REQUIRE(Utils::Log2OfPow2<uint64_t>(1ULL << 63) == 63);
}

TEST_CASE("OctToInt", "[Utils]")
{
	REQUIRE(Utils::OctToInt("7") == 7);
	REQUIRE(Utils::OctToInt("10") == 8);
	REQUIRE(Utils::OctToInt("1234") == 668);
	REQUIRE(Utils::OctToInt("777") == 511);
	REQUIRE(Utils::OctToInt("1000") == 512);

	REQUIRE(Utils::OctToInt("000") == 0);
	REQUIRE(Utils::OctToInt("07777") == 4095);

	REQUIRE(Utils::OctToInt("9") == -1);
	REQUIRE(Utils::OctToInt("A") == -1);
	REQUIRE(Utils::OctToInt("") == 0);

	REQUIRE(Utils::OctToInt("  1234   ") == 668);
}

TEST_CASE("StartsWith", "[Utils]")
{
	REQUIRE(Utils::StartsWith("abcdef", "abc"));
	REQUIRE(Utils::StartsWith("abcdef", "a"));
	REQUIRE(Utils::StartsWith("abcdef", ""));

	REQUIRE_FALSE(Utils::StartsWith("abcdef", "abcdz"));
	REQUIRE_FALSE(Utils::StartsWith("a", "aa"));
	REQUIRE_FALSE(Utils::StartsWith("", "a"));
	REQUIRE(Utils::StartsWith("abcdef", "abcdef"));
}

TEST_CASE("EndsWith", "[Utils]")
{
	REQUIRE(Utils::EndsWith("abcdef", "def"));
	REQUIRE(Utils::EndsWith("abcdef", "f"));
	REQUIRE(Utils::EndsWith("abcdef", ""));

	REQUIRE_FALSE(Utils::EndsWith("abcdef", "abcdeh"));
	REQUIRE_FALSE(Utils::EndsWith("a", "aa"));
	REQUIRE(Utils::EndsWith("abcdef", "abcdef"));
}

TEST_CASE("SplitAtLastOccurrence Extended", "[Utils]")
{
	auto result = Utils::SplitAtLastOccurrence("folder/file.txt", '/');
	REQUIRE(result.first == "folder");
	REQUIRE(result.second == "file.txt");

	result = Utils::SplitAtLastOccurrence("test.ext", '.');
	REQUIRE(result.first == "test");
	REQUIRE(result.second == "ext");

	result = Utils::SplitAtLastOccurrence("nochar", '/');
	REQUIRE(result.first == "nochar");
	REQUIRE(result.second == "");

	result = Utils::SplitAtLastOccurrence("/leading/slash", '/');
	REQUIRE(result.first == "/leading");
	REQUIRE(result.second == "slash");

	result = Utils::SplitAtLastOccurrence("/leadingonly/", '/');
	REQUIRE(result.first == "/leadingonly");
	REQUIRE(result.second == "");

	result = Utils::SplitAtLastOccurrence("onlyslash/", '/');
	REQUIRE(result.first == "onlyslash");
	REQUIRE(result.second == "");
}

TEST_CASE("Split", "[Utils]")
{
	std::vector<std::string> result;

	result = Utils::Split("a,b,c", ',');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	result = Utils::Split("one|two|three", '|');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "one");
	REQUIRE(result[1] == "two");
	REQUIRE(result[2] == "three");

	result = Utils::Split("", ',');
	REQUIRE(result.empty());

	result = Utils::Split("no separator here", ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "no separator here");

	result = Utils::Split("a,,b,,c", ',');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	result = Utils::Split(",a,b,c,", ',');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	result = Utils::Split(",,,,,", ',');
	REQUIRE(result.empty());

	result = Utils::Split("a", ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "a");

	result = Utils::Split(",,", ',');
	REQUIRE(result.empty());

	result = Utils::Split(",", ',');
	REQUIRE(result.empty());

	result = Utils::Split("a,", ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "a");
}

TEST_CASE("Split with non-null-terminated strings", "[Utils]")
{
	std::string_view input;
	std::vector<std::string> result;

	input = { "a,b,c|", 5 };
	result = Utils::Split(input, ',');

	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	input = { "one|two|three>", 13 };
	result = Utils::Split(input, '|');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "one");
	REQUIRE(result[1] == "two");
	REQUIRE(result[2] == "three");

	input = { "no separator here|", 17 };
	result = Utils::Split(input, ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "no separator here");

	input = { "a,,b,,c|", 7 };
	result = Utils::Split(input, ',');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	input = { ",a,b,c,|", 7 };
	result = Utils::Split(input, ',');
	REQUIRE(result.size() == 3);
	REQUIRE(result[0] == "a");
	REQUIRE(result[1] == "b");
	REQUIRE(result[2] == "c");

	input = { ",,,,|", 4 };
	result = Utils::Split(input, ',');
	REQUIRE(result.empty());

	input = { "a|", 1 };
	result = Utils::Split(input, ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "a");

	input = { "a,|", 2 };
	result = Utils::Split(input, ',');
	REQUIRE(result.size() == 1);
	REQUIRE(result[0] == "a");

	input = { ",,,|", 3 };
	result = Utils::Split(input, ',');
	REQUIRE(result.empty());
}