#include <stdio.h>
#include "stddef.h"
#include "slist.h"

/* create or initialize a list */
slist_list* slist_create(slist_list *list) {
  if (list == NULL) {
    list = (slist_list *) malloc (sizeof(slist_list));
  }  
  list->first = NULL;
  list->last  = NULL;
}

/* insert at the first position */
void slist_insert_first(slist_list *list, void* data) {
  if (list == NULL)
    return;
  
  slist_item *item;
  item = (slist_item*) malloc (sizeof(slist_item));
  
  item->data = data;
  item->next = list->first;
  item->prev = NULL;
  
  if (list->first == NULL) {
    list->last = item;
  } else {
    list->first->prev = item;
  }
  
  list->first = item;
  
}

void slist_insert_last(slist_list *list, void* data) {
  if (list == NULL)
    return;
  
  slist_item *item;
  item = (slist_item*) malloc (sizeof(slist_item));
  
  item->data = data;
  item->prev = list->last;
  item->next = NULL;
  
  if (list->last == NULL) {
    list->first = item;
  } else {
    list->last->next = item;
  }

  list->last = item; 
}


/* delete the first item list and return the item.
   return NULL if list is empty
 */
void *slist_delete_first(slist_list *list) {
  slist_item *item;
  void* ret;
  if (list->first == NULL) {
    return NULL;
  }
  
  item = list->first;
  if (item->next == NULL) {
    /* last item */
    list->first = NULL;
    list->last = NULL;
  } else {
    list->first = item->next;
    item->next->prev = NULL;
  }
    
  ret = item->data;
  free(item);
  return ret;
}


/* delete the first item list and return the item.
   return NULL if list is empty
 */
void *slist_delete_last(slist_list *list) {
  slist_item *item;
  void* ret;
  if (list->last == NULL) {
    return NULL;
  }
  
  item = list->last;
  if (item->prev == NULL) {
    /* last item */
    list->first = NULL;
    list->last = NULL;
  } else {
    list->last = item->prev;
    item->prev->next = NULL;
  }
    
  ret = item->data;
  free(item);
  return (void*) ret;
}


/* delete all item in the list, 
   when free_item is set to true than automatically free the allocated memeory,
   both the ite list and data list. Data list should not contain netsted allocated
   memory. use slist_empty_finalize instead, when freeing nested allocated memory */
void slist_empty(slist_list *list, bool free_item) {
  if (list != NULL) {
    void *item;
    while ((item = slist_delete_last(list)) != NULL) {
      if (free_item) {
        free(item);
      }
    }
  }  
}

void slist_empty_finalize(slist_list *list, slist_callback_finalize* finalizer) {
  if (list != NULL) {
    void *item;
    while ((item = slist_delete_last(list)) != NULL) {
      finalizer(item);
      free(item);
    }
  }  
}

slist_list* slist_destroy(slist_list *list) {
  if (list != NULL) {
    slist_empty(list, true);
    free(list);
  } 
  return NULL;
}

slist_list* slist_destroy_finalize(slist_list *list, slist_callback_finalize* finalizer) {
  if (list != NULL) {
    slist_empty_finalize(list, finalizer);
    free(list);
  } 
  return NULL;
}

slist_item* slist_iterate(slist_list *list, slist_item* current) {
  if (current == NULL) {
    return list->first;
  } else {
    return current->next;
  }
}

/*
int main (int argc, char** argv) {
  slist_list* list;
  int i;
  
  list = slist_create(NULL);
  for ( i = 0 ; i < 100000; i++) {
    int * data = (int*) malloc (sizeof(int));
    *data = i;

    if (i % 2 == 10) {
      slist_insert_first(list, (void*) data);
      printf("insert first %d\n", *data);
    } else {
      slist_insert_last(list, (void*) data);
      printf("insert last %d\n", *data);
    }    
  }
  
  
  for ( i = 0 ; i < 100000; i++) {
    int * data;
    if (i % 2 == 10) {
      data = slist_delete_first(list);
      printf("delete first %d\n", *data);
    } else {
      data = slist_delete_last(list);
      printf("delete last %d\n", *data);
    }
    if (data != NULL) free (data);
    
  }
  
  slist_empty(list, true);
  
  slist_destroy(list);
  
  return 0;
}
*/
