#include "core/list.h"

int list_empty(struct list *list) {
  return !list->head;
}

void list_add(struct list *list, struct list_node *n) {
  list_add_after(list, list->tail, n);
}

void list_add_after(struct list *list, struct list_node *after,
                    struct list_node *n) {
  struct list_node *before = NULL;

  if (after) {
    before = after->next;
    n->prev = after;
    n->prev->next = n;
  } else {
    before = list->head;
    list->head = n;
    list->head->prev = NULL;
  }

  if (before) {
    n->next = before;
    n->next->prev = n;
  } else {
    list->tail = n;
    list->tail->next = NULL;
  }
}

void list_remove(struct list *list, struct list_node *n) {
  if (n->prev) {
    n->prev->next = n->next;
  } else {
    list->head = n->next;
  }

  if (n->next) {
    n->next->prev = n->prev;
  } else {
    list->tail = n->prev;
  }

  n->prev = n->next = NULL;
}

void list_clear(struct list *list) {
  list->head = list->tail = NULL;
}

/* implements the mergesort for linked lists as described at
   http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html */
void list_sort(struct list *list, list_node_cmp cmp) {
  struct list_node *head = list->head;
  struct list_node *tail = NULL;
  int k = 1;

  while (1) {
    int merges = 0;
    struct list_node *p = head;

    head = NULL;
    tail = NULL;

    while (p) {
      /* track the number of lists merged this pass */
      merges++;

      /* step q forward k places, tracking the size of p */
      int psize = 0;
      int qsize = k;
      struct list_node *q = p;
      while (psize < k && q) {
        psize++;
        q = q->next;
      }

      /* merge the list starting at p of length psize with the list starting
         at q of at most, length qsize */
      while (psize || (qsize && q)) {
        struct list_node *next;

        if (!psize) {
          next = q;
          q = q->next;
          qsize--;
        } else if (!qsize || !q) {
          next = p;
          p = p->next;
          psize--;
        } else if (cmp(q, p) < 0) {
          next = q;
          q = q->next;
          qsize--;
        } else {
          next = p;
          p = p->next;
          psize--;
        }

        /* move merged node to tail */
        if (!tail) {
          head = next;
        } else {
          tail->next = next;
        }
        next->prev = tail;
        tail = next;
      }

      p = q;
    }

    if (tail) {
      tail->next = NULL;
    }

    /* if only 1 pair of lists was merged, this is the end */
    if (merges <= 1) {
      break;
    }

    k *= 2;
  }

  /* update internal head and tail with sorted head and tail */
  list->head = head;
  list->tail = tail;
}
