// DWBS ------------------------------------------------------------------ DWBS
//
// PLATFORM		: ALL
// PRODUCT		: BIT_DEMON
// VISIBILITY	: PUBLIC
//
// DWBS ------------------------------------------------------------------ DWBS

#include <bdCore/bdMemory/bdDefaultMemory.h>

#if defined (BD_MALLOC_MEMORY)

#if defined (BD_PLATFORM_WIN32)
#	define WIN32_LEAN_AND_MEAN
#	include <windows.h>
#endif // defined (BD_PLATFORM_WIN32)

#define BD_LOG_LEVEL "mallocmemory"

bdMallocMemory::bdMemoryChainElement *bdMallocMemory::m_memoryChain = BD_NULL;
bdUWord bdMallocMemory::m_allocatedBytes = 0;
bdUInt bdMallocMemory::m_numAllocations = 0;

bdMutex bdMallocMemory::m_mutex;

void BD_CALL bdMallocMemory::leakCheck()
{
	if(m_allocatedBytes)
	{
		m_mutex.lock();
		bdNChar8 buf[100];
		bdSnprintf(buf, 100, "%u Bytes leaked in %u allocation(s)\n", m_allocatedBytes, m_numAllocations);
		m_mutex.unlock();

#if defined (BD_PLATFORM_WIN32)

		OutputDebugString("********************************************\n");
		OutputDebugString("*      BITDEMON MEMORY LEAKS DETECTED      *\n");
		OutputDebugString("********************************************\n");
		OutputDebugString(buf);
		OutputDebugString("********************************************\n");

#else // defined (BD_PLATFORM_WIN32)

		bdFprintf(BD_STDERR, "********************************************\n");
		bdFprintf(BD_STDERR, "*      BITDEMON MEMORY LEAKS DETECTED      *\n");
		bdFprintf(BD_STDERR, "********************************************\n");
		bdFprintf(BD_STDERR, buf);
		bdFprintf(BD_STDERR, "********************************************\n");

#endif // defined (BD_PLATFORM_WIN32)
	}
}

void BD_CALL bdMallocMemory::releaseAllMemory()
{
	m_mutex.lock();
	while(m_memoryChain)
	{
		bdMemoryChainElement *const link = m_memoryChain;

		eraseMemory(link);
		
		if(link->m_aligned)
		{
			bdAlignedOffsetFree(link);
		}
		else
		{
			bdFree(link);
		}
	}
	m_mutex.unlock();
}

void* BD_CALL bdMallocMemory::recordMemory(bdMemoryChainElement *link,
										   const bdUWord size,
										   const bdBool aligned)				   
{
	if(link)
	{
		m_mutex.lock();
		link->m_magic = BD_MEMORY_MAGIC;
		link->m_size = size;
		link->m_aligned = aligned;
		link->m_next = m_memoryChain;
		link->m_prev = BD_NULL;
		if(m_memoryChain)
		{
			m_memoryChain->m_prev = link;
		}
		m_memoryChain = link;

		m_allocatedBytes += size;
		m_numAllocations++;

		m_mutex.unlock();
		return reinterpret_cast<void*>(link+1);
	}

	return BD_NULL;
}

void BD_CALL bdMallocMemory::eraseMemory(bdMemoryChainElement *link)
{
	m_mutex.lock();
	if(link->m_magic != BD_MEMORY_MAGIC)
	{
		// log might cause memory allocation...
		m_mutex.unlock();
		BD_ERR(BD_LOG_LEVEL, " BD_MEMORY_MAGIC is incorrect.");
		m_mutex.lock();
	}

	if(link->m_prev)
	{
		link->m_prev->m_next = link->m_next;
	}
	else
	{
		m_memoryChain = link->m_next;
	}

	if(link->m_next)
	{
		link->m_next->m_prev = link->m_prev;
	}

	m_allocatedBytes -= link->m_size;
	m_numAllocations--;
	m_mutex.unlock();
}

bdMallocMemory::bdMallocMemory()
{
}

#endif // defined (BD_MALLOC_MEMORY)
