#pragma once

#include <assert.h>

typedef struct Arena Arena;

// ::Util::Macros::Header::

#define Assert(condition, message) do { assert((condition) && (message)); } while(0)

#define KB(n) n			* 1024LL
#define MB(n) KB(n) * 1024LL
#define GB(n) MB(n) * 1024LL
#define TB(n) GB(n) * 1024LL

#define BitEq(var, bits) (((var) & (bits)) == (bits))
#define AlignPow2(x, b)	(((x) + (b) - 1) & (~((b) - 1)))	
#define IsPow2(x)				((x) != 0 && ((x) &((x) - 1)) == 0)
#define PtrAdd(ptr, add) (((u8 *)ptr) + add)
#define PtrAddAdjustLen(ptr, len, add) \
ptr = PtrAdd(ptr, add); \
len -= add

#define Diff(value, sub) (value - sub)
#define MakeArray(arena, type, count) ArenaAlloc(arena, (isize)(sizeof(type)) * (isize)(count))
#define Len(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

#define MakeString(x) #x

#define i8(x) ((i8)(x))
#define i16(x) ((i16)(x))
#define i32(x) ((i32)(x))
#define i64(x) ((i64)(x))

#define u8(x) ((u8)(x))
#define u16(x) ((u16)(x))
#define u32(x) ((u32)(x))
#define u64(x) ((u64)(x))

#define c8(x) ((c8)(x))
#define c8ptr(x) ((c8 *)(x))
#define u8ptr(x) ((u8 *)(x))
#define i8ptr(x) ((i8 *)(x))

#define intptr(x) ((intptr)(x))
#define uintptr(x) ((uintptr)(x))

#define f32(x) ((f32)(x))
#define f64(x) ((f64)(x))

#define rawptr(x) ((rawptr)(x))

#define cast(T, x) ((T)(x))

#define DefScalarSig(def) \
	DefSig##def(i8); \
	DefSig##def(i16); \
	DefSig##def(i32); \
	DefSig##def(i64); \
	DefSig##def(u8); \
	DefSig##def(u16); \
	DefSig##def(u32); \
	DefSig##def(u64); \
	DefSig##def(b8); \
	DefSig##def(b32)

#define DefScalarImpl(def) \
	Def##def(i8); \
	Def##def(i16); \
	Def##def(i32); \
	Def##def(i64); \
	Def##def(u8); \
	Def##def(u16); \
	Def##def(u32); \
	Def##def(u64); \
	Def##def(b8); \
	Def##def(b32)

#define ArrayType(T) \
typedef struct T##Array \
{ \
	T 	*data; \
	u64 length; \
	u64 cap; \
} T##Array

#define PtrArrayType(T) \
typedef struct T##PtrArray \
{ \
 	T 	**data; \
	u64 	length; \
	u64 	cap; \
} T##PtrArray

#define InitArrayType(arr, arena, T, len) \
arr.data = MakeArray(arena, T, len); \
arr.length = 0; \
arr.cap = len

// ::Util::LinkedList::Macros::

#define CheckNil(nil, p) ((p) == 0 || (p) == nil)
#define SetNil(nil, p) ((p) = nil)

// ::Util::DoubleHeadedLinkedList::Macros::

#define SLLConcatInPlaceNoCount(list, to_concat) do { \
	if ((to_concat)->first)                             \
	{                                                   \
		if ((list)->first) \
		{ \
			(list)->last->next = (to_concat)->first; \
			(list)->last = (to_concat)->last; \
		}	\
		else \
		{ \
			(list)->first = (to_concat)->first; \
			(list)->last = (to_concat)->last; \
		} \
		MemZero(to_concat, sizeof(*to_concat)); \
	} \
} while (0)
#define _SLLQueuePop(nil, f, l, next) ((f) == (l) ? \
	(SetNil(nil, f), SetNil(nil, l)) : 								\
	((f) = (f)->next))
#define _SLLQueuePushFront(nil, f, l, n, next) (CheckNil(nil, f) ?  \
	((f) = (l) = (n), SetNil(nil, (n)->next)) : 											\
	((n)->next = (f), (f) = (n)))
#define _SLLQueuePush(nil, f, l, n, next) (CheckNil(nil, f) ? \
	((f) = (l) = (n), SetNil(nil, (n)->next)) :									\
	((l)->next = (n), (l) = (n), SetNil(nil, (n)->next)))

#define SLLQueuePop(nil, f, l) _SLLQueuePop(nil, f, l, next)
#define SLLQueuePush(nil, f, l, n) _SLLQueuePush(nil, f, l, n, next)
#define SLLQueuePushFront(nil, f, l, n) _SLLQueuePushFront(nil, f, l, n, next)

// ::Util::String8::Macros::

