#ifndef DBL_LIST_H
#define DBL_LIST_H

#include "../mem/allocator/mem_allocator.h"
#include "../../common/aliases/aliases.h"
#include "../../common/platform/platform.h"

#ifdef WAPP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WAPP_PLATFORM_CPP

#define WAPP_DBL_LIST_MAGIC (u64)0x57415f444c5354
#define WAPP_DBL_NODE_MAGIC (u64)0x57415f444e44

#define WAPP_DEF_DBL_LIST_TYPE(T, NODE_NAME, LIST_NAME) \
  typedef struct NODE_NAME NODE_NAME;                   \
  struct NODE_NAME {                                    \
    u64 magic;                                          \
    T *item;                                            \
    NODE_NAME *prev;                                    \
    NODE_NAME *next;                                    \
    u64 item_size;                                      \
  };                                                    \
                                                        \
  typedef struct {                                      \
    u64 magic;                                          \
    NODE_NAME *first;                                   \
    NODE_NAME *last;                                    \
    u64 node_count;                                     \
    u64 item_size;                                      \
  } LIST_NAME

#ifdef WAPP_PLATFORM_CPP
#define wapp_dbl_list(ELEM_TYPE, LIST_TYPE)                                     \
  LIST_TYPE{WAPP_DBL_LIST_MAGIC, nullptr, nullptr, 0, sizeof(ELEM_TYPE)}
#define wapp_dbl_list_node(ELEM_TYPE, NODE_TYPE, ELEM_PTR)                      \
  NODE_TYPE{WAPP_DBL_NODE_MAGIC, ELEM_PTR, nullptr, nullptr, sizeof(ELEM_TYPE)}
#else
#define wapp_dbl_list(ELEM_TYPE, LIST_TYPE) (                                                 \
  (LIST_TYPE){.magic = WAPP_DBL_LIST_MAGIC, .item_size = sizeof(ELEM_TYPE)}                   \
)
#define wapp_dbl_list_node(ELEM_TYPE, NODE_TYPE, ELEM_PTR) (                                  \
  (NODE_TYPE){.magic = WAPP_DBL_NODE_MAGIC, .item = ELEM_PTR, .item_size = sizeof(ELEM_TYPE)} \
)
#endif // !WAPP_PLATFORM_CPP

#define wapp_dbl_list_alloc(ELEM_TYPE, LIST_TYPE, ALLOCATOR)                                        \
  (LIST_TYPE *)_dbl_list_alloc(ALLOCATOR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_node_alloc(ELEM_TYPE, NODE_TYPE, ALLOCATOR)                                   \
  (NODE_TYPE *)_dbl_list_node_alloc(ALLOCATOR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_get(ELEM_TYPE, NODE_TYPE, LIST_PTR, ELEM_INDEX)                               \
  (NODE_TYPE *)_dbl_list_get((GenericList *)LIST_PTR, ELEM_INDEX, sizeof(ELEM_TYPE))
#define wapp_dbl_list_push_front(ELEM_TYPE, LIST_PTR, NODE_PTR)                                     \
  _dbl_list_push_front((GenericList *)LIST_PTR, (GenericNode *)NODE_PTR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_push_back(ELEM_TYPE, LIST_PTR, NODE_PTR)                                      \
  _dbl_list_push_back((GenericList *)LIST_PTR, (GenericNode *)NODE_PTR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_insert(ELEM_TYPE, LIST_PTR, NODE_PTR, ELEM_INDEX)                             \
  _dbl_list_insert((GenericList *)LIST_PTR, (GenericNode *)NODE_PTR, ELEM_INDEX, sizeof(ELEM_TYPE))
#define wapp_dbl_list_pop_front(ELEM_TYPE, NODE_TYPE, LIST_PTR)                                     \
  (NODE_TYPE *)_dbl_list_pop_front((GenericList *)LIST_PTR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_pop_back(ELEM_TYPE, NODE_TYPE, LIST_PTR)                                      \
  (NODE_TYPE *)_dbl_list_pop_back((GenericList *)LIST_PTR, sizeof(ELEM_TYPE))
#define wapp_dbl_list_remove(ELEM_TYPE, NODE_TYPE, LIST_PTR, ELEM_INDEX)                            \
  (NODE_TYPE *)_dbl_list_remove((GenericList *)LIST_PTR, ELEM_INDEX, sizeof(ELEM_TYPE))
#define wapp_dbl_list_empty(ELEM_TYPE, LIST_PTR)                                                    \
  _dbl_list_empty((GenericList *)LIST_PTR, sizeof(ELEM_TYPE))

WAPP_DEF_DBL_LIST_TYPE(void, GenericNode, GenericList);

GenericList *_dbl_list_alloc(const Allocator *allocator, u64 item_size);
GenericNode *_dbl_list_node_alloc(const Allocator *allocator, u64 item_size);
GenericNode *_dbl_list_get(const GenericList *list, u64 index, u64 item_size);
void         _dbl_list_push_front(GenericList *list, GenericNode *node, u64 item_size);
void         _dbl_list_push_back(GenericList *list, GenericNode *node, u64 item_size);
void         _dbl_list_insert(GenericList *list, GenericNode *node, u64 index, u64 item_size);
GenericNode *_dbl_list_pop_front(GenericList *list, u64 item_size);
GenericNode *_dbl_list_pop_back(GenericList *list, u64 item_size);
GenericNode *_dbl_list_remove(GenericList *list, u64 index, u64 item_size);
void         _dbl_list_empty(GenericList *list, u64 item_size);

// Base list types
typedef struct str8 Str8;

WAPP_DEF_DBL_LIST_TYPE(void *, VoidPtrNode, VoidPtrList);
WAPP_DEF_DBL_LIST_TYPE(c8    , C8Node     , C8List);
WAPP_DEF_DBL_LIST_TYPE(c16   , C16Node    , C16List);
WAPP_DEF_DBL_LIST_TYPE(c32   , C32Node    , C32List);
WAPP_DEF_DBL_LIST_TYPE(u8    , U8Node     , U8List);
WAPP_DEF_DBL_LIST_TYPE(u16   , U16Node    , U16List);
WAPP_DEF_DBL_LIST_TYPE(u32   , U32Node    , U32List);
WAPP_DEF_DBL_LIST_TYPE(u64   , U64Node    , U64List);
WAPP_DEF_DBL_LIST_TYPE(b8    , B8Node     , B8List);
WAPP_DEF_DBL_LIST_TYPE(i8    , I8Node     , I8List);
WAPP_DEF_DBL_LIST_TYPE(i16   , I16Node    , I16List);
WAPP_DEF_DBL_LIST_TYPE(i32   , I32Node    , I32List);
WAPP_DEF_DBL_LIST_TYPE(i64   , I64Node    , I64List);
WAPP_DEF_DBL_LIST_TYPE(f32   , F32Node    , F32List);
WAPP_DEF_DBL_LIST_TYPE(f64   , F64Node    , F64List);
WAPP_DEF_DBL_LIST_TYPE(f128  , F128Node   , F128List);
WAPP_DEF_DBL_LIST_TYPE(uptr  , UptrNode   , UptrList);
WAPP_DEF_DBL_LIST_TYPE(iptr  , IptrNode   , IptrList);
WAPP_DEF_DBL_LIST_TYPE(Str8  , Str8Node   , Str8List);

#ifdef WAPP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WAPP_PLATFORM_CPP

#endif // !DBL_LIST_H
