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

#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 WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP

#define WP_DBL_LIST_MAGIC (u64)0x57415f444c5354
#define WP_DBL_NODE_MAGIC (u64)0x57415f444e44

typedef struct WpDblNode WpDblNode;

typedef struct {
	u64 magic;
	u64 item_size;
	WpDblNode *prev;
	WpDblNode *next;
} WpDblNodeHeader;

struct WpDblNode {
	WpDblNodeHeader header;
	void *item;
};

typedef struct {
	u64 magic;
	u64 node_count;
	u64 item_size;
	WpDblNode *first;
	WpDblNode *last;
} WpDblList;

// NOTE (Abdelrahman): WpDblList typedefs for readability
typedef WpDblList WpVoidPtrList;
typedef WpDblList WpC8List;
typedef WpDblList WpC16List;
typedef WpDblList WpC32List;
typedef WpDblList WpU8List;
typedef WpDblList WpU16List;
typedef WpDblList WpU32List;
typedef WpDblList WpU64List;
typedef WpDblList WpB8List;
typedef WpDblList WpI8List;
typedef WpDblList WpI16List;
typedef WpDblList WpI32List;
typedef WpDblList WpI64List;
typedef WpDblList WpF32List;
typedef WpDblList WpF64List;
typedef WpDblList WpF128List;
typedef WpDblList WpUptrList;
typedef WpDblList WpIptrList;
typedef WpDblList WpStr8List;

// NOTE (Abdelrahman): WpDblNode typedefs for readability
typedef WpDblNode WpVoidPtrNode;
typedef WpDblNode WpC8Node;
typedef WpDblNode WpC16Node;
typedef WpDblNode WpC32Node;
typedef WpDblNode WpU8Node;
typedef WpDblNode WpU16Node;
typedef WpDblNode WpU32Node;
typedef WpDblNode WpU64Node;
typedef WpDblNode WpB8Node;
typedef WpDblNode WpI8Node;
typedef WpDblNode WpI16Node;
typedef WpDblNode WpI32Node;
typedef WpDblNode WpI64Node;
typedef WpDblNode WpF32Node;
typedef WpDblNode WpF64Node;
typedef WpDblNode WpF128Node;
typedef WpDblNode WpUptrNode;
typedef WpDblNode WpIptrNode;
typedef WpDblNode WpStr8Node;

#ifdef WP_PLATFORM_CPP
#define wpDblList(TYPE)                                                                            \
	WpDblList{WP_DBL_LIST_MAGIC, 0, sizeof(TYPE), nullptr, nullptr}
#define _dblListNode(TYPE, ITEM_PTR) ([&]() {                                                      \
	wp_persist WpDblNode node = {                                                                    \
		WpDblNodeHeader{WP_DBL_NODE_MAGIC, sizeof(TYPE), nullptr, nullptr},                            \
		ITEM_PTR,                                                                                      \
	};                                                                                               \
                                                                                                   \
	return &node;                                                                                    \
}())
#else
#define wpDblList(TYPE) (                                                                          \
	(WpDblList){.magic = WP_DBL_LIST_MAGIC, .item_size = sizeof(TYPE)}                               \
)
#define _dblListNode(TYPE, ITEM_PTR) (                                                             \
	&((WpDblNode){.header = {.magic = WP_DBL_NODE_MAGIC, .item_size = sizeof(TYPE)},                 \
	                .item = ITEM_PTR})                                                               \
)
#endif // !WP_PLATFORM_CPP

#define wpDblListAlloc(TYPE, ALLOCATOR)                                                            \
	(_dblListAlloc(ALLOCATOR, sizeof(TYPE)))
