#include "../../test/IZ_test.h"
#include "../../common/IZ_common.h"
#include "../../../__mocks__/SDL_stdinc.mock.h"
#include "IZ_list.h"

bool NodeExists(IZ_ListNode** node, u64 _index, IZ_List* list) {
	return *((u64*) (*node)->value) == 42069;
}

bool NodeExists2(IZ_ListNode** node, u64 _index, IZ_List* list) {
	return *((u64*) (*node)->value) == 69420;
}

bool NodeDoesNotExist(IZ_ListNode** node, u64 _index, IZ_List* list) {
	return *((u64*) (*node)->value) == 55555;
}

spec("data") {
	describe("list") {
		describe("Initialize") {
			static IZ_List list;

			after_each() {
				IZ_ListTeardown(&list);
			}

			it("sets root to NULL") {
				IZ_ListInitialize(&list);

				check(list.root == NULL, "List not properly initialized.");
			}
		}

		describe("Teardown") {
			static IZ_List list;

			before_each() {
				static u64 value1 = 69420u;
				static u64 value2 = 42069u;
				static u64 value3 = 69069u;

				IZ_ListInitialize(&list);
				IZ_ListAppendNode(&list, &value1);
				IZ_ListAppendNode(&list, &value2);
				IZ_ListAppendNode(&list, &value3);
			}

			after_each() {
				mock_reset(SDL_free);
			}

			it("removes all nodes from the list") {
				mock_set_expected_calls(SDL_free, 3);
				IZ_ListTeardown(&list);

				check(
					mock_get_expected_calls(SDL_free) == mock_get_actual_calls(SDL_free),
					"Deallocator function call count mismatch."
				);
			}
		}

		describe("AppendNode") {
			static IZ_List list;
			static IZ_List list2;

			before_each() {
				IZ_ListInitialize(&list);
			}

			before_each() {
				IZ_ListInitialize(&list2);
				static u64 existing_value = 69420u;
				static IZ_ListNode existing_node = {
					.value = &existing_value,
					.next = NULL,
				};
				list2.root = &existing_node;
				list2.length = 1;
			}

			after_each() {
				mock_reset(SDL_malloc);
			}

			after_each() {
				IZ_ListTeardown(&list);
			}

			after_each() {
				IZ_ListTeardown(&list2);
			}

			it("appends new node to empty list and sets it as root") {
				static u64 value = 69420u;
				IZ_ListAppendNode(&list, &value);

				check(*((u64*) list.root->value) == 69420u, "Node not properly appended.");
				check(
					mock_is_called(SDL_malloc),
					"Allocator function not called."
				);
				check(list.length == 1, "Length mismatch.");
			}

			it("appends new node to non-empty list") {
				static u64 value1 = 42069u;

				IZ_ListAppendNode(&list2, &value1);

				check(*((u64*) list2.root->next->value) == 42069u, "Node not properly appended.");
				check(
					mock_is_called(SDL_malloc),
					"Allocator function not called."
				);
				check(list2.length == 2, "Length mismatch.");
			}
		}

		describe("FindFirstNode") {
			static IZ_List list;
			static u64 value1 = 69420u;
			static u64 value2 = 42069u;

			before_each() {
				IZ_ListInitialize(&list);
				IZ_ListAppendNode(&list, &value1);
				IZ_ListAppendNode(&list, &value2);
			}

			after_each() {
				IZ_ListTeardown(&list);
			}

			it("retrieves first node satisfying the filter condition") {
				static IZ_ListNode** node;
				node = IZ_ListFindFirstNode(&list, NodeExists);
				check(*((u64*) (*node)->value) == 42069u, "Existing node not found.");
			}

			it("returns NULL when all nodes do not satisfy the filter condition") {
				static IZ_ListNode** node;
				node = IZ_ListFindFirstNode(&list, NodeDoesNotExist);
				check(node == NULL, "Non-existing node found.");
			}
		}

		describe("DeleteFirstNode") {
			static IZ_List list;

			before_each() {
				static u64 value1 = 69420u;
				static u64 value2 = 42069u;
				static u64 value3 = 69069u;

				IZ_ListInitialize(&list);
				IZ_ListAppendNode(&list, &value1);
				IZ_ListAppendNode(&list, &value2);
				IZ_ListAppendNode(&list, &value3);
			}

			after_each() {
				mock_reset(SDL_free);
			}

			it("removes first node satisfying the filter condition") {
				IZ_ListDeleteFirstNode(&list, NodeExists);

				check(
					mock_is_called(SDL_free),
					"Deallocator function not called."
				);

				check(
					IZ_ListFindFirstNode(&list, NodeExists2),
					"Node supposed to be present in list is absent."
				);

				check(
					!IZ_ListFindFirstNode(&list, NodeExists),
					"Deleted node still present in list."
				);

				check(list.length == 2, "Length mismatch.");
			}
		}
	}
}
