/*
    slforum version 1.x - Simple chat room usable via telnet.
    Copyright (C) 1999-2001 Jeremy D. Monin, Dan Chin, Matt Lochansky
	(forum@shadowlands.org).
    Based on jdkchat version 1.3, J.D.Koftinoff Software, Ltd. (GPL'd)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*************************************************************
 *  General linked list.  Keeps track of things.
 *  See linkedlist.h for docs.
 *  jer 19981221.1930
 * 19990215.1830: added llist_next and llist_ldata for traversal.
 *	llist_lrmnext will be added at some point;
 *	for now, you can add things to this list
 *	but you can't remove them... this is fine,
 *	for the purpose I built it.
 * 20001006.0730: added llist_add_after
 ************************************************************/

#include <stdio.h>
#ifdef USE_MALLOC_H
#include <malloc.h>
#endif
#include "linkedlist.h"

LinkedList *llist_new (ListData_freeFunc *free_data_func) {
  LinkedList *LL = (LinkedList *) malloc (sizeof (LinkedList));
  if (LL == NULL)
    return NULL;

  LL->tail = NULL;
  LL->n_items = 0;
  LL->free_data = free_data_func;
  return LL;
}


int llist_add_start (LinkedList *L, void *data) {
  LinkedListLink *ll;
  if (L == NULL)
    return -1;
  if (data == NULL)
    return -1;

  ll = (LinkedListLink *) malloc (sizeof (LinkedListLink));
  if (ll == NULL)
    return -1;

  if (L->tail == NULL) {  /* first elem in list */
    L->tail = ll;
    ll->next = ll;
  } else {
    ll->next = L->tail->next;  /* ll->next = current head */
    L->tail->next = ll;    /* head = ll */
  }
  ll->data = data;

  ++(L->n_items);
  return 0;  /* success */
}


int llist_add_end (LinkedList *L, void *data) {
  LinkedListLink *ll;
  if (L == NULL)
    return -1;
  if (data == NULL)
    return -1;

  ll = (LinkedListLink *) malloc (sizeof (LinkedListLink));
  if (ll == NULL)
    return -1;

  if (L->tail == NULL) {  /* first elem in list */
    L->tail = ll;
    ll->next = ll;
  } else {
    ll->next = L->tail->next;  /* ll->next = current head */
    L->tail->next = ll;	/* currenttail->next = ll */
    L->tail = ll;	/* newtail = ll */
  }
  ll->data = data;

  ++(L->n_items);
  return 0;  /* success */
}


#if 0
static void llist_print(LinkedList *L) {
    LinkedListLink *LL = NULL;

    printf ("--- print list ---\n");
    printf ("n_items: %d  tail: %p\n", L->n_items, L->tail);
    printf ("links: ");
    if (L->tail != NULL)
    {
	for (LL = L->tail->next;
	     LL != NULL;
	     LL = LL->next)
	{
	    printf ("%p (%p)", LL, LL->data);
	    if (LL->next == LL) {
		printf("<self>\n");
		break;
	    }
	    if(LL == L->tail) {
		printf("(tail)\n");
		break;
	    }
	    printf("-> ");
	}
    }
    if(LL == NULL)
	printf ("<NUL>\n");
    printf ("--- end print list ---\n");
}
#endif


int llist_add_after (LinkedList *L, LinkedListLink *LL, void *data) {
    if (LL == NULL)
    {
	return llist_add_start (L, data);
    }
    else
    {
	LinkedListLink *llnew;

	if (L == NULL)
	    return -1;
	if (data == NULL)
	    return -1;

	llnew = (LinkedListLink *) malloc (sizeof (LinkedListLink));
	if (llnew == NULL)
	    return -1;

	if (LL == L->tail)   /* new tail */
	{
	    llnew->next = LL->next;  /* llnew->next = current head */
	    LL->next = llnew;	/* currenttail->next = llnew */
	    L->tail = llnew;	/* newtail = llnew */
	} else {    /* not a new tail, just in the middle */
	    llnew->next = LL->next;
	    LL->next = llnew;
	}
	llnew->data = data;

	++(L->n_items);

	return 0;  /* success */
    }
}



int llist_length (LinkedList *L) {
  if (L == NULL)
    return 0;
  return L->n_items;
}


void *llist_remove_start (LinkedList *L) {
  LinkedListLink *Lremove;
  void *ldata;

  if (L->tail == NULL)
    return NULL;

  Lremove = L->tail->next;
  ldata = Lremove->data;

  if (L->tail == L->tail->next) {
      /* removing the only element */
    L->tail = NULL;
    L->n_items = 0;
  } else
  {
    --(L->n_items);
    L->tail->next = L->tail->next->next;
  }

  free (Lremove);
  return ldata;
}


void* llist_remove_after(LinkedList *L, LinkedListLink *LLprev)
{
  LinkedListLink *Lremove;
  void* ldata;
  
  if ((L == NULL) || (LLprev == NULL))
    return NULL;

  /* what if LLprev is the only element? */
  if (LLprev->next == LLprev)
    return NULL;  /* only 1 element, so removing after it is pointless */
  
  /* are we removing the tail? */
  if (LLprev->next == L->tail)
    L->tail = LLprev;  /* LLprev is the new tail */

  Lremove = LLprev->next;
  ldata = Lremove->data;

  /* adjust list */
  LLprev->next = LLprev->next->next;
  --(L->n_items);

  /* free the link and return */
  free (Lremove);
  return ldata;
}


void llist_free (LinkedList *L) {
  LinkedListLink *ll,  *ln, *tail;
  ListData_freeFunc *fdf;

  if (L == NULL)
    return;

  fdf = L->free_data;
  if (L->tail != NULL) {
    ll = tail = L->tail;
    do {
      if ((fdf != NULL) && (ll->data != NULL)) {
	(fdf) (ll->data);
      }
      ln = ll->next;
      free(ll);
      ll = ln;
    } while (ll != tail);
  }

  free (L);
}

LinkedListLink *llist_head (LinkedList *L) {
  if (L == NULL)  return NULL;
  if ((L->n_items == 0) || (L->tail == NULL))  return NULL;
  return L->tail->next;
}

LinkedListLink *llist_next (LinkedList *L, LinkedListLink *LL) {
  if ((LL == NULL) || (LL == L->tail))	return NULL;
  return LL->next;
}

void *llist_ldata (LinkedListLink *LL) {
  if (LL == NULL)	return NULL;
  return LL->data;
}

