/* File: list1a.c

   An implementation of the list ADT using arrays
*/

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

#define START_SIZE 5


/* Private Function Prototypes */

static void CrashOnNull(void *, char *) ;
static void Reallocate(list L, int inc) ;


/* 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 the list bigger by at least inc elements */
static void Reallocate(list L, int inc) {
   node *temp ;
   int new_limit ;

   new_limit = L->limit + inc ;
   if (new_limit < 2 * L->limit) { /* at least double */
      new_limit = 2 * L->limit ;
   }
   temp = (node *) realloc(L->array, new_limit * sizeof(node)) ;
   CrashOnNull(temp, "could not reallocate array") ;

   L->array = temp ;
   L->limit = new_limit ;
}


/* Public Functions */


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

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

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

   NewL->array = temp ;
   NewL->size = 0 ;
   NewL->limit = START_SIZE ;

   return NewL ;
}


/* Add item to the end of the list */
void Append(list L, char *item) {
   
   if (L->size == L->limit) Reallocate(L,1) ;

   /* Important: use strdup() */
   L->array[L->size] = strdup(item) ;
   L->size++ ;
}


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

   if (L->size == L->limit) Reallocate(L,1) ;

   /* Copy L->array[0..size-1] to L->array[1..size] */
   for (i = L->size ; i >= 1 ; i--) {
      L->array[i] = L->array[i-1] ;
   }
   
   /* Important: use strdup() */
   L->array[0] = strdup(item) ;
   L->size++ ;
}


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

   size = L->size ;
   for (i = 0 ; i < size ; i++) {
      if (strcmp(L->array[i], key) == 0) return 1 ;
   }

   return 0 ;
}


/* Returns the pointer to the node that contains the given key, or
   NULL if no such node.

   Note: that this is different from the representation used in
   the linked list implementation, but the main program doesn't
   need to know this.
*/
node *Locate(list L, char *key) {
   int i, size ;

   size = L->size ;
   for (i = 0 ; i < size ; i++) {
      if (strcmp(L->array[i], key) == 0) return &(L->array[i]) ;
   }
   return NULL ;
}


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

   end = L->array + L->size ;

   /* Sanity Check */
   if (position < L->array || position >= end) return ;

   /* Copy L->array elements from (position + 1)..end to
      position..(end-1).

      Weird use of pointer arithmetic necessary for 
      compatibility with previous list1.h interface 
   */
   for (ptr = position ; ptr < end ; ptr++) {
      *ptr = *(ptr + 1) ;
   }

   L->size-- ;
}


/* Add list L2 to the end of list L1.  L2 is destroyed.  */
void Concatenate(list L1, list L2) {
   int size1, size2, totalsize, i ; 

   size1 = L1->size ;
   size2 = L2->size ;
   totalsize = size1 + size2 ;

   if (totalsize >= L1->limit) Reallocate(L1, size2) ;
   L1->size = totalsize ;

   /* Copy elements of L2->array to end of L1->array.
      Note that we do not use strdup() here.
   */
   for (i = 0 ; i < size2 ; i++) {
      L1->array[i+size1] = L2->array[i] ;
   }
   
   /* Manually free L2.  Should not free elements of L2->array since
      they are now used by L1.
   */
   free(L2->array) ;
   free(L2) ;
}


/* Duplicate list. All items and nodes have newly allocated memory.  */
list ListDup(list L) {
   list NewL ;
   int i, size ;

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

   if (size >= NewL->limit) {
      Reallocate(NewL, size - NewL->limit) ;
   }

   /* Important: use strdup() */
   for (i = 0 ; i < size ; i++) {
      NewL->array[i] = strdup(L->array[i]) ;
   }

   return NewL ;
}


/* Print the contents of the list. */
void PrintList(list L) {
   int i, size ;

   size = L->size ;

   if (size == 0) {
      printf("<>\n") ;
      return ;
   }

   i = 0  ;
   printf("<") ;
   while (1) {
      printf("\"%s\"", L->array[i]) ;
      i++ ;
      if (i == size) break ;
      printf(",") ;
   }
   printf(">\n") ;
}


/* Free memory allocated to the list. */
void FreeList(list L) {
   int i, size ;

   size = L->size ;
   for (i = 0 ; i < size ; i++) {
      free(L->array[i]) ;
   }

   free(L->array) ;
   free(L) ;
}


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

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

   return L->array[0] ;
}


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

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

   return L->array[L->size - 1] ;
}


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

   return L->size ;
}
