// ::Util::Globals::Start::

static Profiler GLOBAL_PROFILER;

// ::Util::Globals::End::



// ::Util::Strings::Functions::Start::

static void StrConcat(c8 *str, c8 *append)
{
	u32 len = StrLen(str);
	u32 i = 0;
	for (; append[i] != '\0'; i += 1)
	{
		str[i] = append[i];
	}

	str[i] = '\0';
}

b32 StrEq(const char *l, const char *r) {
	for (; *l == *r && *l; l++, r++);
	return *l == '\0' && *r == '\0';
}

u32 StrLen(const char *str) 
{
	const char *start;
	
	for (start = str; *str != '\0'; ++str)
		/* Iterate only */;

	return (u32)(str - start);
}

i32 SPrintf(char *buf, rawptr fmt, ...) 
{
	va_list arg;

	va_start(arg, fmt);
	int sprf_res = stbsp_vsnprintf(buf, sizeof(buf), fmt, arg);
	va_end(arg);

	return sprf_res;
}

String8 MakeString8(u8 *str, u64 len)
{
	return (String8){ .len = len, .value = str };
}

String8 PreSplitString8(String8 string, String8 delimiter)
{
	if (string.len == 0 || delimiter.len == 0) return string;

	u32 new_len = 0;
	u32 matched_chars = 0;

	for (u32 i = 0; i < string.len; i++)
	{

		if (string.value[i] == delimiter.value[matched_chars])
			matched_chars += 1;
		else
			matched_chars = 0;

		if (matched_chars == delimiter.len || delimiter.value[matched_chars+1] == '\0') 
		{
			new_len = i;
			break;
		}
	}

	return (String8){ .len = new_len, .value = string.value };
}

String8 PreSplitNewString8(Arena *arena, String8 string, String8 delimiter)
{
	String8 result = PreSplitString8(string, delimiter);
	
	if (result.len > 0)
	{
		result.value = MakeArray(arena, char, result.len+1);
		MemCpy(result.value, string.value, result.len);
		result.value[result.len] = '\0';
	}

	return result;
}

// ::Util::Strings::Functions::End::



// ::Util::Memory::Functions::Start::

void MemZero(void *ptr, isize size)
{
	if (!size || !ptr) return;

	isize iter_size = size > sizeof(u64) ? 8 : 1;
	iter_size *= 4;
	isize iter_len = size / iter_size;

	u64 *mem = (u64 *)ptr;

	if (iter_size > sizeof(u64))
	{
		while (iter_len > 0)
		{
			mem[0] = 0;
			mem[1] = 0;
			mem[2] = 0;
			mem[3] = 0;
			mem += 4;
			iter_len--;
		}
	}

	u8 *byte_mem = (u8 *)mem;
	isize rem_len = size % iter_size;
	while (rem_len > 0)
	{
		byte_mem[0] = 0;
		byte_mem++;
		rem_len--;
	}
}

void MemCpy(rawptr dst, rawptr src, usize size)
{
	if (size == 0) return;

	isize iter_size = size > sizeof(u64) ? 8 : 1;
	iter_size *= 4;
	isize iter_len = size / iter_size;
	isize rem_len = 0;

	u64 *mem_dst = (u64 *)dst;
	u64 *mem_src = (u64 *)src;


	if (iter_size > sizeof(size))
	{
		while (iter_len > 0)
		{
			mem_dst[0] = mem_src[0];
			mem_dst[1] = mem_src[1];
			mem_dst[2] = mem_src[2];
			mem_dst[3] = mem_src[3];
			mem_dst += 4;
			mem_src += 4;
			iter_len--;
		}

		rem_len = size % iter_size;
	}
	else
		rem_len = size;


	u8 *byte_dst = (u8 *)mem_dst;
	u8 *byte_src = (u8 *)mem_src;
	while (rem_len > 0)
	{
		byte_dst[0] = byte_src[0];
		byte_dst++;
		byte_src++;
		rem_len--;
	}
}

// ::Util::Memory::Functions::End::



// ::Util::Math::Functions::Start::

DefMathImpl(Min);
DefMathImpl(Max);
DefMathImpl(Clamp);
DefMathImpl(Abs);

// ::Util::Math::Functions::End::



// ::Util::Hashing::Functions::Start::

u64 static inline HashFromString(String8 string)
{
	return XXH3_64bits(string.value, string.len);
}

// ::Util::Hashing::Functions::End::



