#include "llist.h"
#define DMID 12
#include "debug.h"

/*	llist_init() initialises 'list'.  
 *
 *	llist_init() returns 1 upon successful initialisation, and 0 upon failure.
 */
int llist_init(llist *list)
{
	list->head = list->tail = list->current = NULL;
	list->elements = 0;
	
	return 1;
}


/*	llist_add_tail() adds a node containing 'data' to the tail of 'list'. 
 *
 *	llist_add_tail() returns 1 upon success, and 0 upon failure.
 */
int llist_add_tail(llist *list, void *data)
{
	llist_node	*new_node;
	
	if ( !list ) return 0;
	
	/* create new node */
	new_node = malloc(sizeof(llist_node));
	if ( new_node == NULL ) return 0;
	
	/* check whether list is currently empty */
	if ( list->tail == NULL )
	{
		list->tail = list->head = list->current = new_node;
		list->tail->prev = list->tail->next = NULL;
	}
	else
	{
		/* link new node and tail of list */
		list->tail->next = new_node;
		new_node->prev = list->tail;
		new_node->next = NULL;
		
		/* make tail point to new node */
		list->tail = list->tail->next;
	}
	
	list->tail->data = data;
	list->elements ++;
	
	return 1;
}


/*	llist_add_head() adds a node containing 'data' to the head of 'list'.
 *
 *	llist_add_head() returns 1 upon success, and 0 upon failure.
 */
int llist_add_head(llist *list, void *data)
{
	llist_node	*new_node;
	
	if ( !list ) return 0;
	
	/* create new node */
	new_node = malloc(sizeof(llist_node));
	if ( new_node == NULL ) return 0;
	
	/* check whether list is currently empty */
	if ( list->head == NULL )
	{
		list->tail = list->head = list->current = new_node;
		list->tail->prev = list->tail->next = NULL;
	}
	else
	{
		/* link new node and head of list */
		list->head->prev = new_node;
		new_node->prev = NULL;
		new_node->next = list->head;
		
		/* make tail point to new node */
		list->head = list->head->prev;
	}
	
	list->head->data = data;
	list->elements ++;
	
	return 1;
}


/*	llist_reset_current() resets the current pointer for 'list' to either
 *	the head of 'list', if 'list_pos' is LLIST_HEAD, or to the tail of
 *	'list', if 'list_pos' is LLIST_TAIL.
 *
 *	llist_reset_current() returns 1 upon success, and 0 upon failure.
 */
int llist_reset_current(llist *list, int list_pos)
{
	if ( !list ) return 0;
	
	if ( list_pos == LLIST_HEAD) list->current = list->head;
	if ( list_pos == LLIST_TAIL) list->current = list->tail;
	
	return 1;
}


/*	llist_get_current() returns a pointer to the data contained by the
 *	current node of 'list', or NULL if 'list' is NULL or empty.
 */
void *llist_get_current(llist *list)
{
	void *data;
	
	if ( !list || !list->current ) return NULL;
	
	data = list->current->data;
	return data;
}


/*	llist_get_elements() returns the number of elements in 'list', or
 *	-1 upon failure.
 */
int llist_get_elements(llist *list)
{
	if ( !list || !list->current ) return -1;
	
	return list->elements;
}


/*	llist_delete_current() deletes the current node of 'list', making the
 *	new current node point to the node previous to the one deleted.
 *
 *	If 'free_data' is non-zero, then the data block in the node will
 *	be free()d as well.
 *
 *	llist_delete_current() returns 1 upon success, and 0 upon failure.
 */
int llist_delete_current(llist *list, int free_data)
{
	llist_node	*temp;
	
	if ( !list || !list->current ) return 0;

	if ( free_data ) free(list->current->data);
	
	if ( list->current->next ) list->current->next->prev = list->current->prev;
	if ( list->current->prev ) list->current->prev->next = list->current->next;

	temp = list->current;
	
	list->current = list->current->prev;
	if ( temp == list->head ) list->head = list->current;
	if ( temp == list->tail ) list->tail = list->current;
	
	list->elements --;
		
	free(temp);
	return 1;
}


/*	_ll_llist_delete_tail() deletes the tail element of list 'list', pointing
 *	the new tail at the element prior to the old tail.
 *
 *	_ll_llust_delete_tail() returns 1 upon success, and 0 upon failure.
 */
int _ll_llist_delete_tail(llist *list, int free_data)
{
	llist_node	*temp;
	
	if ( !list || !list->tail ) return 0;

	if ( free_data ) free(list->tail->data);
	
	if ( list->tail->prev ) list->tail->prev->next = NULL;

	temp = list->tail;
	list->tail = list->tail->prev;
	
	free(temp);
	
	list->elements --;
	return 1;
}


/*	llist_next() moves the current node of 'list' to the next node in the
 *	list.
 *
 *	llist_next() returns 1 upon success, and 0 upon failure.
 */	
int llist_next(llist *list)
{
	if ( !list || !list->current ) return 0;
	
	list->current = list->current->next;
	
	if ( !list->current )
	{
		return 0;
	}
	else
	{
		return 1;
	}
}	


/*	llist_delete_list() deletes all data associated with 'list', except
 *	for the llist structure itself.
 *
 *	If 'free_data' is non-zero, then the data block in each node will
 *	be free()d as well.
 *
 *	llist_delete_list() returns 1 upon success, and 0 upon failure.
 */	
int llist_delete_list(llist *list, int free_data)
{
	if ( !list ) return 0;
	
	llist_reset_current(list, LLIST_HEAD);
	if ( !list->current ) return 0;
	
	while ( list->elements > 0 )
	{
		_ll_llist_delete_tail(list, free_data);
	}	
	
	return 1;			
}


#ifdef _LLIST_TESTING
int main(void)
{
	llist	new_list;
	void	*data;
	
	llist_init(&new_list);
	llist_add_tail(&new_list, "hello");
	llist_add_tail(&new_list, "there");
	llist_add_tail(&new_list, "world");
	llist_add_head(&new_list, "first");

	llist_reset_current(&new_list, LLIST_HEAD);	
	for ( data = llist_get_current(&new_list); data != NULL; data = llist_get_current(&new_list) )
	{
		if ( strcmp("there", data) == 0 )
		{
			llist_delete_current(&new_list, 0);
		}
		llist_next(&new_list);
	}
		
	llist_reset_current(&new_list, LLIST_HEAD);	
	for ( data = llist_get_current(&new_list); data != NULL; data = llist_get_current(&new_list) )
	{
		printf("%s\n", (char *)data);
		llist_next(&new_list);
	}


	llist_delete_list(&new_list, 0);
	
	return EXIT_SUCCESS;
}
#endif

