/* Standard C */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

/* Project */
#include "util.h"

/* --8<-- list_tail_type */
typedef int T;

struct list_node {
	struct list_node *next;
	T                 data;
};

struct list {
	struct list_node *head, *tail;
};
/* -->8-- */

/* --8<-- list_tail_init */
void
list_init(struct list *list)
{
	list->head = NULL;
}
/* -->8-- */

/* --8<-- list_tail_create_node */
static struct list_node *
create_node(struct list_node *next, T data)
{
	struct list_node *node = malloc(sizeof *node);

	if ( node ) {
		node->next = next;
		node->data = data;
	}
	return node;
}
/* -->8-- */

/* --8<-- list_tail_push_back */
void
list_push_back(struct list *list, T data)
{
	struct list_node *node = create_node(NULL, data);

	if ( node ) {
		if ( list->head == NULL ) {
			list->head = node;
		}
		else {
			list->tail->next = node;
		}
		list->tail = node;
	}
	else {
		ERROR("out of memory");
	}
}
/* -->8-- */

/* --8<-- list_tail_front */
void
list_push_front(struct list *list, T data)
{
	struct list_node *node = create_node(list->head, data);

	if ( node ) {
		if ( list->head == NULL ) { // Einfügen in eine leere Liste?
			list->tail = node;
		}
		list->head = node;
	}
	else {
		ERROR("out of memory");
	}
}
/* -->8-- */

/* --8<-- list_tail_pop_front */
bool
list_pop_front(struct list *list, T *data)
{
	if ( list->head ) {
		struct list_node *next = list->head->next;

		if ( data ) {
			*data = list->head->data;
		}
		free(list->head);
		list->head = next;

		return true;
	}
	else {
		return false;
	}
}
/* -->8-- */

/* --8<-- list_tail_insert_next */
void
list_insert_next(struct list *list, struct list_node *node, T data)
{
	if ( node == NULL ) { // Am Anfang einfügen
		list_push_front(list, data);
	}
	else {
		struct list_node *new_node = create_node(node->next, data);

		if ( new_node ) {
			if ( node->next == NULL ) { // Am Ende einfügen
				list->tail = new_node;
			}
			node->next = new_node;
		}
		else {
			ERROR("out of memory");
		}
	}
}
/* -->8-- */

/* --8<-- list_tail_delete_next */
void
list_delete_next(struct list *list, struct list_node *node, T *data)
{
	if ( node == NULL ) { // Am Anfang entfernen
		list_pop_front(list, data);
	}
	else {
		if ( node->next ) {
			struct list_node *old_node = node->next;
			node->next                 = node->next->next;

			if ( node->next == NULL ) {
				list->tail = node;
			}

			if ( data ) {
				*data = old_node->data;
			}
			free(old_node);
		}
	}
}
/* -->8-- */

/* --8<-- list_tail_empty */
bool
list_empty(struct list *list)
{
	return list->head == NULL;
}
/* -->8-- */

/* --8<-- list_tail_free */
void
list_free(struct list *list)
{
	struct list_node *item, *next;

	for ( item = list->head; item; item = next ) {
		next = item->next;
		free(item);
	}
}
/* -->8-- */

int
main()
{
	struct list list[1];

	list_init(list);

	for ( int i = 0; i != 10; ++i )
		list_push_front(list, -i);

	for ( int i = 0; i != 10; ++i )
		list_push_back(list, i);

	while ( !list_empty(list) ) {
		int i;
		if ( list_pop_front(list, &i) )
			printf("%d\n", i);
		else
			ERROR("this shouldn't happen!");
	}

	list_free(list);

	return EXIT_SUCCESS;
}
