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

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;
}

Str8 MakeStr8(u8 *str, u32 len)
{
	return (Str8){ .len = len, .value = str };
}

Str8 PreSplitStr8(Str8 string, Str8 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 (Str8){ .len = new_len, .value = string.value };
}

Str8 PreSplitNewStr8(Arena *arena, Str8 string, Str8 delimiter)
{
	Str8 result = PreSplitStr8(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::

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)
{
	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--;
	}
}

u32 u32Min(u32 l, u32 r)
{
	return l >= r ? r : l;
}

i32 i32Min(i32 l, i32 r)
{
	return l >= r ? r : l;
}

u32 u32Max(u32 l, u32 r)
{
	return l >= r ? l : r;
}

i32 i32Max(i32 l, i32 r)
{
	return l >= r ? l : r;
}

i32 i32Clamp(i32 v, i32 min, i32 max)
{
	return i32Min(max, i32Max(v, min));
}

u32 u32Clamp(u32 v, u32 min, u32 max)
{
	return i32Min(max, i32Max(v, min));
}

u32 inline ReadBit(BitWriter *bw)
{
	u32 r = bw->bits >> 31;
	bw->bits <<= 1;
	bw->pos += 1;
	return r;
}

void static inline RefillBits(BitWriter *bw)
{
	while (bw->pos >= 0)
	{
		bw->bits |= (bw->current < bw->end ? *bw->current : 0) << bw->pos;
		bw->pos -= 8;
		bw->current += 1;
	}
}

void static inline FlushBits(BitWriter *bw)
{
	while (bw->pos >= 8)
	{
		bw->pos -= 8;	
		*bw->current = (bw->bits >> bw->pos) & 0xFF;
		*bw->current += 1;
	}
}

void static inline WriteBit(BitWriter *bw, u32 v)
{
	bw->bits = (bw->bits << 1) | v;
	bw->pos += 1;
	if (bw->pos >= 8)
		FlushBits(bw);
}

void static inline HMScan1(HMNode *nodes, u8 symbol)
{
	nodes[symbol].freq += 1;
}

void static inline HMScan2(HMNode *nodes, u8 symbol1, u8 symbol2)
{
	nodes[symbol1].freq += 1;
	nodes[symbol2].freq += 1;
}

void static inline HMScan3(HMNode *nodes, u8 symbol1, u8 symbol2, u8 symbol3)
{
	nodes[symbol1].freq += 1;
	nodes[symbol2].freq += 1;
	nodes[symbol3].freq += 1;
}

void static inline HMScan4(HMNode *nodes, u8 symbol1, u8 symbol2, u8 symbol3, u8 symbol4)
{
	nodes[symbol1].freq += 1;
	nodes[symbol2].freq += 1;
	nodes[symbol3].freq += 1;
	nodes[symbol4].freq += 1;
}

void static inline HMQuicksort(HMNode *nodes, u32 low, u32 high)
{
	if (low < high)
	{
		u32 pi = low - 1;
		HMNode *p = &nodes[high];
		HMNode temp;
		for (u32 i = low; i < high; i++)
		{
			if (nodes[i].freq < p->freq)
			{
				pi += 1;
				temp = nodes[pi];
				nodes[pi] = nodes[i];
				nodes[i] = temp;
			}
		}

		pi += 1;
		temp = nodes[pi];
		nodes[pi] = nodes[high];
		nodes[high] = temp;

		HMQuicksort(nodes, low, pi-1);
		HMQuicksort(nodes, pi+1, high);
	}
}

void static inline HMBuildTable(HMNode *nodes)
{
	HMNode *q[256];
	HMNode *p[256];
	u32 num_symbols = 0;
	for (u32 i = 0; i < HM_MAX_SYMBOLS; i++)
	{
		if (nodes[i].freq)
		{
			nodes[num_symbols] = nodes[i];
			q[num_symbols] = &nodes[num_symbols];
			num_symbols += 1;
		}
	}

	// TODO: also finish this trash

	HMQuicksort(nodes, 0, num_symbols);
}

b32 HMCompressData(u8 *data, u32 len, u8 *out)
{
	b32 success = true;

	BitWriter bw = {
		.current = data,
		.end = &data[len-1],
		.bits = 0,
		.pos = 24,
	};

	// TODO: finish this trash

	u32 iterations;

	return success;
}

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

u64 static inline HashFromString(Str8 string)
{
	return XXH3_64bits_withSeed(string.value, string.len, HASH_SEED);
}

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