#define wpDblListGet(TYPE, LIST_PTR, ITEM_INDEX)                                                   \
	((TYPE *)(_dblListGet(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item))
#define wpDblListGetNode(TYPE, LIST_PTR, ITEM_INDEX)                                               \
	(_dblListGet(LIST_PTR, ITEM_INDEX, sizeof(TYPE)))
#define wpDblListGetNodeItem(TYPE, NODE_PTR)                                                       \
	((TYPE *)(                                                                                       \
		(NODE_PTR == NULL) ?                                                                           \
		NULL :                                                                                         \
		(NODE_PTR)->item                                                                               \
	))
#define wpDblListPushFront(TYPE, LIST_PTR, ITEM_PTR)                                               \
	(_dblListPushFront(LIST_PTR, _dblListNode(TYPE, ITEM_PTR), sizeof(TYPE)))
#define wpDblListPushBack(TYPE, LIST_PTR, ITEM_PTR)                                                \
	(_dblListPushBack(LIST_PTR, _dblListNode(TYPE, ITEM_PTR), sizeof(TYPE)))
#define wpDblListInsert(TYPE, LIST_PTR, ITEM_PTR, ITEM_INDEX)                                      \
	(_dblListInsert(LIST_PTR, _dblListNode(TYPE, ITEM_PTR),                                          \
	                  ITEM_INDEX, sizeof(TYPE)))
#define wpDblListPushFrontAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR)                               \
	(_dblListPushFront(LIST_PTR, _dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)),               \
	                      sizeof(TYPE)))
#define wpDblListPushBackAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR)                                \
	(_dblListPushBack(LIST_PTR, _dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)),                \
	                     sizeof(TYPE)))
#define wpDblListInsertAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR, ITEM_INDEX)                      \
	(_dblListInsert(LIST_PTR, _dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)),                  \
	                  ITEM_INDEX, sizeof(TYPE)))
#define wpDblListPopFront(TYPE, LIST_PTR)                                                          \
	((TYPE *)(                                                                                       \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ?                                            \
		NULL :                                                                                         \
		_dblListPopFront(LIST_PTR, sizeof(TYPE))->item                                                 \
	))
#define wpDblListPopBack(TYPE, LIST_PTR)                                                           \
	((TYPE *)(                                                                                       \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ?                                            \
		NULL :                                                                                         \
		_dblListPopBack(LIST_PTR, sizeof(TYPE))->item                                                  \
	))
#define wpDblListRemove(TYPE, LIST_PTR, ITEM_INDEX)                                                \
	((TYPE *)(                                                                                       \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ?    \
		NULL :                                                                                         \
		_dblListRemove(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item                                       \
	))
#define wpDblListPopFrontNode(TYPE, LIST_PTR)                                                      \
	(                                                                                                \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ?                                            \
		NULL :                                                                                         \
		_dblListPopFront(LIST_PTR, sizeof(TYPE))                                                       \
	)
#define wpDblListPopBackNode(TYPE, LIST_PTR)                                                       \
	(                                                                                                \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ?                                            \
		NULL :                                                                                         \
		_dblListPopBack(LIST_PTR, sizeof(TYPE))                                                        \
	)
#define wpDblListRemoveNode(TYPE, LIST_PTR, ITEM_INDEX)                                            \
	(                                                                                                \
		(LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ?    \
		NULL :                                                                                         \
		_dblListRemove(LIST_PTR, ITEM_INDEX, sizeof(TYPE))                                             \
	)
#define wpDblListEmpty(TYPE, LIST_PTR)                                                             \
	(_dblListEmpty(LIST_PTR, sizeof(TYPE)))

WpDblList *_dblListAlloc(const WpAllocator *allocator, u64 item_size);
WpDblNode *_dblListNodeAlloc(const WpAllocator *allocator, void *item, u64 item_size);
WpDblNode *_dblListGet(const WpDblList *list, u64 index, u64 item_size);
void       _dblListPushFront(WpDblList *list, WpDblNode *node, u64 item_size);
void       _dblListPushBack(WpDblList *list, WpDblNode *node, u64 item_size);
void       _dblListInsert(WpDblList *list, WpDblNode *node, u64 index, u64 item_size);
WpDblNode *_dblListPopFront(WpDblList *list, u64 item_size);
WpDblNode *_dblListPopBack(WpDblList *list, u64 item_size);
WpDblNode *_dblListRemove(WpDblList *list, u64 index, u64 item_size);
void       _dblListEmpty(WpDblList *list, u64 item_size);

#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP

#endif // !DBL_LIST_H
