// 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

#include <luxcore/memorygeneric.h>

#include <luxplatform/debug.h>
#include <luxcore/contmacrolinkedlist.h>

lxMemoryGenericInfo_t	g_CoreMemGen = {0,0};

//////////////////////////////////////////////////////////////////////////
// MemoryGeneric

lxMemoryGenericInfo_t	lxMemoryGeneric_getInfo()
{
	return g_CoreMemGen;
}

//////////////////////////////////////////////////////////////////////////
#if defined(MEMORY_GENERIC_STATS)

typedef struct MemInfo_s
{
	size_t		size;
	int			line;
	const char* src;

	struct MemInfo_s LISTNODEVARS;
} MemInfo_t;

static struct MemStats_s{
	MemInfo_t	*List;
}l_CoreMemGen = { NULL};


void lxMemoryGeneric_freeStats(void *ptr, size_t size,const char *source, int line)
{
	MemInfo_t *info;

	ptr = (char*)ptr-sizeof(MemInfo_t);
	info = (MemInfo_t*)ptr;
	ListNode_remove(l_CoreMemGen.List,info);

	LUX_PROFILING_OP (g_CoreMemGen.mem -= size);
	LUX_PROFILING_OP (g_CoreMemGen.allocs--);

	if (info->size!=size){
		LUX_PRINTF("free size err: %s (l:%i) (%i/%i)\n  allocated in %s (l:%i)\n",
			source,line,info->size,size, info->src,info->line);
	}

	MEMORY_GLOBAL_FREE(ptr);
}

#define MEMORY_ALIGNED_MAGIC	0xAABBCCDD

size_t	lxMemoryGeneric_offsetAlignedStats(void *alptr)
{
	size_t *ptr1 = ((size_t *)alptr) - 3;
	size_t *ptr2 = ((size_t *)alptr) - 2;
	size_t *ptr3 = ((size_t *)alptr) - 1;

	if (*ptr1 != MEMORY_ALIGNED_MAGIC){
		LUX_ASSERT(0);
	}

	return *ptr3;
}

void*	lxMemoryGeneric_zallocAlignedStats(size_t size, size_t alignsize,const char *source,int line)
{
	char *ptr = (char*)lxMemoryGeneric_zallocStats(size + (sizeof(size_t)*3) + alignsize,source,line);
	char *alptr = (char*)lxPointerAlign(ptr+sizeof(size_t)*3,alignsize);

	{
		size_t *ptr1 = ((size_t *)alptr) - 3;
		size_t *ptr2 = ((size_t *)alptr) - 2;
		size_t *ptr3 = ((size_t *)alptr) - 1;

		*ptr1 = MEMORY_ALIGNED_MAGIC;
		*ptr2 = (sizeof(size_t)*3) + alignsize;
		*ptr3 = alptr-ptr;
	}


	return alptr;
}

void*	lxMemoryGeneric_mallocAlignedStats(size_t size, size_t alignsize,const char *source,int line)
{
	char *ptr = (char*)lxMemoryGeneric_mallocStats(size + (sizeof(size_t)*3) + alignsize,source,line);
	char *alptr = (char*)lxPointerAlign(ptr+sizeof(size_t)*3,alignsize);

	{
		size_t *ptr1 = ((size_t *)alptr) - 3;
		size_t *ptr2 = ((size_t *)alptr) - 2;
		size_t *ptr3 = ((size_t *)alptr) - 1;

		*ptr1 = MEMORY_ALIGNED_MAGIC;
		*ptr2 = (sizeof(size_t)*3) + alignsize;
		*ptr3 = alptr-ptr;
	}


	return alptr;
}

void*	lxMemoryGeneric_reallocAlignedStats(void* ptr,size_t size,size_t alignsize,size_t oldsize,const char *source,int line)
{
	size_t *ptr1 = ((size_t *)ptr) - 3;
	size_t *ptr2 = ((size_t *)ptr) - 2;
	size_t *ptr3 = ((size_t *)ptr) - 1;
	size_t offset = *ptr3;
	char *newptr;
	char *alptr;

	if (*ptr1 != MEMORY_ALIGNED_MAGIC){
		LUX_PRINTF("realloc align err: %s (l:%i) (%s)\n  non-aligned alloc used\n",source,line,size);
		LUX_ASSERT(0);
	}
	else{
		ptr = (void*)(((char*)ptr) - offset);
	}

	newptr = (char*)lxMemoryGeneric_reallocStats(ptr, size + (sizeof(size_t)*3) + alignsize, oldsize + *ptr2,source,line);
	lxMemAlignedRealloc(newptr,alignsize,size,(sizeof(size_t)*3),(offset)-(sizeof(size_t)*3));

	alptr = (char*)lxPointerAlign(newptr+sizeof(size_t)*3,alignsize);

	{
		ptr1 = ((size_t *)alptr) - 3;
		ptr2 = ((size_t *)alptr) - 2;
		ptr3 = ((size_t *)alptr) - 1;

		*ptr1 = MEMORY_ALIGNED_MAGIC;
		*ptr2 = (sizeof(size_t)*3) + alignsize;
		*ptr3 = alptr-newptr;
	}


	return alptr;
}

