/* File: list1d.c

   An implementation of the list ADT using doubly linked lists
*/

#include <stdio.h>
#include <stdlib.h>
#include "string.h"
#include "list1d.h"


/* Private Function Prototypes */

static void CrashOnNull(void *, char *) ;
static node *NewNode(char *) ;
static void FreeNode(node *ptr) ;


/* Private Function Implementations */

/* Exit if ptr is NULL. */
static void CrashOnNull(void *ptr, char *mesg) {
   if (ptr == NULL) {
      fprintf(stderr, "%s\n", mesg) ;
      exit(1) ;
   }
}


/* Make a new node with given item and next field set to NULL */
static node *NewNode(char *item) {
   node *new ;

   new = (node *) malloc (sizeof(node)) ;
   CrashOnNull(new, "cannot make new node") ;

   if (item == NULL) item = "" ;
   new->item = strdup(item) ;
   CrashOnNull(new->item, "cannot duplicate string") ;

   new->next = NULL ;
   new->prev = NULL ;
   return new ;
}


/* Free memory allocated to the node */
static void FreeNode(node *ptr) {
   
   if (ptr->item != NULL) free(ptr->item) ;
   free(ptr) ;
}



/* Public Functions */


/* Make a new list */
list CreateList (void) {
   list NewL ;

   NewL = (list) malloc(sizeof (*NewL)) ;
   CrashOnNull(NewL, "cannot make new list") ;

   NewL->header.item = NULL ;
   NewL->header.prev = &NewL->header ;
   NewL->header.next = &NewL->header ;
   NewL->count = 0 ;

   return NewL ;
}


/* Add item to the end of the list */
void Append(list L, char *item) {
   node *new ;
   
   new = NewNode(item) ;

   new->prev = L->header.prev ;
   new->next = &L->header ;

   new->prev->next = new ;
   L->header.prev = new ;

   L->count++ ;
}


/* Add item to the beginning of the list */
void Prepend(list L, char *item) {
   node *new ;

   new = NewNode(item) ;

   new->prev = &L->header ;
   new->next = L->header.next ;

   new->next->prev = new ;
   L->header.next = new ;

   L->count++ ;
}


/* Does item with key appear on the list? 0=No 1=Yes */
int IsMember(list L, char *key) {
   node *current, *end ;

   current = L->header.next ;
   end = &L->header ;

   while(current != end) {
      if (strcmp(current->item, key) == 0) return 1 ;
      current = current->next ;
   }

   return 0 ;
}

/* Return the position in the list of the item with key or
   NULL if no such item.

   Note: this time, position is a pointer to the node
   that contains the key, since deletion is easier in
   a doubly linked list.
*/ 
node *Locate(list L, char *key) {
   node *current, *end ;

   current = L->header.next ;
   end = &L->header ;

   while(current != end) {
      if (strcmp(current->item, key) == 0) return current ;
      current = current->next ;
   }

   return NULL ;
}


/* Remove item in given position from the list.
   See note regarding position in documentation for Locate().
*/
void Delete(list L, node *position) {

   /* These should never happen.  Check anyway. */
   if (position == NULL) return ;
   if (position == &L->header) return ;

   position->next->prev = position->prev ;
   position->prev->next = position->next ;

   FreeNode(position) ;
   L->count-- ;
}


/* Add list L2 to the end of list L1.  L2 is destroyed.  */
void Concatenate(list L1, list L2) {
   node *h1, *h2 ;

   L1->count += L2->count ;

   h1 = &L1->header ;
   h2 = &L2->header ;

   h1->prev->next = h2->next ;
   h2->next->prev = h1->prev ;
   
   h1->prev = h2->prev ;
   h2->prev->next = h1 ;

   free(L2) ; 
}


/* Duplicate of the list. All items and nodes have newly allocated memory.
*/
list ListDup(list L) {
   list NewL ;
   node *copyto, *copyfrom, *end, *current ;

   NewL = CreateList() ;
   NewL->count = L->count ;

   copyto = &NewL->header ;
   copyfrom = L->header.next ;
   end = &L->header ;

   /* copy data and adjust next pointers */
   while(copyfrom != end) {
      copyto->next = NewNode(copyfrom->item) ;      
      copyto = copyto->next ;
      copyfrom = copyfrom->next ;
   }
   copyto->next = end = &NewL->header ;

   /* Adjust prev pointers */
   current = end ;
   do {
      current->next->prev = current ;
      current = current->next ;
   } while (current != end) ;

   return NewL ;
}


/* Print the contents of the list. */
void PrintList(list L) {
   node *current, *end ;

   current = L->header.next ;
   end = &L->header ;

   if (current == end) {
      printf("<>\n") ;
      return ;
   }

   printf("<") ;
   while (1) {
      printf("\"%s\"", current->item) ;
      current = current->next ; 

      if (current == end) break ;

      printf(",") ;
   }
   printf(">\n") ;
}


/* Free memory allocated to the list. */
void FreeList(list L) {
   node *current, *end, *temp ;

   current = L->header.next ;
   end = &L->header ;

   while (current != end) {
      temp = current ;
      current = current->next ;
      FreeNode(temp) ;
   }

   free(L) ;
}


/* String in first item returned, or NULL if list is empty */
char *FirstItem(list L) {
   char *item ;

   if (L->count == 0) return NULL ;

   return L->header.next->item ;
}


/* String in last item returned, or NULL if list is empty */
char *LastItem(list L) {

   if (L->count == 0) return NULL ;

   return L->header.prev->item ;
}


/* Returns number of items in the list */
int CountList(list L) {

   return L->count ;
}
