#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;
}

void update_item (ll_node * list, int n, int newval) {
  CHECK_NULL (list);
  if (n == 0) {
    list->data = newval;
  }
  else {
    update_item (list->next, n-1, newval);
  }
}

void buggy_code_we_dont_expect_to_update_elt_4 (ll_node * list) {
  /* obviously does update the 4th element, but for the sake of example */
  list->next->next->next->next->data = 12345;
}

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

  /* new function: change item 4 to 42 */
  update_item (my_list, 4, 42);


  buggy_code_we_dont_expect_to_update_elt_4 (my_list);
  

  /* print things out */
  my_ptr = to_array (my_list);
  sz = size (my_list);
  for (i = 0; i < sz; i++) {
    printf("my_ptr[%d] = %d\n", i, my_ptr[i]);
  }


  return EXIT_SUCCESS;
}


