/*
 *  list.c:		List operations	
 *
 *  Written by:		Ullrich Hafner
 *		
 *  This file is part of FIASCO (Fractal Image And Sequence COdec)
 *  Copyright (C) 1994-2000 Ullrich Hafner
 */

/*
 *  $Date: 2000/06/14 20:49:37 $
 *  $Author: hafner $
 *  $Revision: 5.1 $
 *  $State: Exp $
 */

#include "config.h"

#include <string.h>

#include "types.h"
#include "macros.h"
#include "error.h"

#include "misc.h"
#include "list.h"

/*****************************************************************************

				public code
  
*****************************************************************************/

list_t *
alloc_list (size_t size_of_element)
/*
 *  List constructor:
 *  Allocate a new list.
 *  Size of list element values is given by 'size_of_element'.
 *
 *  Return value:
 *	pointer to an empty list
 */
{
   list_t *new_list = Calloc (1, sizeof (list_t));

   assert (size_of_element > 0);

   new_list->head 	     = NULL;
   new_list->tail 	     = NULL;
   new_list->size_of_element = size_of_element;

   return new_list;
}

void
free_list (list_t *list)
/*
 *  List destructor:
 *  Discard list and its elements.
 *
 *  No return value.
 *
 *  Side effects:
 *	struct 'list' is discarded
 */
{
   assert (list);
   
   while (list_remove (list, HEAD, NULL))
      ;
   Free (list);
}

void
list_insert (list_t *list, pos_e pos, const void *data)
/*
 *  Insert a new 'list' element at head ('pos' = HEAD) or
 *  tail ('pos' = TAIL) of 'list'. 
 *  'data' is a pointer to a memory segment of size
 *  'list'->size_of_element containing the value to store.
 *  The value is directly copied - no references are stored.
 *
 *  No return value.
 *
 *  Side effects:
 *	lists current tail or head is replaced by the new element
 */
{
   node_t *element;

   assert (list && data);

   element 	  = Calloc (1, sizeof (node_t));
   element->value = Calloc (1, list->size_of_element);
   memcpy (element->value, data, list->size_of_element);

   if (pos == TAIL)
   {
      element->next = NULL;
      element->prev = list->tail;
      if (list->tail)
	 list->tail->next = element;
      list->tail = element;
      if (!list->head)
	 list->head = element;
   }
   else					/* pos == HEAD */
   {
      element->prev = NULL;
      element->next = list->head;
      if (list->head)
	 list->head->prev = element;
      list->head = element;
      if (!list->tail)
	 list->tail = element;
   }
}

bool_t
list_remove (list_t *list, pos_e pos, void *data)
/*
 *  Remove 'list' element from head or tail of 'list'.
 *
 *  Return value:
 *	TRUE on success,
 *	FALSE if list is empty or
 *	      if list value data is NULL
 *
 *  Side effects:
 *	lists current head or tail is removed
 *	value of the removed list element (if not NULL) is copied to
 *      'data' (if 'data' is not NULL)
 */
{
   node_t *element;
   void	  *valueptr;

   assert (list);
   
   if (pos == TAIL)
   {
      element = list->tail;
      if (element)
      {
	 list->tail = element->prev;
	 valueptr   = element->value;
	 Free (element);
      }
      else
	 valueptr = NULL;
      if (!list->tail)			/* 'element' was last node */
	 list->head = NULL;
   }
   else					/* pos == HEAD */
   {
      element = list->head;
      if (element)
      {
	 list->head = element->next;
	 valueptr   = element->value;
	 Free (element);
      }
      else
	 valueptr = NULL;
      if (!list->head)			/* 'element' was last node */
	 list->tail = NULL;
   }

   if (valueptr)			/* copy value of node */
   {
      if (data)				
	 memcpy (data, valueptr, list->size_of_element);
      Free (valueptr);
   }
   
   return valueptr ? TRUE : FALSE;
}

bool_t
list_element_n (const list_t *list, pos_e pos, unsigned n, void *data)
/*
 *  Get value of 'list' element number 'n'.
 *  (First element is list head if 'pos' == HEAD
 *                 or list tail if 'pos' == TAIL.
 *   Accordingly, traverse the list in ascending or descending order).
 *  
 *  Return value:
 *	TRUE on success, FALSE if there is no element 'n'
 *
 *  Side effects:
 *	value of list element 'n' is copied to 'data' 
 */
{
   node_t *element;

   assert (list && data);
   
   if (pos == HEAD)
      for (element = list->head; element != NULL && n;
	   element = element->next, n--)
	 ;
   else
      for (element = list->tail; element != NULL && n;
	   element = element->prev, n--)
	 ;
      
   if (element)
   {
      memcpy (data, element->value, list->size_of_element);
      return TRUE;
   }
   else
      return FALSE;
}

unsigned
list_sizeof (const list_t *list)
/*
 *  Count number of 'list' elements.
 *
 *  Return value:
 *	number of 'list' elements.
 */
{
   node_t   *element;
   unsigned  n = 0;

   assert (list);
   
   for (element = list->head; element != NULL; element = element->next)
      n++;

   return n;
}

void
list_foreach (const list_t *list, void (*function)(void *, void *), void *data)
/*
 *  Call 'function' for each element of the 'list'.
 *  Parameters given to 'function' are a pointer to the value of the
 *  current 'list' element and the user pointer 'data'.
 *
 *  No return value.
 */
{
   node_t *element;

   assert (list && function && data);
   
   for (element = list->head; element; element = element->next)
      function (element->value, data);
}

