// clang-format off
#include <FIFO.hpp>
#include <CppUTest/CommandLineTestRunner.h>
// clang-format on

constexpr size_t TEST_QUEUE_SIZE = 32;

#define CHECK_QUEUE_EMPTY(queue_name)      \
	CHECK_TRUE(queue_name.isEmpty());      \
	CHECK_EQUAL(0, queue_name.getCount()); \
	CHECK_FALSE(queue_name.isFull());      \
	EXPECT_N_LEAKS(0);

#define CHECK_ONE_ELEMENT_INSIDE_THE_QUEUE(queue_name) \
	CHECK_EQUAL(1, queue_name.getCount());             \
	EXPECT_N_LEAKS(0);                                 \
	CHECK_FALSE(queue_name.isEmpty());                 \
	CHECK_FALSE(queue_name.isFull());

#define FILL_QUEUE(queue_name, queue_size)                                          \
	for(uint64_t val = 0; val < queue_size; val++)                                  \
	{                                                                               \
		FIFO::FIFORecord fill_record{.tickstamp = val, .message = {0, 2, 3, 4, 5}}; \
		bool enqueue_success = queue_name.enqueue(fill_record);                     \
		CHECK(enqueue_success);                                                     \
		CHECK_EQUAL(val + 1, queue_name.getCount());                                \
		CHECK_FALSE(queue_name.isEmpty());                                          \
	}

#define EMPTY_QUEUE(queue_size)                                     \
	for(uint64_t val = 0; val < queue_size; val++)                  \
	{                                                               \
		FIFO::FIFORecord read_record;                               \
		bool dequeue_success = test_fifo.dequeue(read_record);      \
		CHECK(dequeue_success);                                     \
		CHECK_EQUAL(31 - val, test_fifo.getCount());                \
		CHECK_EQUAL(val, read_record.tickstamp);                    \
		MEMCMP_EQUAL(test_record.message, read_record.message, 32); \
	}

#define CHECK_NOT_EMPTY_NOT_FULL(queue_name) \
	CHECK_FALSE(queue_name.isEmpty());       \
	CHECK_FALSE(queue_name.isFull());

#define CHECK_EMPTY_BUT_NOT_FULL(queue_name) \
	CHECK_TRUE(queue_name.isEmpty());        \
	CHECK_FALSE(queue_name.isFull());

TEST_GROUP(FIFOTestGroup){};

TEST(FIFOTestGroup, CreateAQueue)
{
	FIFO test_fifo("test_fifo");
	CHECK_QUEUE_EMPTY(test_fifo);
}

TEST(FIFOTestGroup, AddAndRemoveOneElementFromQueue)
{
	FIFO::FIFORecord test_record{.tickstamp = 0xDEADBEEF, .message = {1, 2}};
	FIFO test_fifo("test_fifo");

	bool success = test_fifo.enqueue(test_record);
	CHECK(success);
	CHECK_ONE_ELEMENT_INSIDE_THE_QUEUE(test_fifo);

	FIFO::FIFORecord read_record;
	success = test_fifo.dequeue(read_record);
	CHECK(success);
	CHECK_QUEUE_EMPTY(test_fifo);

	MEMCMP_EQUAL(test_record.message, read_record.message, 32);
}

TEST(FIFOTestGroup, FillAndRemoveManyElemetsFromTheQueue)
{
	FIFO::FIFORecord test_record{.tickstamp = 0xDEADBEEF, .message = {0, 2, 3, 4, 5}};
	FIFO test_fifo("test_fifo");
	FILL_QUEUE(test_fifo, TEST_QUEUE_SIZE);
	CHECK(test_fifo.isFull());

	EMPTY_QUEUE(TEST_QUEUE_SIZE);
	CHECK_EMPTY_BUT_NOT_FULL(test_fifo);
}

TEST(FIFOTestGroup, FillAndAddOneElementandThenRemoveTheElemetsFromTheQueue)
{
	FIFO::FIFORecord test_record{.tickstamp = 0xDEADBEEF, .message = {0, 2, 3, 4, 5}};

	FIFO test_fifo("test_fifo");

	FILL_QUEUE(test_fifo, TEST_QUEUE_SIZE);

	CHECK(test_fifo.isFull());

	bool success = test_fifo.enqueue(test_record);
	CHECK_FALSE(success);
	CHECK_EQUAL(32, test_fifo.getCount());
	CHECK_FALSE(test_fifo.isEmpty());
	CHECK(test_fifo.isFull());

	EMPTY_QUEUE(TEST_QUEUE_SIZE);
	CHECK_EMPTY_BUT_NOT_FULL(test_fifo);
}

TEST(FIFOTestGroup, TestingTheBoundryOfQueueDepth)
{
	FIFO::FIFORecord test_record{.tickstamp = 0xDFFFFFFFFFFFFFFF, .message = {1}};
	FIFO test_fifo("test_fifo");

	bool success = test_fifo.enqueue(test_record);
	CHECK(success);
	CHECK_ONE_ELEMENT_INSIDE_THE_QUEUE(test_fifo);

	FIFO::FIFORecord read_record;
	success = test_fifo.dequeue(read_record);
	CHECK(success);
	CHECK_QUEUE_EMPTY(test_fifo);
	CHECK_EQUAL(test_record.tickstamp, read_record.tickstamp);
	MEMCMP_EQUAL(test_record.message, read_record.message, 64);
}
