/**
 * Copyright (c) 2021 Dominic Msters
 * 
 * This software is released under the MIT License.
 * https://opensource.org/licenses/MIT
 */

#include "list.h"

list_t * listCreate() {
  list_t *list = malloc(sizeof(list_t));
  if(list == NULL) return NULL;

  list->start = NULL;
  list->end = NULL;
  list->size = 0;
  return list;
}

listentry_t * listAdd(list_t *list, void *data) {
  //Create the list entry
  listentry_t *entry = malloc(sizeof(listentry_t));
  if(entry == NULL) return NULL;

  entry->data = data;
  entry->next = NULL;
  entry->prev = NULL;

  //Add it to the list
  listAddEntry(list, entry);

  //Return the entry
  return entry;
}

void listAddEntry(list_t *list, listentry_t *entry) {
  //Is this the first / only thing in the list?
  if(list->start == NULL) {
    list->start = entry;
    list->end = entry;
  } else {
    //Make the end's next be this entry, and this entry's prev the end, then 
    //make this the new end
    entry->prev = list->end;
    list->end->next = entry;
    list->end = entry;
  }
  list->size++;
}

void listRemove(list_t *list, void *data) {
  uint32_t i = 0;
  listentry_t *previous = list->start;

  while(previous != NULL) {
    if(previous->data == data) {
      listRemoveEntry(list, previous, false);
      break;
    }
    i++;
    previous = previous->next;
  }
}

void listRemoveEntry(list_t *list, listentry_t *entry, bool freeData) {
  //Update next and prev
  if(entry->prev != NULL) entry->prev->next = entry->next;
  if(entry->next != NULL) entry->next->prev = entry->prev;

  //Was this at the end?
  if(list->start == entry) list->start = entry->next;
  if(list->end == entry) list->end = entry->prev;

  if(freeData) free(entry->data);
  free(entry);

  list->size--;
}


listentry_t * listGetByIndex(list_t *list, uint32_t index) {
  if(index >= list->size) return NULL;

  //TODO: We can probably make this more efficient by deciding which way we 
  //should loop from based on the list size.

  uint32_t i = 0;
  listentry_t *previous = list->start;

  while(previous != NULL) {
    if(i == index) return previous;
    i++;
    previous = previous->next;
  }

  return NULL;
}

uint32_t listGetIndex(list_t *list, void* data) {
  uint32_t i = 0;
  listentry_t *previous = list->start;

  while(previous != NULL) {
    if(previous->data == data) return i;
    i++;
    previous = previous->next;
  }
  
  return -1;
}

uint32_t listGetEntryIndex(list_t *list, listentry_t *entry) {
  uint32_t i = 0;
  listentry_t *previous = list->start;

  while(previous != NULL) {
    if(previous == entry) return i;
    i++;
    previous = previous->next;
  }
  
  return -1;
}

void listDispose(list_t *list, bool freeData) {
  //Free all of the entries
  listentry_t *next = list->start;
  listentry_t *current;
  
  while(next != NULL) {
    current = next;
    next = current->next;
    if(freeData) free(current->data);
    free(current);
  }

  free(list);
}