#pragma once

// ::DataStructures::RedBlackTree::Header::

#define NodeDir(node) (node == node->parent->left ? RB_LEFT : RB_RIGHT)
#define RBQueuePop(f, l) SLLQueuePop(P_RB_DN_NIL, f, l)
#define RBQueuePush(f, l, n) SLLQueuePush(P_RB_DN_NIL, f, l, n)
#define RBQueuePushFront(f, l, n) SLLQueuePush(P_RB_DN_NIL, f, l, n)

typedef enum RBNodeColor_e
{
	RB_RED,
	RB_BLACK,
} RBNodeColor;

typedef enum RBNodeDir_e
{
	RB_LEFT,
	RB_RIGHT,
} RBNodeDir;

typedef struct RBDataNode
{
	rawptr 						 data;
	struct RBDataNode *next;
} RBDataNode;

typedef struct RBBucket
{
	RBDataNode *first;
	RBDataNode *last;
} RBBucket;

typedef struct RBNode
{
	union 
	{
		struct 
		{
			struct RBNode *left;
			struct RBNode *right;
		};
		struct RBNode *child[2];
	};
	struct RBNode *parent;
	u64 key;
	RBBucket bucket;
	RBNodeColor color;
} RBNode;

typedef struct RBTree
{
	RBNode *root;
	RBNode *nil;
} RBTree;

static void RBTreeInit     (RBTree *tree);
static inline RBNode *RBTreeInitNode(u64 key, rawptr value);
static inline void RBTreePushDataNode(RBDataNode *first, RBDataNode *last, rawptr value);
static void RBTreeInsert(RBTree *tree, u64 key, rawptr value);
static b32 RBTreeSearchNearest(RBTree *tree, u64 key, RBNode **node);
static b32  RBTreeSearch     (RBTree *tree, u64 key, RBNode **node);
static void RBTreeDelete     (RBTree *tree, u64 key, rawptr value);
static void RBTreeLeftRotate (RBTree *tree, RBNode *node);
static void RBTreeRightRotate(RBTree *tree, RBNode *node);
static void RBTreeRotate     (RBTree *tree, RBNode *node, RBNodeDir dir);
static void RBTreeCorrect    (RBTree *tree, RBNode *node);
static void RBTreeTransplant (RBTree *tree, RBNode *node, RBNode *placed_node);

// ::DataStructures::HashTable::Functions::Header::

#define HTQueuePop(f, l) SLLQueuePop(P_HT_NIL, f, l)
#define HTQueuePush(f, l, n) SLLQueuePush(P_HT_NIL, f, l, n)
#define HTQueuePushFront(f, l, n) SLLQueuePush(P_HT_NIL, f, l, n)

typedef struct KeyValuePair 
{
	union
	{
		u64 	  		key_u64;
		rawptr  		key_rawptr;
	};
	union
	{
		String8 		value_string;
		rawptr 			value_rawptr;
		u32    			value_u32;
		u64    			value_u64;
		U64Split 		value_u64_split;
	};
} KeyValuePair;

typedef struct HashNode
{
	struct HashNode *next;
	KeyValuePair 		v;
} HashNode;

typedef struct HashList
{
	HashNode *first;
	HashNode *last;
} HashList;
                                                                   
typedef struct HashTable                                        
{                                                                  
	HashList *lists;
	HashList free_lists;
	u64 count;
	u32 cap;
} HashTable;
                                                                   
static void HashTableInit(HashTable *table, u32 init_size); 
static void HashTableClear(HashTable *table);
static void HashTableConcatInPlace(HashList *list, HashList *to_concat);
static u64 HashTableHash(String8 str);
static HashNode *HashListPop(HashList *list);
static KeyValuePair *HashTableSearchU64(HashTable *table, u64 key);
static KeyValuePair *HashTableSearchRawptr(HashTable *table, rawptr key);
static HashNode *HashTablePush(HashTable *table, u64 hash, KeyValuePair value);
static HashNode *HashTablePushU64U32(HashTable *table, u64 key, u32 value);
static HashNode *HashTablePushU64U64(HashTable *table, u64 key, u64 value);
static HashNode *HashTablePushU64String8(HashTable *table, u64 key, String8 value);
static HashNode *HashTablePushU64Rawptr(HashTable *table, u64 key, rawptr value);
static HashNode *HashTablePushU64U64Split(HashTable *table, u64 key, u32 upper, u32 lower);
static rawptr HashTableDeleteU64Rawptr(HashTable *table, u64 key);
static void HashTableDeleteU64(HashTable *table, u64 key);

