#pragma once

// ::Allocator::Util::Header::

static inline usize CalcPaddingWithHeader(uintptr ptr, uintptr alignment, usize header_size);
static inline usize CalcPadding(uintptr ptr, uintptr alignment);

// ::Allocator::Arena::Header::

#define ARENA_HEADER_SIZE 64

typedef struct Arena
{
	u8		*buffer;
	usize length;
	usize pos;
	u32   init_line_no;
} Arena;

typedef struct TempArena
{
	Arena *arena;
	u64   pos;
} TempArena;

static Arena *ArenaInit(rawptr buffer, usize size);
static Arena *ArenaCreate(usize size);
static Arena *ArenaCreateDebug(usize size, u32 init_line_no);
static Arena *ArenaInitDebug(rawptr buffer, usize size, u32 init_line_no);
static rawptr ArenaAllocAlign(Arena *arena, usize size, usize align);
static rawptr ArenaAlloc(Arena *arena, usize size);
static void ArenaFree(Arena *arena);
static void ArenaFreeZeroed(Arena *arena);
static void DeallocArena(Arena *arena);

// ::Allocator::GlobalAlloc::Header::

typedef struct AllocInfo
{
	rawptr 						ptr;
	struct AllocInfo *next;
} AllocInfo;

typedef struct Allocator
{
	RBTree 				 *tree;
	RBTree				 *used_tree;
	HashTable			 *hash_table;
	rawptr				 buffer;
	u64						 size;
	u64						 free_size;
	u64	           grow_size;
} Allocator;

static void InitAllocator(usize init_size);
static void DeinitAlloc();
static void AllocGrow(usize size);
static rawptr Alloc(usize size);
static rawptr AllocAlign(usize size, usize alignment);
static void Free(rawptr ptr);

// ::Allocator::FreeList::Header::

typedef struct FLAllocHeader
{
	usize size;
	usize padding;
} FLAllocHeader;

typedef struct FLNode
{
	struct FLNode *next;
	usize  				size;
} FLNode;

typedef struct FreeList
{
	rawptr 		data;
	FLNode 	 *head;
	usize  		size;
	usize  		used;
	struct FreeList *next;
} FreeList;

typedef struct FLAlloc
{
	FreeList 	*list_head;
	TicketMut mut;
	FLNode   	*nil;
	usize    	grow_size;
} FLAlloc;

static void GlobalFreeListInit(usize size);
static rawptr FLMemAlloc(usize size);
static rawptr FLMemAllocZeroed(usize size);
static rawptr FLMemRealloc(rawptr old_ptr, usize size);
static void FLMemFree(rawptr ptr);
static usize FreeListPtrSize(FLAlloc *alloc, rawptr ptr);
static void FreeListInit(FLAlloc *alloc, usize size);
static rawptr FreeListAllocAlign(FLAlloc *alloc, usize size, usize alignment);
static rawptr FreeListAlloc(FLAlloc *alloc, usize size);
static void FreeListFree(FLAlloc *alloc, rawptr ptr);
static void FreeListFreeAll(FLAlloc *alloc);
static FreeList *FreeListGrow(FLAlloc *alloc, usize alloc_size);
static rawptr FreeListRealloc(FLAlloc *alloc, rawptr old_ptr, usize size);
static FLNode *FreeListSearch(FreeList *alloc, usize size, usize alignment, usize *out_padding, FLNode **prev_node);
static void FreeListCoalescence(FreeList *alloc, FLNode *prev_node, FLNode *free_node);
static void FreeListRemove(FLNode **head, FLNode *prev_node, FLNode *del_node);
static void FreeListInsert(FLNode **head, FLNode *prev_node, FLNode *new_node);
static FreeList *_FreeListFindList(FLAlloc *alloc, rawptr ptr);
static void _FreeListInit(FreeList **alloc, usize size);
static rawptr _FreeListAllocAlign(FreeList *alloc, usize size, usize alignment);
static void _FreeListFree(FreeList *alloc, rawptr ptr);

