// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt
//
// <Author: Christoph Kubisch>


#ifndef __LUXCORE_MEMORYBASE_H__
#define __LUXCORE_MEMORYBASE_H__

#include <luxplatform/luxplatform.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
//////////////////////////////////////////////////////////////////////////
// Switches
//
//	MEMORY_GENERIC_STATS
//	MEMORY_STATS
//	MEMORY_GLOBAL_DLMALLOC

#ifdef _DEBUG
#define MEMORY_GENERIC_STATS
#define MEMORY_STATS
#endif

#ifdef __cplusplus
extern "C"{
#endif

#define MEMORY_MEGS(a)		(a*1048576)
#define MEMORY_KBS(a)		(a*1024)

	typedef struct lxMemAllocator_s{
		void*	(__cdecl *_malloc)(size_t);
		void*	(__cdecl *_calloc)(size_t,size_t);
		void*	(__cdecl *_realloc)(void*,size_t);
		void	(__cdecl *_free)(void*);
	}lxMemAllocator_t;


	LUX_API extern lxMemAllocator_t	g_MemAllocator;

#define MEMORY_GLOBAL_MALLOC		g_MemAllocator._malloc
#define MEMORY_GLOBAL_CALLOC		g_MemAllocator._calloc
#define MEMORY_GLOBAL_REALLOC		g_MemAllocator._realloc
#define MEMORY_GLOBAL_FREE			g_MemAllocator._free
#define MEMORY_GLOBAL_FREEALIGNED	lxMemAlignedFree
#define MEMORY_GLOBAL_MALLOCALIGNED	lxMemAlignedAlloc


//////////////////////////////////////////////////////////////////////////
// Aligned alloc & free

LUX_API void	lxMemAlignedFree(void *ptr);

// alignsize must be power of 2
LUX_API void*	lxMemAlignedAlloc(size_t size, size_t align_size);
void	lxMemAlignedRealloc(void* reallocated, size_t alignsize, 
								size_t newsize, size_t info, size_t shiftold);

void*	lxMemAlignedToOrig(void *ptr);
void*	lxMemAlignedFromOrig(void *ptr, size_t align_size);


//////////////////////////////////////////////////////////////////////////
// Pointer

// advances a pointer for continous allocation, assumes all allocations
// fit into memory block
void*	lxPointerAdvanceAlloc(void **ptr, size_t size);
void*	lxPointerAdvanceAllocAligned(void **ptr, size_t size, size_t align);

// aligns must be power of 2
void*	lxPointerAlign(void *ptr, size_t align);
size_t	lxSizeAlign(size_t val, size_t align);

//////////////////////////////////////////////////////////////////////////
LUX_INLINE void*	lxPointerAlign(void *ptr, size_t align){
	align = align-1;
	return (void*) ((((size_t)ptr) + align) & ~align);
}
LUX_INLINE size_t	lxSizeAlign(size_t ptr, size_t align){
	align = align-1;
	return ((((size_t)ptr) + align) & ~align);
}

LUX_INLINE void*	lxPointerAdvanceAlloc(void **ptr, size_t size)
{
	uchar *inptr = *(uchar**)ptr;
	*ptr = inptr + size;

	return inptr;
}

LUX_INLINE void*	lxPointerAdvanceAllocAligned(void **ptr, size_t size, size_t align)
{
	uchar *inptr = *(uchar**)ptr;
	size_t offset;

	// shift mem->inuse
	offset = ((size_t)inptr)%align;
	offset = offset ? align-offset : 0;
	inptr += offset;

	*ptr = inptr + size;

	return inptr;
}

LUX_INLINE void*	lxMemAlignedToOrig(void *ptr){
	char *ptrb = (char*)ptr;
	size_t *ptr2=(size_t *)ptr - 1;
	ptrb -= *ptr2;

	return ptrb;
}

LUX_INLINE void*	lxMemAlignedFromOrig(void* optr, size_t align_size)
{
	size_t align_mask = align_size - 1;
	char *ptr = (char*)optr;
	char *ptr2 = ptr + sizeof(size_t);
	char *aligned_ptr = ptr2 + (align_size - ((size_t)ptr2 & align_mask));


	ptr2 = aligned_ptr - sizeof(size_t);
	*((size_t *)ptr2)=(size_t)(aligned_ptr - ptr);

	return (void*)aligned_ptr;
}

LUX_INLINE void	lxMemAlignedRealloc(void* reallocated, size_t alignsize, 
									  size_t newsize, size_t info, size_t shiftold)
{
	byte*	oldptr = (byte*)reallocated;
	byte*	ptr = (byte*)lxPointerAlign(oldptr+info,alignsize);
	size_t shiftnew = (ptr-oldptr);
	ptrdiff_t diff = shiftold-shiftnew;

	if (diff == 0){
		return;
	}
	memmove(ptr,oldptr+(shiftold+info),newsize);
}

#ifdef __cplusplus
};  
#endif

#endif

