#ifndef VECTOR_C
#define VECTOR_C

#include "utils.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
  size_t capacity;
  size_t length;
  generic_pointer_t *data;
} vector_t;

vector_t *vector_create(size_t initial_capacity) {
  vector_t *vec = malloc(sizeof(vector_t));
  vec->capacity = initial_capacity;
  vec->length = 0;
  vec->data = malloc(sizeof(generic_pointer_t) * initial_capacity);
  return vec;
}

int vector_push(vector_t *vec, generic_pointer_t item) {
  if (vec->length >= vec->capacity) {
    vec->capacity *= 2;
    vec->data = realloc(vec->data, sizeof(generic_pointer_t) * vec->capacity);
    if (vec->data == NULL) {
      printf("<!> Failed to reallocate vector data\n");
      return -1;
    }
  }
  vec->data[vec->length++] = item;
  return 0;
}

generic_pointer_t vector_pop_at(vector_t *vec, size_t index) {
  if (index >= vec->length) {
    return NULL;
  }

  generic_pointer_t item = vec->data[index];
  vec->data[index] = NULL;

  if (index == vec->length - 1) {
    vec->length--;
  }

  return item;
}

generic_pointer_t vector_pop_last(vector_t *vec) {
  if (vec->length == 0) {
    return NULL;
  }
  return vector_pop_at(vec, vec->length - 1);
}

#define vector_pop_select(_1, _2, NAME, ...) NAME
#define vector_pop(...)                                                        \
  vector_pop_select(__VA_ARGS__, vector_pop_at, vector_pop_last)(__VA_ARGS__)

generic_pointer_t vector_get(vector_t *vec, size_t index) {
  if (index < vec->length) {
    return vec->data[index];
  }
  return NULL;
}

void vector_clear(vector_t *vec) {
  for (size_t i = 0; i < vec->length; i++) {
    vec->data[i] = NULL;
  }
  vec->length = 0;
}

void vector_destroy(vector_t *vec) {
  free(vec->data);
  vec->data = NULL;
  vec->capacity = 0;
  vec->length = 0;
}

int test_vector() {
  printf("--- START TEST:\t vector ---\n");
  vector_t *vec = vector_create(2);

  vector_push(vec, (generic_pointer_t) "Hello");
  vector_push(vec, (generic_pointer_t) "World");
  vector_push(vec, (generic_pointer_t) "!");

  for (size_t i = 0; i < vec->length; i++) {
    printf("Item %zu: %s\n", i, (char *)vector_get(vec, i));
  }

  if (strcmp((char *)vector_pop(vec), "!") != 0) {
    printf("Expected to pop '!' from vector!\n");
    vector_destroy(vec);
    return 1;
  }

  if (strcmp((char *)vector_pop(vec), "World") != 0) {
    printf("Expected to pop 'World' from vector!\n");
    vector_destroy(vec);
    return 1;
  }

  if (strcmp((char *)vector_pop(vec), "Hello") != 0) {
    printf("Expected to pop 'Hello' from vector!\n");
    vector_destroy(vec);
    return 1;
  }

  vector_destroy(vec);
  printf("--- END TEST:\t vector ---\n");
  return 0;
}

#endif
