#include "Core.h"

NAMESPACE_UPP

int sMemDiagInitCount = 0;

#if defined(_DEBUG) && defined(UPP_HEAP)

static StaticCriticalSection sHeapLock2;

struct DbgBlkLink {
	DbgBlkLink *prev;
	DbgBlkLink *next;

	void LinkSelf() {
		next = prev = this;
	}
	void Unlink() {
		prev->next = next;
		next->prev = prev;
	}
	void Insert(DbgBlkLink *lnk) {
		lnk->prev = this;
		lnk->next = next;
		next->prev = lnk;
		next = lnk;
	}
};

static void *l_leak[8];
static int   l_count;

void  MemoryWatch(void *leak)
{
	if(l_count < 8)
		l_leak[l_count++] = leak;
}

void _PLACE_DEBUGER_BREAK_HERE_()
{
	LOG("Memory watch...");
}

static void *l_wfree[8];
static int   l_wfcount;

void  MemoryWatchFree(void *ptr)
{
	if(l_wfcount < 8)
		l_wfree[l_wfcount++] = ptr;
}

void _PLACE_DEBUGER_FREE_BREAK_HERE_()
{
	LOG("Memory watch free...");
}

static DbgBlkLink dbg_live;

extern bool PanicMode;

void *MemoryAllocDebug(size_t size)
{
	if(PanicMode)
		return malloc(size);
#ifdef _MULTITHREADED
	sHeapLock2.Enter();
#endif
	if(!dbg_live.next)
		dbg_live.LinkSelf();
	size = (size + 7) & 0xfffffff8;
	DbgBlkLink *p = (DbgBlkLink *)MemoryAlloc(size + 2 * sizeof(DbgBlkLink));
	DbgBlkLink *e = (DbgBlkLink *)((byte *)(p + 1) + size);
	dbg_live.Insert(e);
	dbg_live.Insert(p);
	p++;
#ifdef _MULTITHREADED
	sHeapLock2.Leave();
#endif
	for(int i = 0; i < l_count; i++)
		if(l_leak[i] == p) {
			fprintf(stderr, "Allocating leak: %p\n", p);
			fflush(stderr);
			LOG("Leak spot allocation: " << FormatIntHex(p));
			_PLACE_DEBUGER_BREAK_HERE_();
		}
	return p;
}

void HeapPanic(const char *text, void *pos, int size);

void MemoryFreeDebug(void *ptr)
{
	if(PanicMode)
		return;
	if(!ptr) return;
//	LOG("T");
#ifdef _MULTITHREADED
	CriticalSection::Lock __(sHeapLock2);
#endif
	for(int i = 0; i < l_wfcount; i++)
		if(l_wfree[i] == ptr)
			_PLACE_DEBUGER_FREE_BREAK_HERE_();
	DbgBlkLink *p = (DbgBlkLink *)ptr - 1;
	DbgBlkLink *e = p->next;
	if(p != e->prev) {
		sHeapLock2.Leave();
		char h[256];
		sprintf(h, "Heap is corrupted p:%p e->prev:%p", p, e->prev);
		HeapPanic(h, p + 1, (uintptr_t)p->next - (uintptr_t)(p + 1));
	}
	e->Unlink();
	p->Unlink();
	MemoryFree(p);
}

void MemoryCheckDebug()
{
	CriticalSection::Lock __(sHeapLock2);
	DbgBlkLink *p = &dbg_live;
	do {
		if(p->prev->next != p || p->next->prev != p) {
			sHeapLock2.Leave();
			HeapPanic("HEAP CHECK: Heap is corrupted", p + 1, (uintptr_t)p->next - (uintptr_t)(p + 1));
		}
		p = p->next;
	}
	while(p != &dbg_live);
}

void MemoryDumpLeaks()
{
	if(!dbg_live.next || dbg_live.next == dbg_live.prev)
		return;
	BugLog() << "\n\nHeap leaks detected:\n";
	DbgBlkLink *p = dbg_live.next;
	for(;;) {
		BugLog() << Sprintf("MemoryWatch(0x%lX);\n", p + 1);
		HexDump(VppLog(), p + 1, (dword)((uintptr_t)p->next - (uintptr_t)(p + 1)), 64);
		p = p->next;
		if(p == &dbg_live) break;
		p = p->next;
		if(p == &dbg_live) break;
	}
#ifdef PLATFORM_WIN32
#ifdef PLATFORM_WINCE
	MessageBox(::GetActiveWindow(),
	           L"Heap leaks detected !",
	           L"Warning",
	           MB_ICONSTOP|MB_OK|MB_APPLMODAL);
#else
	MessageBox(::GetActiveWindow(),
	           "Heap leaks detected !",
	           "Warning",
	           MB_ICONSTOP|MB_OK|MB_APPLMODAL);
#endif
#else
	static const char leaks[] = "Heap leaks detected!\n";
	write(2, leaks, sizeof(leaks) - 1);
#endif
}

#ifdef COMPILER_MSC

#pragma warning(disable: 4074)
#pragma init_seg(compiler)

EXITBLOCK { MemoryDumpLeaks(); }

#endif

#ifdef COMPILER_GCC
/*
struct MemoryInitDiagCls {
	~MemoryInitDiagCls() { MemoryDumpLeaks(); }
};
*/

void MemoryInitDiagnostics()
{
//	static MemoryInitDiagCls diagcls;
/*
	static bool b;
	if(!b) {
		static
		atexit(MemoryDumpLeaks);
		b = true;
		MemoryWatch(0x417FDE60);
	}
*/
}
#endif

#endif

END_UPP_NAMESPACE