#define String8Array(v, c) MakeString8((u8 *)(v), sizeof(*(v))*(c)) 
#define String8Struct(v) MakeString8((u8 *)(v), sizeof(*(v)))
#define String8CStr(v) MakeString8((u8 *)v, StrLen(v))

// ::Util::Defines::Header::

#define HM_MAX_SYMBOLS 256
#define DEFAULT_ALIGNMENT (2*sizeof(rawptr))
#define HASH_SEED 5995

// ::Util::Strings::Functions::Header::

String8 MakeString8(u8 *str, u64 len);
u32 StrLen(const char *str);
b32 StrEq(const char *l, const char *r);
i32 SPrintf(char *buf, rawptr fmt, ...);
String8 PreSplitString8(String8 string, String8 delimiter);
String8 PreSplitNewString8(Arena *arena, String8 string, String8 delimiter);
static void StrConcat(c8 *str, c8 *append);

// ::Util::Memory::Functions::Header::

void MemZero(void *ptr, isize size);
void MemCpy(rawptr dst, rawptr src, usize len);

// ::Util::Math::Functions::Header::

#define DefIntegerImpl(def) \
	Def##def(i8); \
	Def##def(i16); \
	Def##def(i32); \
	Def##def(i64); \
	Def##def(u8); \
	Def##def(u16); \
	Def##def(u32); \
	Def##def(u64)

#define DefFloatImpl(def) \
	Def##def(f32); \
	Def##def(f64)

#define DefMathImpl(def) \
	DefIntegerImpl(def); \
	DefFloatImpl(def)

#define DefMin(T) \
T Min##T(T l, T r) \
{ \
	return l < r ? l : r; \
}

#define DefMax(T) \
T Max##T(T l, T r) \
{ \
	return l > r ? l : r; \
}

#define DefClamp(T) \
T Clamp##T(T v, T min, T max) \
{ \
	return Min##T(max, Max##T(v, min)); \
}

#define DefAbs(T) \
T Abs##T(T v) \
{ \
	return v < (T)0 ? -v : v; \
}

// ::Util::Hashing::Functions::Header::

u64 static inline HashFromString(String8 string);

// ::Util::Print::Functions::Header::

i32 EPrint(void *str);
i32 Printf(char *fmt, ...);
i32 Printfln(char *fmt, ...);
i32 EPrintf(char *fmt, ...);
i32 _EPrint(void *str);
i32 _Printf(char *fmt, va_list arg);
i32 _Printfln(char *fmt, va_list arg);
i32 _EPrintf(char *fmt, va_list arg);

// ::Util::Profiling::Header::

#define NameConcat2(a, b) a##b

#define NameConcat(a, b) NameConcat2(a, b)

#define TimeBlockStart(name) ProfileBlock NameConcat(Block, name) = {0}; \
StartProfileBlock(&NameConcat(Block, name), #name, __COUNTER__ + 1)

#define TimeBlockEnd(name) EndProfileBlock(&NameConcat(Block, name))

#define TimeBlockFuncStart(name) ProfileBlock NameConcat(Block, function) = {0}; \
StartProfileBlock(& NameConcat(Block, function), name, __COUNTER__ + 1)

#define TimeFunctionStart TimeBlockFuncStart(__func__)

#define TimeFunctionEnd TimeBlockEnd(function)

typedef struct ProfileAnchor
{
	u64 tsc_elapsed;
	u64 hit_count;
	c8 *label;
} ProfileAnchor;

typedef struct ProfileBlock
{
	c8 *label;
	u64 start_tsc;
	u32 anchor_index;
} ProfileBlock;

typedef struct Profiler
{
	ProfileAnchor anchors[4096];
	u64 start_tsc;
	u64 end_tsc;
} Profiler;

static inline void StartProfileBlock(ProfileBlock *block, c8 *label, u32 anchor_index);
static inline void EndProfileBlock(ProfileBlock *block);


// ::Util::Async::Header::

typedef struct TicketMut
{
	u32 volatile ticket;
	u32 volatile next_ticket;
} TicketMut;

typedef struct Mut
{
	b32 volatile lock;
} Mut;

typedef struct JobQueue
{
	u32 volatile queued;
	u32 volatile remaining;
} JobQueue;

static inline b32 MutTryLock(Mut *mut);
static inline void MutUnlock(Mut *mut);
static inline void TicketMutLock(TicketMut *mut);
static inline void TicketMutUnlock(TicketMut *mut);
static inline u32 JobQueueAdd(JobQueue *queue, u32 count);
static inline u32 JobQueueGetCount(JobQueue *queue);
static inline void JobQueueMarkUnqueued(JobQueue *queue, u32 count);
static inline void JobQueueMarkCompleted(JobQueue *queue, u32 count);
static inline void JobQueueReset(JobQueue *queue);
static inline b32 JobQueueCompleted(JobQueue *queue);