// ::Util::Print::Functions::Start::

i32 Printf(char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	i32 result = _Printf(fmt, arg);
	va_end(arg);

	return result;
}

i32 Printfln(char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	i32 result = _Printfln(fmt, arg);
	va_end(arg);

	return result;
}

i32 EPrintf(char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	i32 result = _EPrintf(fmt, arg);
	va_end(arg);

	return result;
}

i32 EPrint(void *str) 
{
	return pWriteStdErr(str, (isize)StrLen(str));
}

i32 _EPrintf(char *fmt, va_list arg) 
{
	char buffer[1024];

	int sprf_res = stbsp_vsnprintf(&buffer[0], 1024, fmt, arg);

	if (sprf_res < 0) return sprf_res;
	
	return EPrint(&buffer);
}

i32 _Printf(char *fmt, va_list arg) 
{
	char buffer[1024];
	
	int sprf_res = stbsp_vsnprintf(&buffer[0], 1024, fmt, arg);
	
	i32 pr_res;
	if (sprf_res < 0) 
		pr_res = sprf_res;
	else 
		pr_res = pWriteStdOut(&buffer, sprf_res);

	return pr_res;
}

i32 _Printfln(char *fmt, va_list arg)
{
	char buffer[1024];

	int sprf_res = stbsp_vsnprintf(&buffer[0], 1023, fmt, arg);

	i32 pr_res;
	if (sprf_res < 0)
	{
		pr_res = sprf_res;
	}
	else
	{
		buffer[sprf_res] = '\n';
		buffer[sprf_res+1] = '\0';
		pr_res = pWriteStdOut(&buffer, sprf_res+1);
	}

	return pr_res;
}

// ::Util::Print::Functions::End::



// ::Util::Profiling::Functions::Start::

static inline void StartProfileBlock(ProfileBlock *block, c8 *label, u32 anchor_index)
{
	block->anchor_index = anchor_index;
	block->label = label;
	block->start_tsc = pCPUTimerRead();
}

static inline void EndProfileBlock(ProfileBlock *block)
{
	u64 elapsed = pCPUTimerRead() - block->start_tsc;

	ProfileAnchor *anchor = GLOBAL_PROFILER.anchors + block->anchor_index;
	anchor->tsc_elapsed += elapsed;
	anchor->hit_count += 1;
	anchor->label = block->label;
}

// ::Util::Profiling::Functions::End::



// ::Util::Async::Functions::Start::

static inline b32 MutTryLock(Mut *mut)
{
	b32 lock = false;
	return pAtomicCompareExchangeB32(&mut->lock, &lock, 1);
}

static inline void MutUnlock(Mut *mut)
{
	pAtomicStoreB32(&mut->lock, 0);
}

static inline void TicketMutInit(TicketMut *mut)
{
	mut->ticket = 0;
	mut->next_ticket = 1;
}

static inline void TicketMutLock(TicketMut *mut)
{
	u32 ticket = pAtomicFetchIncrU32(&mut->ticket);
	while (ticket != mut->next_ticket);
}

static inline void TicketMutUnlock(TicketMut *mut)
{
	pAtomicIncrU32(&mut->next_ticket);
}

static inline u32 JobQueueAdd(JobQueue *queue, u32 count)
{
	u32 job_idx = pAtomicFetchIncrU32(&queue->queued);
	pAtomicFetchIncrU32(&queue->remaining);

	return job_idx;
}

static inline u32 JobQueueGetCount(JobQueue *queue)
{
	return pAtomicLoadU32(&queue->queued);
}

static inline void JobQueueMarkUnqueued(JobQueue *queue, u32 count)
{
	Assert(queue->queued != 0, "queue queued is 0 before trying to mark dequeued");
	pAtomicFetchSubU32(&queue->queued, count);
}

static inline void JobQueueMarkCompleted(JobQueue *queue, u32 count)
{
	Assert(queue->remaining != 0, "queue remaining is 0 before trying to mark completed");
	pAtomicFetchSubU32(&queue->remaining, count);
}

static inline void JobQueueReset(JobQueue *queue)
{
	pAtomicFetchSubU32(&queue->queued, queue->queued);
	pAtomicFetchSubU32(&queue->remaining, queue->remaining);
}

static inline b32 JobQueueCompleted(JobQueue *queue)
{
	u32 remaining = pAtomicLoadU32(&queue->remaining);
	return remaining == 0;
}

// ::Util::Async::Functions::End::
