#ifndef MAXON_MALLOC_H__
#define MAXON_MALLOC_H__

#include <utility>
#include <stdexcept>
#include <new>

using MallocPtr = void* (*)(std::size_t s);
extern MallocPtr g_malloc;

using FreePtr = void(*)(void* p);
extern FreePtr g_free;

template <typename T, typename ... ARGS>
T* NewObject(ARGS&& ... args)
{ 
	void* mem = g_malloc(sizeof(T));

	if (mem == nullptr)
		throw std::bad_alloc();

	T* res = new (mem) T(std::forward<ARGS>(args) ...);
	return res; 
}

template <typename T>
T* NewMemory(size_t count)
{
	void* mem = g_malloc(sizeof(T) * count + sizeof(size_t));

	if (mem == nullptr)
		throw std::bad_alloc();
	
	size_t* offsetMemory = &((size_t*)mem)[1];
	T* placememory = (T*)offsetMemory;
	for (size_t loopIndex = 0; loopIndex < count; loopIndex++)
	{
		new (placememory + loopIndex) T;
	}
	offsetMemory[-1] = count;
	return placememory;
}

struct DeleteShared
{
	template <typename T>
	void operator()(T* ptr) const 
	{
		if (ptr == nullptr)
			return;

		ptr->~T();
		g_free(ptr);
	}
};

struct DeleteUnique
{
	template <typename T>
	void operator()(T* ptr) const
	{
		if (ptr == nullptr)
			return;

		ptr->~T();
		g_free(ptr);
	}
};

struct DeleteUniqueList
{
	template <typename T>
	void operator()(T* ptr) const
	{
		if (ptr == nullptr)
			return;

		size_t* tempPtr = (size_t*)ptr;
		size_t elements = tempPtr[-1];

		for (int64_t loopIndex = (int64_t)elements - 1; loopIndex >= 0; loopIndex--)
		{
			ptr[loopIndex].~T();
		}

		T* rawMemory = (T*)&(tempPtr[-1]);
		g_free(rawMemory);
	}
};

template <typename T>
void DeleteObject(T* ptr)
{
	if (ptr == nullptr)
		return;

	ptr->~T();
	g_free(ptr);
}

template <typename T>
void DeleteMemory(T* ptr)
{
	if (ptr == nullptr)
		return;

	size_t* tempPtr = (size_t*)ptr;
	size_t elements = tempPtr[-1];

	for (int64_t loopIndex = (int64_t)elements - 1; loopIndex >= 0; loopIndex--)
	{
		ptr[loopIndex].~T();
	}

	T* rawMemory = (T*)&(tempPtr[-1]);
	g_free(rawMemory);
}

#endif // MAXON_MALLOC_H__
