import aliases;
import m = math;
import std.stdio;
import core.stdc.string : memset;
import core.memory;

const DEFAULT_ALIGNMENT = (void *).sizeof * 2;

struct Arena
{
	u8* mem;
	u64 length;
	u64 pos;
};

T*
Alloc(T)()
{
	void* mem = pureMalloc(T.sizeof);
	memset(mem, 0, T.sizeof);
	return (cast(T*)mem);
}

T[]
AllocArray(T)(u64 count)
{
	void* mem = pureMalloc(T.sizeof * count);
	memset(mem, 0, T.sizeof * count);
	return (cast(T*)mem)[0 .. count];
}

Arena 
CreateArena(u64 size)
{
	Arena arena = {
		mem: cast(u8 *)pureMalloc(size),
		length: size,
		pos: 0,
	};

	assert(arena.mem != null, "Unable to allocate memory for arena");

	return arena;
};

T[]
AllocArray(T)(Arena* arena, u64 count)
{
	void* mem = AllocAlign(arena, T.sizeof * count, DEFAULT_ALIGNMENT);
	memset(mem, 0, T.sizeof * count);
	return (cast(T*)mem)[0 .. count];
}

T* 
Alloc(T)(Arena* arena)
{
	void* mem = AllocAlign(arena, T.sizeof, DEFAULT_ALIGNMENT);
	memset(mem, 0, T.sizeof);
	return cast(T*)mem;
};

void* 
AllocAlign(Arena* arena, u64 size, u64 alignment)
{
	void* ptr = null;

	uintptr mem_pos = cast(uintptr)arena.mem;
	uintptr current = mem_pos + arena.pos;
	uintptr offset = m.AlignPow2(current, alignment) - mem_pos;
	
	if (offset+size <= arena.length)
	{
		ptr = &arena.mem[offset];
		arena.pos = offset+size;
	}
	else
	{
		writefln("AllocAlign failure: out of memory, size requested: %llu", size);
		assert(0);
	}

	return ptr;
};

void
Reset(Arena* arena)
{
	arena.pos = 0;
}

void
Free(Arena* arena)
{
	pureFree(arena.mem);
}

void
FreeArray(T)(T[] arr)
{
	pureFree(arr.ptr);
}

void Free(T)(T* ptr)
{
	pureFree(ptr);
}
