
// 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/memorystack.h>
#include <luxplatform/debug.h>

#include <string.h>

#include "memory_defs.h"

//////////////////////////////////////////////////////////////////////////
// MemoryStack

lxMemoryStack_t *lxMemoryStack_new(const char* name, size_t totalbytes)
{
	lxMemoryStack_t *mem;

	//if (l_poolMutex == -1) l_poolMutex = critical_create();

	mem = (lxMemoryStack_t*)MEMORY_LOCAL_MALLOC(sizeof(lxMemoryStack_t)+strlen(name)+1);
	memset(mem,0,sizeof(lxMemoryStack_t));

	mem->name = (const char*)(mem+1);
	strcpy((char*)mem->name,name);

	lxMemoryStack_initMin(mem,totalbytes);

	return mem;
}
void* lxMemoryStack_zalloc(lxMemoryStack_t *mem, size_t size)
{
	char *pMem;
	// always pad size to 4-byte alignment
#ifdef LUX_MEMORY_STACK_PAD
	size_t padsize = size%LUX_MEMORY_STACK_PAD;
	size += padsize ? LUX_MEMORY_STACK_PAD-padsize : 0;
#endif

	if (size+mem->inuse > mem->total){
		LUX_PRINTF("ERROR: MemPoolMalloc failed - %s - size: %d inuse: %d total: %d\n",mem->name,size,mem->inuse,mem->total);
		LUX_ASSERT(0);
		return NULL;
	}
	else{
		pMem = &mem->data[mem->inuse];
		mem->inuse += size;
		memset(pMem,0,size);
		return pMem;
	}
}
void* lxMemoryStack_zallocAligned(lxMemoryStack_t *mem, size_t size, size_t align)
{
	char *pMem;

	size_t padsize;
	size_t offset;

	// shift mem->inuse
	offset = ((size_t)(&mem->data[mem->inuse]))%align;
	offset = offset ? align-offset : 0;
	mem->inuse += offset;

	// add pad to 4
#ifdef LUX_MEMORY_STACK_PAD
	padsize = size%LUX_MEMORY_STACK_PAD;
	size += padsize ? LUX_MEMORY_STACK_PAD-padsize : 0;
#endif

	if (size+mem->inuse > mem->total){
		LUX_PRINTF("ERROR: MemPoolMalloc failed - %s - size: %d inuse: %d total: %d\n",mem->name,size,mem->inuse,mem->total);
		LUX_ASSERT(0);
		return NULL;
	}
	else{
		pMem = &mem->data[mem->inuse];
		mem->inuse += size;
		memset(pMem,0,size);
		return pMem;
	}
}
void* lxMemoryStack_alloc(lxMemoryStack_t *mem, size_t size)
{
	char *pMem;

	if (size+mem->inuse > mem->total){
		LUX_PRINTF("ERROR: MemPoolMalloc failed - %s - size: %d inuse: %d total: %d\n",mem->name,size,mem->inuse,mem->total);
		LUX_ASSERT(0);
		return NULL;
	}
	else{
		pMem = &mem->data[mem->inuse];
		mem->inuse += size;
		return pMem;
	}
}
void* lxMemoryStack_current(lxMemoryStack_t *mem)
{
	void *ptr;

	ptr = &mem->data[mem->inuse];

	return ptr;
}
// returns true if ptr is inside pool
booln	 lxMemoryStack_checkPtr(lxMemoryStack_t *mem, void *ptr)
{
	if ((char*)ptr >= mem->data && (char*)ptr < mem->data+mem->total)
		return LUX_TRUE;
	LUX_PRINTF("ERROR: MemPoolPtr failed - %s - ptr: %p inuse: %d total: %d\n",mem->name,ptr,mem->inuse,mem->total);
	LUX_ASSERT(0);
	return LUX_FALSE;
}

// from this point on new allocs will be done
// if outside pool false is returned
void lxMemoryStack_setBegin(lxMemoryStack_t *mem, void *ptr){

	if (!lxMemoryStack_checkPtr(mem,ptr)){
		LUX_PRINTF("ERROR: MemPoolSetBegin failed - %s - ptr: %d inuse: %d total: %d\n",mem->name,ptr,mem->inuse,mem->total);
		LUX_ASSERT(0);
		return;
	}

	mem->inuse = (int)((char*)ptr - mem->data);
}
booln lxMemoryStack_initMin(lxMemoryStack_t *mem, size_t totalbytes)
{
	char *olddata = mem->dataorig;

	if (mem->dataorig){
		if (totalbytes > mem->total){
			mem->dataorig = (byte*)MEMORY_LOCAL_REALLOC(mem->data, totalbytes + LUX_MEMORY_STACK_PAD,mem->total + LUX_MEMORY_STACK_PAD);
			mem->data = (byte*)lxPointerAlign(mem->dataorig,LUX_MEMORY_STACK_PAD);
			mem->total = totalbytes;
		}
	}
	else{
		mem->dataorig = (byte*)MEMORY_LOCAL_MALLOC(totalbytes + LUX_MEMORY_STACK_PAD);
		mem->data = (byte*)lxPointerAlign(mem->dataorig,LUX_MEMORY_STACK_PAD);
		mem->total = totalbytes;
	}

	mem->inuse = 0;
	mem->resized = (olddata != mem->dataorig);

	return mem->resized;
}

int	 lxMemoryStack_popResized(lxMemoryStack_t *mem)
{
	int resized = mem->resized;
	mem->resized = LUX_FALSE;

	return resized;
}

void lxMemoryStack_clear(lxMemoryStack_t *mem)
{
	mem->inuse = 0;
}

void lxMemoryStack_deinit(lxMemoryStack_t *mem)
{
	if (mem->dataorig != NULL)
		MEMORY_LOCAL_FREE(mem->dataorig,mem->total + LUX_MEMORY_STACK_PAD);

	mem->data = NULL;
	mem->inuse = 0;
	mem->total = 0;
}

void	lxMemoryStack_delete(lxMemoryStackPTR mem)
{
	lxMemoryStack_deinit(mem);
	MEMORY_LOCAL_FREE(mem,sizeof(lxMemoryStack_t)+strlen(mem->name)+1);
}

size_t	lxMemoryStack_bytesLeft(lxMemoryStack_t *mem)
{
	return (mem->total-mem->inuse);
}
size_t lxMemoryStack_bytesUsed(lxMemoryStack_t *mem)
{
	return (mem->inuse);
}
size_t lxMemoryStack_bytesTotal(lxMemoryStack_t *mem)
{
	return (mem->total);
}
