/* list.c: generieke lineaire lijsten */

#include "Memory.h"
#include "List.h"
#include "debug.h"
#include "error.h"

#ifdef BETTER_MEMMAN
static STORAGE *listStor = (STORAGE *)NULL;
#define NEWLIST()  	(LIST *)New(sizeof(LIST), &listStor)
#define DISPOSELIST(ptr) Dispose((unsigned char *)(ptr), &listStor)
#else /*BETTER_MEMMAN*/
#define NEWLIST()	(LIST *)Alloc(sizeof(LIST))
#define DISPOSELIST(ptr) Free((char *)ptr, sizeof(LIST))
#endif /*BETTER_MEMMAN*/


#ifndef NOLISTMACROS
void *__plistel__;	/* wordt gebruikt in de ListNext() macro */
#endif /*NOLISTMACROS*/

#ifdef NOLISTMACROS
/* creeert een lege lijst */
LIST *ListCreate(void)
{
	return (LIST *)NULL;
}
#endif /*NOLISTMACROS*/

/* voegt het element vooraan aan de lijst toe, geeft de nieuwe wijzer naar 
 * de lijst terug */
LIST *ListAdd(LIST *list, void *pelement)
{
	LIST *newlist;

/* weiger NULL-pointers in de lijst */
	if (pelement == (void *)NULL)
		return list;

	newlist = NEWLIST();
	newlist->pelement = pelement;
	newlist->next = list;

	return newlist;
}

/* telt het aantal elementen in de lijst */
int ListCount(LIST *list)
{
	int count = 0;

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

	return count;
}

/* geeft het index-ste element uit de lijst terug of NULL als er
 * geen index elementen in de lijst zijn. index telt vanaf 0. */
void *ListGet(LIST *list, int index)
{
	while (list && index > 0) {
		list = list->next;
		index--;
	}

	if (!list)
		return (void *)NULL;
	else
		return list->pelement;
}

#ifdef NOLISTMACROS
/* het eerste argument is het adres van een LIST *. 
 * Aanvankelijk moet deze LIST * naar het begin van een lijst wijzen.
 * Bij herhaaldelijke oproepen zal de lijst doorlopen worden.
 * De functie geeft bij herhaaldelijk oproepen een na een alle
 * elementen van de lijst terug. Indien de lijst doorlopen is,
 * wordt een NULL wijzer teruggegeven */
void *ListNext(LIST **list)
{
	void *pelement = (void *)NULL;

	if (*list) {
		pelement = (*list)->pelement;
		*list = (*list)->next;
	} else
		pelement = (void *)NULL;

	return pelement;
}
#endif /*NOLISTMACROS*/

/* voegt twee lijsten samen: de elementen van lijst list2 komen voor de elementen van 
 * lijst list1. Een wijzer naar de verlengde lijst list1 wordt teruggegeven. */
LIST *ListMerge(LIST *list1, LIST *list2)
{
	void *pelement = (void *)NULL;

	while ((pelement = ListNext(&list2)))
		list1 = ListAdd(list1, pelement);

	return list1;
}

/* dupliceert een lijst: de elementen zelf worden niet gedupliceerd */
LIST *ListDuplicate(LIST *list)
{
	LIST *newlist = (LIST *)NULL;
	void *pelement = (void *)NULL;

	while ((pelement = ListNext(&list)))
		newlist = ListAdd(newlist, pelement);

	return newlist;
}

/* verwijdert een element uit de lijst. Geeft een pointer naar de gewijzigde 
 * lijst terug */
LIST *ListRemove(LIST *list, void *pelement)
{
	LIST *p, *q;

/* als de lijst ledig is */
	if (!list) {
		Fatal(-2, "ListRemove", "Poging een element te verwijderen uit een ledige lijst");
		return list;	/* voor het geval we ooit besluiten tolerant te zijn */
	}

/* als het eerste element verwijderd moet worden */
	if (pelement == list->pelement) {
		p = list->next;
		DISPOSELIST(list);
		return p;
	}

/* chasing pointers: */
	q = list;
	p = list->next;
	while (p && p->pelement != pelement) {
		q = p;
		p = p->next;
	}

/* als p de NULL pointer is komt het te verwijderen element niet in de lijst
 * voor: fout */
	if (!p) {
		Fatal(-2, "ListRemove", "Poging een niet voorkomend element uit een lijst te verwijderen");
		return list;
	}

/* in het andere geval is p een wijzer naar de lijst-cel die eruit gehaald moet
 * worden */
	else {
		q->next = p->next;
		DISPOSELIST(p);
	}

/* geef een (ongewijzigde) wijzer naar de gewijzigde lijst terug */
	return list;
}

#ifdef NOLISTMACROS
/* iterator: voer de procedure uit vooral alle elementen van de lijst.
 * De procedure heeft als argument een wijzer naar een element */
void ListIterate(LIST *list, void (*proc)(void *))
{
	void *pelement;

	while (list) {
		pelement = list->pelement;
		list = list->next;
		proc(pelement);
	}		
}
#endif /*NOLISTMACROS*/

#ifdef NOLISTMACROS
/* iterator: voer de procedure uit vooral alle elementen van de lijst.
 * De procedure heeft twee argumenten: eerst een wijzer naar
 * het element, dan een wijzer naar andere data de gelijk is voor alle
 * elementen */
void ListIterate1A(LIST *list, void (*proc)(void *, void *), void *extra)
{
	void *pelement;

	while (list) {
		pelement = list->pelement;
		list = list->next;
		proc(pelement, extra);
	}		
}
#endif /*NOLISTMACROS*/

#ifdef NOLISTMACROS
/* iterator: voer de procedure uit vooral alle elementen van de lijst.
 * De procedure heeft twee argumenten: eerst een wijzer naar
 * data die gelijk is voor alle elementen, dan een wijzer naar het element */
void ListIterate1B(LIST *list, void (*proc)(void *, void *), void *extra)
{
	void *pelement;

	while (list) {
		pelement = list->pelement;
		list = list->next;
		proc(extra, pelement);
	}		
}
#endif /*NOLISTMACROS*/

/* een volledige lijst vernietigen: vernietigt niet de elementen! */
void ListDestroy(LIST *list)
{
	LIST *p;

	while (list) {
		p = list->next;
		DISPOSELIST(list);
		list = p;
	}
}




