// vim:fileencoding=utf-8:foldmethod=marker

#include "./dbl_list.h"
#include "wapp_core.h"
#include <stddef.h>

wapp_intern CEnumValList cenumval_node_to_list(CEnumValNode *node);
wapp_intern CArgList carg_node_to_list(CArgNode *node);
wapp_intern CQualifierList cqualifier_node_to_list(CQualifierNode *node);
wapp_intern CIncludeList cinclude_node_to_list(CIncludeNode *node);
wapp_intern CUserTypeList cusertype_node_to_list(CUserTypeNode *node);
wapp_intern CFuncList cfunc_node_to_list(CFuncNode *node);
wapp_intern CStructList cstruct_node_to_list(CStructNode *node);
wapp_intern CMacroList cmacro_node_to_list(CMacroNode *node);

CEnumValNode *wapp_cenumval_list_get(const CEnumValList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CEnumValNode *output  = NULL;
  CEnumValNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cenumval_list_push_front(CEnumValList *list, CEnumValNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CEnumValList node_list = cenumval_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CEnumValNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cenumval_list_push_back(CEnumValList *list, CEnumValNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CEnumValList node_list = cenumval_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CEnumValNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cenumval_list_insert(CEnumValList *list, CEnumValNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cenumval_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cenumval_list_push_back(list, node);
    return;
  }

  CEnumValNode *dst_node = wapp_cenumval_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CEnumValList node_list = cenumval_node_to_list(node);

  list->node_count += node_list.node_count;

  CEnumValNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CEnumValNode *wapp_cenumval_list_pop_front(CEnumValList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CEnumValNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CENUMVAL_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CEnumValList){0};
    goto RETURN_CENUMVAL_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CENUMVAL_LIST_POP_FRONT:
  return output;
}

CEnumValNode *wapp_cenumval_list_pop_back(CEnumValList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CEnumValNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CENUMVAL_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CEnumValList){0};
    goto RETURN_CENUMVAL_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CENUMVAL_LIST_POP_BACK:
  return output;
}

CEnumValNode *wapp_cenumval_list_remove(CEnumValList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CEnumValNode *output = NULL;

  if (index == 0) {
    output = wapp_cenumval_list_pop_front(list);
    goto RETURN_CENUMVAL_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cenumval_list_pop_back(list);
    goto RETURN_CENUMVAL_LIST_REMOVE;
  }

  output = wapp_cenumval_list_get(list, index);
  if (!output) {
    goto RETURN_CENUMVAL_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CENUMVAL_LIST_REMOVE:
  return output;
}

void wapp_cenumval_list_empty(CEnumValList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cenumval_list_pop_back(list);
  }
}

CArgNode *wapp_carg_list_get(const CArgList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CArgNode *output  = NULL;
  CArgNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_carg_list_push_front(CArgList *list, CArgNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CArgList node_list = carg_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CArgNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_carg_list_push_back(CArgList *list, CArgNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CArgList node_list = carg_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CArgNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_carg_list_insert(CArgList *list, CArgNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_carg_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_carg_list_push_back(list, node);
    return;
  }

  CArgNode *dst_node = wapp_carg_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CArgList node_list = carg_node_to_list(node);

  list->node_count += node_list.node_count;

  CArgNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CArgNode *wapp_carg_list_pop_front(CArgList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CArgNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CARG_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CArgList){0};
    goto RETURN_CARG_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CARG_LIST_POP_FRONT:
  return output;
}

CArgNode *wapp_carg_list_pop_back(CArgList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CArgNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CARG_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CArgList){0};
    goto RETURN_CARG_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CARG_LIST_POP_BACK:
  return output;
}

CArgNode *wapp_carg_list_remove(CArgList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CArgNode *output = NULL;

  if (index == 0) {
    output = wapp_carg_list_pop_front(list);
    goto RETURN_CARG_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_carg_list_pop_back(list);
    goto RETURN_CARG_LIST_REMOVE;
  }

  output = wapp_carg_list_get(list, index);
  if (!output) {
    goto RETURN_CARG_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CARG_LIST_REMOVE:
  return output;
}

void wapp_carg_list_empty(CArgList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_carg_list_pop_back(list);
  }
}

CQualifierNode *wapp_cqualifier_list_get(const CQualifierList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CQualifierNode *output  = NULL;
  CQualifierNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cqualifier_list_push_front(CQualifierList *list, CQualifierNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CQualifierList node_list = cqualifier_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CQualifierNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cqualifier_list_push_back(CQualifierList *list, CQualifierNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CQualifierList node_list = cqualifier_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CQualifierNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cqualifier_list_insert(CQualifierList *list, CQualifierNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cqualifier_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cqualifier_list_push_back(list, node);
    return;
  }

  CQualifierNode *dst_node = wapp_cqualifier_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CQualifierList node_list = cqualifier_node_to_list(node);

  list->node_count += node_list.node_count;

  CQualifierNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CQualifierNode *wapp_cqualifier_list_pop_front(CQualifierList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CQualifierNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CQUALIFIER_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CQualifierList){0};
    goto RETURN_CQUALIFIER_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CQUALIFIER_LIST_POP_FRONT:
  return output;
}

CQualifierNode *wapp_cqualifier_list_pop_back(CQualifierList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CQualifierNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CQUALIFIER_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CQualifierList){0};
    goto RETURN_CQUALIFIER_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CQUALIFIER_LIST_POP_BACK:
  return output;
}

CQualifierNode *wapp_cqualifier_list_remove(CQualifierList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CQualifierNode *output = NULL;

  if (index == 0) {
    output = wapp_cqualifier_list_pop_front(list);
    goto RETURN_CQUALIFIER_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cqualifier_list_pop_back(list);
    goto RETURN_CQUALIFIER_LIST_REMOVE;
  }

  output = wapp_cqualifier_list_get(list, index);
  if (!output) {
    goto RETURN_CQUALIFIER_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CQUALIFIER_LIST_REMOVE:
  return output;
}

void wapp_cqualifier_list_empty(CQualifierList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cqualifier_list_pop_back(list);
  }
}

CIncludeNode *wapp_cinclude_list_get(const CIncludeList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CIncludeNode *output  = NULL;
  CIncludeNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cinclude_list_push_front(CIncludeList *list, CIncludeNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CIncludeList node_list = cinclude_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CIncludeNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cinclude_list_push_back(CIncludeList *list, CIncludeNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CIncludeList node_list = cinclude_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CIncludeNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cinclude_list_insert(CIncludeList *list, CIncludeNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cinclude_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cinclude_list_push_back(list, node);
    return;
  }

  CIncludeNode *dst_node = wapp_cinclude_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CIncludeList node_list = cinclude_node_to_list(node);

  list->node_count += node_list.node_count;

  CIncludeNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CIncludeNode *wapp_cinclude_list_pop_front(CIncludeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CIncludeNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CINCLUDE_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CIncludeList){0};
    goto RETURN_CINCLUDE_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CINCLUDE_LIST_POP_FRONT:
  return output;
}

CIncludeNode *wapp_cinclude_list_pop_back(CIncludeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CIncludeNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CINCLUDE_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CIncludeList){0};
    goto RETURN_CINCLUDE_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CINCLUDE_LIST_POP_BACK:
  return output;
}

CIncludeNode *wapp_cinclude_list_remove(CIncludeList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CIncludeNode *output = NULL;

  if (index == 0) {
    output = wapp_cinclude_list_pop_front(list);
    goto RETURN_CINCLUDE_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cinclude_list_pop_back(list);
    goto RETURN_CINCLUDE_LIST_REMOVE;
  }

  output = wapp_cinclude_list_get(list, index);
  if (!output) {
    goto RETURN_CINCLUDE_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CINCLUDE_LIST_REMOVE:
  return output;
}

void wapp_cinclude_list_empty(CIncludeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cinclude_list_pop_back(list);
  }
}

CUserTypeNode *wapp_cusertype_list_get(const CUserTypeList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CUserTypeNode *output  = NULL;
  CUserTypeNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cusertype_list_push_front(CUserTypeList *list, CUserTypeNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CUserTypeList node_list = cusertype_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CUserTypeNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cusertype_list_push_back(CUserTypeList *list, CUserTypeNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CUserTypeList node_list = cusertype_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CUserTypeNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cusertype_list_insert(CUserTypeList *list, CUserTypeNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cusertype_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cusertype_list_push_back(list, node);
    return;
  }

  CUserTypeNode *dst_node = wapp_cusertype_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CUserTypeList node_list = cusertype_node_to_list(node);

  list->node_count += node_list.node_count;

  CUserTypeNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CUserTypeNode *wapp_cusertype_list_pop_front(CUserTypeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CUserTypeNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CUSERTYPE_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CUserTypeList){0};
    goto RETURN_CUSERTYPE_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CUSERTYPE_LIST_POP_FRONT:
  return output;
}

CUserTypeNode *wapp_cusertype_list_pop_back(CUserTypeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CUserTypeNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CUSERTYPE_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CUserTypeList){0};
    goto RETURN_CUSERTYPE_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CUSERTYPE_LIST_POP_BACK:
  return output;
}

CUserTypeNode *wapp_cusertype_list_remove(CUserTypeList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CUserTypeNode *output = NULL;

  if (index == 0) {
    output = wapp_cusertype_list_pop_front(list);
    goto RETURN_CUSERTYPE_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cusertype_list_pop_back(list);
    goto RETURN_CUSERTYPE_LIST_REMOVE;
  }

  output = wapp_cusertype_list_get(list, index);
  if (!output) {
    goto RETURN_CUSERTYPE_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CUSERTYPE_LIST_REMOVE:
  return output;
}

void wapp_cusertype_list_empty(CUserTypeList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cusertype_list_pop_back(list);
  }
}

CFuncNode *wapp_cfunc_list_get(const CFuncList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CFuncNode *output  = NULL;
  CFuncNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cfunc_list_push_front(CFuncList *list, CFuncNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CFuncList node_list = cfunc_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CFuncNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cfunc_list_push_back(CFuncList *list, CFuncNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CFuncList node_list = cfunc_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CFuncNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cfunc_list_insert(CFuncList *list, CFuncNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cfunc_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cfunc_list_push_back(list, node);
    return;
  }

  CFuncNode *dst_node = wapp_cfunc_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CFuncList node_list = cfunc_node_to_list(node);

  list->node_count += node_list.node_count;

  CFuncNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CFuncNode *wapp_cfunc_list_pop_front(CFuncList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CFuncNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CFUNC_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CFuncList){0};
    goto RETURN_CFUNC_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CFUNC_LIST_POP_FRONT:
  return output;
}

CFuncNode *wapp_cfunc_list_pop_back(CFuncList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CFuncNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CFUNC_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CFuncList){0};
    goto RETURN_CFUNC_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CFUNC_LIST_POP_BACK:
  return output;
}

CFuncNode *wapp_cfunc_list_remove(CFuncList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CFuncNode *output = NULL;

  if (index == 0) {
    output = wapp_cfunc_list_pop_front(list);
    goto RETURN_CFUNC_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cfunc_list_pop_back(list);
    goto RETURN_CFUNC_LIST_REMOVE;
  }

  output = wapp_cfunc_list_get(list, index);
  if (!output) {
    goto RETURN_CFUNC_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CFUNC_LIST_REMOVE:
  return output;
}

void wapp_cfunc_list_empty(CFuncList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cfunc_list_pop_back(list);
  }
}

CStructNode *wapp_cstruct_list_get(const CStructList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CStructNode *output  = NULL;
  CStructNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cstruct_list_push_front(CStructList *list, CStructNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CStructList node_list = cstruct_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CStructNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cstruct_list_push_back(CStructList *list, CStructNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CStructList node_list = cstruct_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CStructNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cstruct_list_insert(CStructList *list, CStructNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cstruct_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cstruct_list_push_back(list, node);
    return;
  }

  CStructNode *dst_node = wapp_cstruct_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CStructList node_list = cstruct_node_to_list(node);

  list->node_count += node_list.node_count;

  CStructNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CStructNode *wapp_cstruct_list_pop_front(CStructList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CStructNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CSTRUCT_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CStructList){0};
    goto RETURN_CSTRUCT_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CSTRUCT_LIST_POP_FRONT:
  return output;
}

CStructNode *wapp_cstruct_list_pop_back(CStructList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CStructNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CSTRUCT_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CStructList){0};
    goto RETURN_CSTRUCT_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CSTRUCT_LIST_POP_BACK:
  return output;
}

CStructNode *wapp_cstruct_list_remove(CStructList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CStructNode *output = NULL;

  if (index == 0) {
    output = wapp_cstruct_list_pop_front(list);
    goto RETURN_CSTRUCT_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cstruct_list_pop_back(list);
    goto RETURN_CSTRUCT_LIST_REMOVE;
  }

  output = wapp_cstruct_list_get(list, index);
  if (!output) {
    goto RETURN_CSTRUCT_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CSTRUCT_LIST_REMOVE:
  return output;
}

void wapp_cstruct_list_empty(CStructList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cstruct_list_pop_back(list);
  }
}

CMacroNode *wapp_cmacro_list_get(const CMacroList *list, u64 index) {
  wapp_runtime_assert(index < list->node_count, "`index` is out of bounds");

  CMacroNode *output  = NULL;
  CMacroNode *current = list->first;
  for (u64 i = 1; i <= index; ++i) {
    current = current->next;
  }

  output = current;

  return output;
}

void wapp_cmacro_list_push_front(CMacroList *list, CMacroNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CMacroList node_list = cmacro_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CMacroNode *first = list->first;
  if (first) {
    first->prev = node_list.last;
  }

  list->first          = node_list.first;
  node_list.last->next = first;
}

void wapp_cmacro_list_push_back(CMacroList *list, CMacroNode *node) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  CMacroList node_list = cmacro_node_to_list(node);

  if (list->node_count == 0) {
    *list = node_list;
    return;
  }

  list->node_count += node_list.node_count;

  CMacroNode *last = list->last;
  if (last) {
    last->next = node_list.first;
  }

  list->last            = node_list.last;
  node_list.first->prev = last;
}

void wapp_cmacro_list_insert(CMacroList *list, CMacroNode *node, u64 index) {
  wapp_debug_assert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");

  if (index == 0) {
    wapp_cmacro_list_push_front(list, node);
    return;
  } else if (index == list->node_count) {
    wapp_cmacro_list_push_back(list, node);
    return;
  }

  CMacroNode *dst_node = wapp_cmacro_list_get(list, index);
  if (!dst_node) {
    return;
  }

  CMacroList node_list = cmacro_node_to_list(node);

  list->node_count += node_list.node_count;

  CMacroNode *prev = dst_node->prev;

  dst_node->prev = node_list.last;
  prev->next     = node_list.first;

  node_list.first->prev = prev;
  node_list.last->next  = dst_node;
}

CMacroNode *wapp_cmacro_list_pop_front(CMacroList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CMacroNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CMACRO_LIST_POP_FRONT;
  }

  output = list->first;

  if (list->node_count == 1) {
    *list = (CMacroList){0};
    goto RETURN_CMACRO_LIST_POP_FRONT;
  }

  --(list->node_count);
  list->first = output->next;

  output->prev = output->next = NULL;

RETURN_CMACRO_LIST_POP_FRONT:
  return output;
}

CMacroNode *wapp_cmacro_list_pop_back(CMacroList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CMacroNode *output = NULL;

  if (list->node_count == 0) {
    goto RETURN_CMACRO_LIST_POP_BACK;
  }

  output = list->last;

  if (list->node_count == 1) {
    *list = (CMacroList){0};
    goto RETURN_CMACRO_LIST_POP_BACK;
  }

  --(list->node_count);
  list->last = output->prev;

  output->prev = output->next = NULL;

RETURN_CMACRO_LIST_POP_BACK:
  return output;
}

CMacroNode *wapp_cmacro_list_remove(CMacroList *list, u64 index) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  CMacroNode *output = NULL;

  if (index == 0) {
    output = wapp_cmacro_list_pop_front(list);
    goto RETURN_CMACRO_LIST_REMOVE;
  } else if (index == list->node_count) {
    output = wapp_cmacro_list_pop_back(list);
    goto RETURN_CMACRO_LIST_REMOVE;
  }

  output = wapp_cmacro_list_get(list, index);
  if (!output) {
    goto RETURN_CMACRO_LIST_REMOVE;
  }

  output->prev->next = output->next;
  output->next->prev = output->prev;

  --(list->node_count);

  output->prev = output->next = NULL;

RETURN_CMACRO_LIST_REMOVE:
  return output;
}

void wapp_cmacro_list_empty(CMacroList *list) {
  wapp_debug_assert(list != NULL, "`list` should not be NULL");

  u64 count = list->node_count;
  for (u64 i = 0; i < count; ++i) {
    wapp_cmacro_list_pop_back(list);
  }
}

wapp_intern CEnumValList cenumval_node_to_list(CEnumValNode *node) {
  CEnumValList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CArgList carg_node_to_list(CArgNode *node) {
  CArgList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CQualifierList cqualifier_node_to_list(CQualifierNode *node) {
  CQualifierList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CIncludeList cinclude_node_to_list(CIncludeNode *node) {
  CIncludeList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CUserTypeList cusertype_node_to_list(CUserTypeNode *node) {
  CUserTypeList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CFuncList cfunc_node_to_list(CFuncNode *node) {
  CFuncList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CStructList cstruct_node_to_list(CStructNode *node) {
  CStructList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}

wapp_intern CMacroList cmacro_node_to_list(CMacroNode *node) {
  CMacroList output = {.first = node, .last = node, .node_count = 1};

  while (output.first->prev != NULL) {
    output.first = output.first->prev;
    ++(output.node_count);
  }

  while (output.last->next != NULL) {
    output.last = output.last->next;
    ++(output.node_count);
  }

  return output;
}
