#include <stdio.h>
#include <stdlib.h>

/** 
 * This macro uses a obscure features of C, so it may look a little weird.
 * It checks if its argument (ptr) is NULL, and if so prints an error message, then
 * exists.   Doing #ptr makes C put in the text of the argument as a string.
 * i.e. if you did CHECK_NULL(my_variable) its the string "my_variable".
 * __FILE__ and __LINE__ are special- the compiler replaces them with the filename
 * and linenumber respectively.  Furthermore, simply putting two string literals
 * together makes the compiler concatenate them.
 */

#define CHECK_NULL(ptr)  if(ptr == NULL) {fprintf(stderr,"Oops! " #ptr  " is null at " __FILE__ ":%d\n", __LINE__); abort();}



/**
 * A simple linked list node structure that has an 
 * int for data and a next pointer
 */

struct _ll_node {
  int data;
  struct _ll_node *  next;
};

/**
 * Now we can just say ll_node to talk about the struct
 */
typedef struct _ll_node ll_node;


/**
 * This function adds to the list.
 * It returns the new head of the list, which is malloced
 */
ll_node * add_to_list (ll_node * head, int  data) {
  ll_node * temp = malloc (sizeof (*temp));

  CHECK_NULL (temp);

  temp->data = data;
  temp->next = head;

  return temp;
}


/**
 * Count how many items are in the list
 */
int size (ll_node * head) {
  int count = 0;
  ll_node * curr = head;
  while (curr) {
    count ++;
    curr = curr->next;
  }
  return count;
}

/**
 * Convert the list to an array.
 * This function malloc()s space
 * for the array and returns a pointer to it
 */
int * to_array (ll_node * head) {
  int num = size(head);
  int * ptr = malloc(num * sizeof(*ptr));
  int * temp = ptr;

  CHECK_NULL (ptr);

  while (head) {
    *temp = head->data;
    head = head->next;
    temp++;
  }
  return ptr;
}

/**
 * Create a list from an array.
 * This function takes in an int array
 * and the number of elements in the array,
 * it returns a linked list with the elements in the
 * same order as the array.
 */
ll_node * create_from_array (int * array, int num) {
  ll_node * curr;
  if(num <= 0) {
    return NULL;
  }
  curr = malloc (sizeof(*curr));
  curr->data = *array;
  curr->next = create_from_array(array+1, num-1);
  return curr;
}

int main(void) {
  
  int test_array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  ll_node * my_list;
  int * my_ptr;
  int i, sz;
  my_list = create_from_array (test_array, 10);

  printf ("The list was created with %d elements\n", size (my_list));

  my_list = add_to_list(my_list, 0);

  sz = size (my_list); 

  printf ("After adding 0, the list has %d elements\n", sz);
  
  my_ptr = to_array (my_list);
  
  for (i = 0; i < sz; i++) {
    printf("my_ptr[%d] = %d\n", i, my_ptr[i]);
  }

  return EXIT_SUCCESS;
}

