#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "alist.h"

#define INITIAL_CAPACITY 5

struct alist {
  size_t capacity;
  size_t length;
  void **contents;
};

alist *alist_new() {
  alist *a = malloc(sizeof(struct alist));
  if (a == NULL) return NULL;
  a->capacity = INITIAL_CAPACITY;
  a->length = 0;
  a->contents = calloc(INITIAL_CAPACITY, sizeof(void *));
  if (a->contents == NULL) {
    free(a);
    return NULL;
  }
  return a;
}

void alist_free(alist *a) {
  assert(a != NULL);
  free(a->contents);
  free(a);
}

size_t alist_length(alist *a) {
  assert(a != NULL);
  return a->length;
}

static alist *resize(alist *a) {
  assert(a != NULL);
  size_t new_capacity = a->capacity * 2;
  void **new_contents = realloc(a->contents, new_capacity * sizeof(void *));
  if (new_contents == NULL) return NULL;
  else {
    a->capacity = new_capacity;
    a->contents = new_contents;
    return a;
  } 
}

alist *alist_append(alist *a, void *value) {
  assert(a != NULL);
  if (a->length == a->capacity) {
    if (resize(a) == NULL) return NULL;
  }
  a->contents[a->length] = value;
  a->length++;
  return a;
}

void *alist_get(alist *a, size_t index) {
  assert(a != NULL);
  assert(index >= 0 && index < a->length);
  return a->contents[index];
}

void alist_put(alist *a, size_t index, void *value) {
  assert(a != NULL);
  assert(index >= 0 && index < a->length);
  a->contents[index] = value;
}

alist *alist_insert(alist *a, size_t index, void *value) {
  assert(a != NULL);
  assert(index >= 0 && index <= a->length);
  if (a->length == a->capacity) {
    if (resize(a) == NULL) return NULL;
  }
  if (index != a->length) {
    memmove(&(a->contents[index + 1]), &(a->contents[index]), (a->length - index + 1) * sizeof(void *));
  }
  a->contents[index] = value;
  a->length++;
  return a;
}
