#include <stdio.h>
#include <string.h>

#include "core.h"
#include "log.h"

#include <SDL3/SDL.h>

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <DbgHelp.h>
#include <stdio.h>
#include <cstdarg>
#pragma comment(lib, "DbgHelp.lib")

LONG WINAPI UnhandleExceptionFilter(_EXCEPTION_POINTERS* ExceptionInfo)
{
	SYSTEMTIME time;
	GetLocalTime(&time);

	char executableName[256];
	GetModuleFileNameA(NULL, executableName, 256);

	char minidumpPath[256];
	sprintf(minidumpPath, "%s_%i-%i-%i_%i-%i-%i-%i.dmp",
		executableName, time.wYear, time.wMonth, time.wDay,
		time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);

	HANDLE minidumpFile = CreateFileA(minidumpPath, GENERIC_WRITE,
		0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (minidumpFile != INVALID_HANDLE_VALUE)
	{
		MINIDUMP_EXCEPTION_INFORMATION minidumpInfo;
		minidumpInfo.ThreadId = GetCurrentThreadId();
		minidumpInfo.ExceptionPointers = ExceptionInfo;
		minidumpInfo.ClientPointers = FALSE;

		DWORD minidumpType =
			MiniDumpNormal |
			MiniDumpFilterMemory |
			MiniDumpScanMemory |
			MiniDumpWithDataSegs |
			MiniDumpWithIndirectlyReferencedMemory;

		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), minidumpFile,
			(MINIDUMP_TYPE)minidumpType, &minidumpInfo, 0, 0);

		CloseHandle(minidumpFile);
	}

	return EXCEPTION_CONTINUE_SEARCH;
}

void CDebugManager__Initialize()
{
	if (!IsDebuggerPresent())
		SetUnhandledExceptionFilter(UnhandleExceptionFilter);

	char szExecutableDirectory[MAX_PATH];
	int iLen = GetModuleFileNameA(GetModuleHandleA(NULL), szExecutableDirectory, MAX_PATH);

	// Search for last path character
	int iExePos = iLen - 4;
	int iDirectoryPos = 0;
	for (int i = iExePos; i > 0; i--)
	{
		if (szExecutableDirectory[i] == '\\')
		{
			iDirectoryPos = i;
			break;
		}
	}

	// Trim string
	szExecutableDirectory[iDirectoryPos] = '\0';

	BOOL bResult = SymInitialize(GetCurrentProcess(), szExecutableDirectory, FALSE);
	OutputDebugStringA(bResult ? "SymInitialize ok\n" : "SymInitialize failed\n");
}

void CDebugManager__Shutdown()
{
	SymCleanup(GetCurrentProcess());
}
#endif

void error_backend(const char* txt)
{
	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Engine Error", txt, nullptr);
}

void warning_backend(const char* txt)
{
	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning!", txt, nullptr);
}

//static void initializeConsole() {
//	AllocConsole(); 
//	freopen("CONOUT$", "w", stdout);
//}

int Core::ms_argc;
char** Core::ms_argv;

void Core::Init(int argc, char* argv[])
{
	ms_argc = argc;
	ms_argv = argv;

	// initialize timer
	//getSystemTimer()->init();

#ifdef WIN32
	//initializeConsole();
	CDebugManager__Initialize();
#endif

	Logger::Open();
}

void Core::Shutdown()
{
	Logger::Close();

	// shutdown timer
	//getSystemTimer()->shutdown();

#ifdef WIN32
	CDebugManager__Shutdown();
#endif
}

void Core::Frame()
{
	//getSystemTimer()->update();
}

void Core::Error(const char* msg, ...)
{
	static char buf[2048 * 2];
	va_list args;

	va_start(args, msg);
	vsnprintf(buf, sizeof(buf), msg, args);
	va_end(args);

	Logger::Msg("ERROR! %s", buf);

	error_backend(buf);

//	__debugbreak();

#ifdef WIN32
	ExitProcess(1);
#endif // WIN32
}

void Core::Warning(const char* msg, ...)
{
	static char buf[2048 * 2];
	va_list args;

	va_start(args, msg);
	vsnprintf(buf, sizeof(buf), msg, args);
	va_end(args);

	Logger::Msg("Warning! %s", buf);
	
	warning_backend(buf);
}

bool Core::HasOption(const char* name)
{
	for (int i = 0; i < ms_argc; i++) {
		if (ms_argv[i]) {
			if (strcmp(ms_argv[i], name) == 0)
				return true;
		}
	}

	return false;
}

const char* Core::GetOptionParameter(const char* name)
{
	for (int i = 0; i < ms_argc; i++) {
		if (ms_argv[i]) {
			if (strcmp(ms_argv[i], name) == 0) {
				if (!ms_argv[i + 1]) {
					Core::Error("Commandline option %s doesn't have parameter!", name);
				} else {
					return ms_argv[i + 1];
				}
			}
		}
	}

	return nullptr;
}
