/*
 * gslist.c: Singly-linked list implementation
 *
 * Authors:
 *   Duncan Mak (duncan@novell.com)
 *   Raja R Harinath (rharinath@novell.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * (C) 2006 Novell, Inc.
 */

#include <stdio.h>
#include <glib.h>

GSList*
g_slist_alloc (void)
{
	return g_new0 (GSList, 1);
}

void
g_slist_free_1 (GSList *list)
{
	g_free (list);
}

GSList*
g_slist_append (GSList *list, gpointer data)
{
	return g_slist_concat (list, g_slist_prepend (NULL, data));
}

/* This is also a list node constructor. */
GSList*
g_slist_prepend (GSList *list, gpointer data)
{
	GSList *head = g_slist_alloc ();
	head->data = data;
	head->next = list;

	return head;
}

/*
 * Insert the given data in a new node after the current node. 
 * Return new node.
 */
static inline GSList *
insert_after (GSList *list, gpointer data)
{
	list->next = g_slist_prepend (list->next, data);
	return list->next;
}

/*
 * Return the node prior to the node containing 'data'.
 * If the list is empty, or the first node contains 'data', return NULL.
 * If no node contains 'data', return the last node.
 */
static inline GSList*
find_prev (GSList *list, gconstpointer data)
{
	GSList *prev = NULL;
	while (list) {
		if (list->data == data)
			break;
		prev = list;
		list = list->next;
	}
	return prev;
}

/* like 'find_prev', but searches for node 'link' */
static inline GSList*
find_prev_link (GSList *list, GSList *link)
{
	GSList *prev = NULL;
	while (list) {
		if (list == link)
			break;
		prev = list;
		list = list->next;
	}
	return prev;
}

GSList*
g_slist_insert_before (GSList *list, GSList *sibling, gpointer data)
{
	GSList *prev = find_prev_link (list, sibling);

	if (!prev)
		return g_slist_prepend (list, data);

	insert_after (prev, data);
	return list;
}

void
g_slist_free (GSList *list)
{
	while (list) {
		GSList *next = list->next;
		g_slist_free_1 (list);
		list = next;
	}
}

GSList*
g_slist_copy (GSList *list)
{
	GSList *copy, *tmp;

	if (!list)
		return NULL;

	copy = g_slist_prepend (NULL, list->data);
	tmp = copy;

	for (list = list->next; list; list = list->next)
		tmp = insert_after (tmp, list->data);

	return copy;
}

GSList*
g_slist_concat (GSList *list1, GSList *list2)
{
	if (!list1)
		return list2;

	g_slist_last (list1)->next = list2;
	return list1;
}

void
g_slist_foreach (GSList *list, GFunc func, gpointer user_data)
{
	while (list) {
		(*func) (list->data, user_data);
		list = list->next;
	}
}

GSList*
g_slist_last (GSList *list)
{
	if (!list)
		return NULL;

	while (list->next)
		list = list->next;

	return list;
}

GSList*
g_slist_find (GSList *list, gconstpointer data)
{
	for (; list; list = list->next)
		if (list->data == data)
			break;
	return list;
}

GSList *
g_slist_find_custom (GSList *list, gconstpointer data, GCompareFunc func)
{
	if (!func)
		return NULL;
	
	while (list) {
		if (func (list->data, data) == 0)
			return list;
		
		list = list->next;
	}
	
	return NULL;
}

guint
g_slist_length (GSList *list)
{
	guint length = 0;

	while (list) {
		length ++;
		list = list->next;
	}

	return length;
}

GSList*
g_slist_remove (GSList *list, gconstpointer data)
{
	GSList *prev = find_prev (list, data);
	GSList *current = prev ? prev->next : list;

	if (current) {
		if (prev)
			prev->next = current->next;
		else
			list = current->next;
		g_slist_free_1 (current);
	}

	return list;
}

GSList*
g_slist_remove_all (GSList *list, gconstpointer data)
{
	GSList *next = list;
	GSList *prev = NULL;
	GSList *current;

	while (next) {
		GSList *tmp_prev = find_prev (next, data);
		if (tmp_prev)
			prev = tmp_prev;
		current = prev ? prev->next : list;

		if (!current)
			break;

		next = current->next;

		if (prev)
			prev->next = next;
		else
			list = next;
		g_slist_free_1 (current);
	}

	return list;
}

GSList*
g_slist_remove_link (GSList *list, GSList *link)
{
	GSList *prev = find_prev_link (list, link);
	GSList *current = prev ? prev->next : list;

	if (current) {
		if (prev)
			prev->next = current->next;
		else
			list = current->next;
		current->next = NULL;
	}

	return list;
}

GSList*
g_slist_delete_link (GSList *list, GSList *link)
{
	list = g_slist_remove_link (list, link);
	g_slist_free_1 (link);

	return list;
}

GSList*
g_slist_reverse (GSList *list)
{
	GSList *prev = NULL;
	while (list){
		GSList *next = list->next;
		list->next = prev;
		prev = list;
		list = next;
	}

	return prev;
}

GSList*
g_slist_insert_sorted (GSList *list, gpointer data, GCompareFunc func)
{
	GSList *prev = NULL;
	
	if (!func)
		return list;

	if (!list || func (list->data, data) > 0)
		return g_slist_prepend (list, data);

	/* Invariant: func (prev->data, data) <= 0) */
	for (prev = list; prev->next; prev = prev->next)
		if (func (prev->next->data, data) > 0)
			break;

	/* ... && (prev->next == 0 || func (prev->next->data, data) > 0)) */
	insert_after (prev, data);
	return list;
}

gint
g_slist_index (GSList *list, gconstpointer data)
{
	gint index = 0;
	
	while (list) {
		if (list->data == data)
			return index;
		
		index++;
		list = list->next;
	}
	
	return -1;
}

GSList*
g_slist_nth (GSList *list, guint n)
{
	for (; list; list = list->next) {
		if (n == 0)
			break;
		n--;
	}
	return list;
}

gpointer
g_slist_nth_data (GSList *list, guint n)
{
	GSList *node = g_slist_nth (list, n);
	return node ? node->data : NULL;
}

typedef GSList list_node;
#include "sort.frag.h"

GSList*
g_slist_sort (GSList *list, GCompareFunc func)
{
	if (!list || !list->next)
		return list;
	return do_sort (list, func);
}