void	lxMemoryGeneric_freeAlignedStats(void *ptr, size_t size,const char *source,int line)
{
	size_t *ptr1 = ((size_t *)ptr) - 3;
	size_t *ptr2 = ((size_t *)ptr) - 2;
	size_t *ptr3 = ((size_t *)ptr) - 1;

	if (*ptr1 != MEMORY_ALIGNED_MAGIC){
		LUX_PRINTF("free align err: %s (l:%i) (%s)\n  non-aligned alloc used\n",source,line,size);
		LUX_ASSERT(0);
	}
	else{
		ptr = (void*)(((char*)ptr) - *ptr3);
	}

	lxMemoryGeneric_freeStats(ptr,size + *ptr2,source,line);
}

#undef MEMORY_ALIGNED_MAGIC

void* lxMemoryGeneric_reallocStats(void *ptr, size_t size,size_t oldsize,const char *source, int line)
{
	ptrdiff_t sz = size;
	MemInfo_t *info;
	char *mem;

	ptr = (char*)ptr-sizeof(MemInfo_t);
	info = (MemInfo_t*)ptr;
	ListNode_remove(l_CoreMemGen.List,info);

	LUX_PROFILING_OP (g_CoreMemGen.mem += (ptrdiff_t)size - (ptrdiff_t)oldsize);
	LUX_PROFILING_OP (g_CoreMemGen.allocs--);


	if (info->size!=oldsize){
		LUX_PRINTF("realloc oldsize err: %s (l:%i) (%i/%i)\n  allocated in %s (l:%i)\n",
			source,line,info->size,size, info->src,info->line);
	}

	mem = (char*)MEMORY_GLOBAL_REALLOC(ptr,size+sizeof(MemInfo_t));

	info = (MemInfo_t*)mem;
	info->size = size;
	info->line = line;
	info->src = source;
	ListNode_init(info);
	ListNode_pushBack(l_CoreMemGen.List,info);

	return mem+sizeof(MemInfo_t);
}

void *lxMemoryGeneric_zallocStats(size_t size,const char *source,int line)
{
	char *mem;
	MemInfo_t *info;


	LUX_PROFILING_OP (g_CoreMemGen.mem+=size);
	LUX_PROFILING_OP (g_CoreMemGen.allocs++);

	mem = (char*)MEMORY_GLOBAL_CALLOC(1,size+sizeof(MemInfo_t));

	info = (MemInfo_t*)mem;
	info->size = size;
	info->line = line;
	info->src = source;
	ListNode_init(info);
	ListNode_pushBack(l_CoreMemGen.List,info);


	return mem+sizeof(MemInfo_t);
}

void *lxMemoryGeneric_callocStats(size_t num, size_t size,const char *source,int line)
{
	char *mem;
	MemInfo_t *info;


	LUX_PROFILING_OP (g_CoreMemGen.mem+= size*num );
	LUX_PROFILING_OP (g_CoreMemGen.allocs++);

	mem = (char*)MEMORY_GLOBAL_CALLOC(num,size+sizeof(MemInfo_t));

	info = (MemInfo_t*)mem;
	info->size = size*num;
	info->line = line;
	info->src = source;
	ListNode_init(info);
	ListNode_pushBack(l_CoreMemGen.List,info);


	return mem+sizeof(MemInfo_t);
}

void *lxMemoryGeneric_mallocStats(size_t size,const char *source,int line)
{
	char *mem;
	MemInfo_t *info;


	LUX_PROFILING_OP (g_CoreMemGen.mem+=size);
	LUX_PROFILING_OP (g_CoreMemGen.allocs++);

	mem = (char*)MEMORY_GLOBAL_MALLOC(size+sizeof(MemInfo_t));

	info = (MemInfo_t*)mem;
	info->size = size;
	info->line = line;
	info->src = source;
	ListNode_init(info);
	ListNode_pushBack(l_CoreMemGen.List,info);


	return mem+sizeof(MemInfo_t);
}
#endif

//////////////////////////////////////////////////////////////////////////

void lxMemoryGeneric_dumpStats(const char* fname)
{
#if defined(MEMORY_GENERIC_STATS)
	MemInfo_t* browse;
	FILE* of = fopen(fname,"wb");
	fprintf(of,"return {\n");
	ListNode_forEach(l_CoreMemGen.List,browse)
		fprintf(of,"{file=[[%s]],\tline=%d,\tsize=%d,},\n",browse->src,browse->line,browse->size);
	ListNode_forEachEnd(l_CoreMemGen.List,browse);
	fprintf(of,"}\n");
	fclose(of);
#endif
}

booln lxMemoryGeneric_deinitStats()
{
#if !defined(MEMORY_GENERIC_STATS)
	return LUX_TRUE;
#else
	MemInfo_t* browse;
	int i,sz;


	if (l_CoreMemGen.List == NULL) return LUX_TRUE;
	i = 0; sz = 0;
	LUX_PRINTF("Unfreed blocks:\n");
	while (l_CoreMemGen.List) {
		ListNode_popBack(l_CoreMemGen.List,browse);
		LUX_PRINTF("    %5.i: %p (%5.i): %s (l:%i)\n",++i,browse+1,browse->size,browse->src,browse->line);
		sz+=browse->size;
	}
	LUX_PRINTF("Number of unfreed blocks: %i (%i bytes)\n",i,sz);

	return LUX_FALSE;
#endif
}



