/*
 * $Id$
 *
 * List handling functions safe from a memory management point of view 
 *
 */

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "mr_mem.h"
#include "mr_msg.h"
#include "mr_list.h"

/* List allocation and initialization */
struct mr_list *mr_list_alloc(void) {

	struct mr_list *list = NULL;

	list = mr_malloc(sizeof(struct mr_list));
	list->first = NULL;
	list->last = NULL;
	return(list);
}

/* Elt of list allocation and initialization */
struct mr_list_elt *mr_list_alloc_elt(void *data, void (*mr_free_data)(void *data)) {

	struct mr_list_elt *elt = NULL;

	elt = mr_malloc(sizeof(struct mr_list_elt));
	elt->mr_free_data = mr_free_data;
	elt->data = (void *)data;
	elt->prev = NULL;
	elt->next = NULL;
	return(elt);
}

void mr_list_free_elt(struct mr_list_elt *elt) {
	elt->mr_free_data(elt->data);
	mr_free(elt->data);
	mr_free(elt);
}

/* List desallocation and removal */
void mr_list_free(struct mr_list *list) {

	struct mr_list_elt *elt = list->first;
	struct mr_list_elt *elt2 = NULL;
	
	while (elt != NULL) {
		/* preserve next elt */
		elt2 = elt->next;
		/* Purge elt */
		mr_list_free_elt(elt);
		/* next elt to consider */
		elt = elt2;
	}

	mr_free(list);
}

/* Count the number of elements in the list */
int mr_list_length(struct mr_list *list) {

	int i = 0;
	struct mr_list_elt *p = list->first;

	while (p != NULL) {
		i++;
		p = p->next;
	}
	return(i);
}

/* Add an element first in the list */
void mr_list_add_elt_first(struct mr_list *list, struct mr_list_elt *elt) {
	
	if (list->first != NULL) {
		/* if there was a first elt shift it */
		list->first->prev = elt;
		elt->next = list->first;
	} else {
		/* if there was no first, there was no last as well, so set it up */
		list->last = elt;
	}
	list->first = elt;
}

/* Add an element last in the list */
void mr_list_add_elt_last(struct mr_list *list, struct mr_list_elt *elt) {
	
	if (list->last != NULL) {
		/* if there was a last elt use it */
		list->last->next = elt;
		elt->prev = list->last;
	} else {
		/* if there was no last, there was no first as well, so set it up */
		list->first = elt;
	}
	list->last = elt;
}

/* Add an element in the list after the ref elt given in parameter */
void mr_list_add_elt_after(struct mr_list *list, struct mr_list_elt *ref, struct mr_list_elt *elt) {

	if (ref->next != NULL) {
		/* if there was a next elt, shit it */
		ref->next->prev = elt;
		elt->next = ref->next;
	} else {
		/* If not, it's the last elt */
		list->last = elt;
	}
	ref->next = elt;
	elt->prev = ref;
}

/* Add an element in the list before the ref elt given in parameter */
void mr_list_add_elt_before(struct mr_list *list, struct mr_list_elt *ref, struct mr_list_elt *elt) {

	if (ref->prev != NULL) {
		/* if there was a prev elt, shit it */
		ref->prev->next = elt;
		elt->prev = ref->prev;
	} else {
		/* If not, it's the first elt */
		list->first = elt;
	}
	ref->prev = elt;
	elt->next = ref;
}

/* Delete the first element in the list */
void mr_list_del_elt_first(struct mr_list *list) {

	struct mr_list_elt *elt = list->first;

	if (elt->next != NULL) {
		/* There are other elts behind */
		list->first = elt->next;
	}
	mr_list_free_elt(elt);
}

/* Delete the last element in the list */
void mr_list_del_elt_last(struct mr_list *list) {

	struct mr_list_elt *elt = list->last;

	if (elt->prev != NULL) {
		/* There are other elts before */
		list->last = elt->prev;
	}
	mr_list_free_elt(elt);
}

/* Delete an element in the list after the ref elt given in parameter */
void mr_list_del_elt_after(struct mr_list *list, struct mr_list_elt *ref) {

	struct mr_list_elt *elt = ref->next;

	if (elt != NULL) {
		/* if there was a next elt, delete it */
		if (elt->next != NULL) {
			/* there is an elt behind, shift it */
			elt->prev->next = elt->next;
			elt->next->prev = elt->prev;
			mr_list_free_elt(elt);
		} else {
			/* that elt is the last one */
			mr_list_del_elt_last(list);
		}
	} else {
		/* If not, ref is the last elt */
		mr_msg(3, "No elt to delete available after the last one");
	}
}

/* Delete an element in the list before the ref elt given in parameter */
void mr_list_del_elt_before(struct mr_list *list, struct mr_list_elt *ref) {

	struct mr_list_elt *elt = ref->prev;

	if (elt != NULL) {
		/* if there was a previous elt, delete it */
		if (elt->prev != NULL) {
			/* there is an elt before, shift it */
			elt->prev->next = elt->next;
			elt->next->prev = elt->prev;
			mr_list_free_elt(elt);
		} else {
			/* that elt is the first one */
			mr_list_del_elt_first(list);
		}
	} else {
		/* If not, ref is the first elt */
		mr_msg(3, "No elt to delete available before the first one");
	}
}
