/*
	From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com )
	An experimental work-in-progress.

	Donated to Sonic Team Junior and adapted to work with
	Sonic Robo Blast 2. The license of this code matches whatever
	the licensing is for Sonic Robo Blast 2.
*/

#include "u_list.h"
#include "z_zone.h"

// Utility for managing
// structures in a linked
// list.
//
// Struct must have "next" and "prev" pointers
// as its first two variables.
//

//
// ListAdd
//
// Adds an item to the list
//
void ListAdd(void *pItem, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;

	if (*itemHead == NULL)
	{
		*itemHead = item;
		(*itemHead)->prev = (*itemHead)->next = NULL;
	}
	else
	{
		listitem_t *tail;
		tail = *itemHead;

		while (tail->next != NULL)
			tail = tail->next;

		tail->next = item;

		tail->next->prev = tail;

		item->next = NULL;
	}
}

//
// ListAddFront
//
// Adds an item to the front of the list
// (This is much faster)
//
void ListAddFront(void *pItem, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;

	if (*itemHead == NULL)
	{
		*itemHead = item;
		(*itemHead)->prev = (*itemHead)->next = NULL;
	}
	else
	{
		(*itemHead)->prev = item;
		item->next = (*itemHead);
		item->prev = NULL;
		*itemHead = item;
	}
}

//
// ListAddBefore
//
// Adds an item before the item specified in the list
//
void ListAddBefore(void *pItem, void *pSpot, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;
	listitem_t *spot = (listitem_t*)pSpot;

	listitem_t *prev = spot->prev;

	if (!prev)
		ListAddFront(pItem, itemHead);
	else
	{
		item->next = spot;
		spot->prev = item;
		item->prev = prev;
		prev->next = item;
	}
}

//
// ListAddAfter
//
// Adds an item after the item specified in the list
//
void ListAddAfter(void *pItem, void *pSpot, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;
	listitem_t *spot = (listitem_t*)pSpot;

	listitem_t *next = spot->next;

	if (!next)
		ListAdd(pItem, itemHead);
	else
	{
		item->prev = spot;
		spot->next = item;
		item->next = next;
		next->prev = item;
	}
}

//
// ListRemove
//
// Take an item out of the list and free its memory.
//
void ListRemove(void *pItem, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;

	if (item == *itemHead) // Start of list
	{
		*itemHead = item->next;

		if (*itemHead)
			(*itemHead)->prev = NULL;
	}
	else if (item->next == NULL) // end of list
	{
		item->prev->next = NULL;
	}
	else // Somewhere in between
	{
		item->prev->next = item->next;
		item->next->prev = item->prev;
	}

	Z_Free (item);
}

//
// ListRemoveAll
//
// Removes all items from the list, freeing their memory.
//
void ListRemoveAll(listitem_t **itemHead)
{
	listitem_t *item;
	listitem_t *next;
	for (item = *itemHead; item; item = next)
	{
		next = item->next;
		ListRemove(item, itemHead);
	}
}

//
// ListRemoveNoFree
//
// Take an item out of the list, but don't free its memory.
//
void ListRemoveNoFree(void *pItem, listitem_t **itemHead)
{
	listitem_t *item = (listitem_t*)pItem;

	if (item == *itemHead) // Start of list
	{
		*itemHead = item->next;

		if (*itemHead)
			(*itemHead)->prev = NULL;
	}
	else if (item->next == NULL) // end of list
	{
		item->prev->next = NULL;
	}
	else // Somewhere in between
	{
		item->prev->next = item->next;
		item->next->prev = item->prev;
	}
}

//
// ListGetCount
//
// Counts the # of items in a list
// Should not be used in performance-minded code
//
unsigned int ListGetCount(void *itemHead)
{
	listitem_t *item = (listitem_t*)itemHead;

	unsigned int count = 0;
	for (; item; item = item->next)
		count++;

	return count;
}

//
// ListGetByIndex
//
// Gets an item in the list by its index
// Should not be used in performance-minded code
//
listitem_t *ListGetByIndex(void *itemHead, unsigned int index)
{
	listitem_t *head = (listitem_t*)itemHead;
	unsigned int count = 0;
	listitem_t *node;
	for (node = head; node; node = node->next)
	{
		if (count == index)
			return node;

		count++;
	}

	return NULL;
}
