#include "IZ_list.h"

bool IZ_ListFindFilterAlwaysTrue(IZ_ListNode** _node, u64 _index, IZ_List* _list) {
	return true;
}

/**
 * Initializes a list.
 * @param list - The list to initialize.
 */
void IZ_ListInitialize(IZ_List* list) {
	list->root = NULL;
	list->length = 0;
	list->iterator = NULL;
}

/**
 * Performs cleanup on a list.
 * @param list - The list to clean up.
 */
void IZ_ListTeardown(IZ_List* list) {
	while (list->root) {
		IZ_ListDeleteFirstNode(list, IZ_ListFindFilterAlwaysTrue);
	}
	list->iterator = NULL;
	list->length = 0;
}

/**
 * Appends a node to the end of the list.
 * @param list - The list to append to.
 * @return Pointer to the newly created node.
 */
IZ_ListNode** IZ_ListAppendNode(IZ_List* list, void* node_value) {
	IZ_ListNode* new_node = SDL_malloc(sizeof(IZ_ListNode));
	new_node->value = node_value;
	new_node->next = NULL;
	list->length += 1;

	if (!(list->root)) {
		list->root = new_node;
		return &list->root;
	}

	IZ_ListNode *last_node = list->root;
	while (last_node->next) {
		last_node = last_node->next;
	}

	last_node->next = new_node;
	return &last_node->next;
}

/**
 * Deletes the first node in the list that matches the filter.
 * @param list - The list to delete from.
 * @param filter - The filter to use to find the node to delete.
 */
void IZ_ListDeleteFirstNode(IZ_List* list, IZ_ListFindPredicate filter) {
	if (!(list && list->root)) {
		// should we raise warnings here?
		return;
	}

	list->iterator = &list->root;
	IZ_ListNode* previous_node = NULL;
	u64 index = 0;
	do {
		if (!filter(list->iterator, index, list)) {
			previous_node = *list->iterator;
			list->iterator = &((*list->iterator)->next);
			index += 1;
			// we haven't found the node we're looking for, go to the next one
			continue;
		}

		if (previous_node) {
			// this is not the first node in the list
			previous_node->next = (*list->iterator)->next;
		} else {
			// this is the first node, set it as new root
			list->root = (*list->iterator)->next;
			// if there is only one item in the list, root, gets set to NULL
		}

		SDL_free(*list->iterator);
		list->iterator = NULL;
		if (list->length > 0) {
			// avoid underflow
			list->length -= 1;
		}
		return;
	} while (*list->iterator);
}

/**
 * Finds the first node in the list that matches the filter.
 * @param list - The list to search.
 * @param filter - The filter to use to find the node.
 * @return Pointer to the node that matches the filter.
 */
IZ_ListNode** IZ_ListFindFirstNode(IZ_List* list, IZ_ListFindPredicate filter) {
	if (!(list && list->root)) {
		return NULL;
	}

	list->iterator = &list->root;
	u64 index = 0;
	do {
		if (!filter(list->iterator, index, list)) {
			list->iterator = &((*list->iterator)->next);
			index += 1;
			continue;
		}
		return list->iterator;
	} while (*list->iterator);

	return NULL;
}
