// 6 april 2015
#include "uipriv_windows.hpp"

HINSTANCE uipriv_hInstance = NULL;
int uipriv_nCmdShow;

#ifndef uiStatic

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved)
{
	if (fdwReason == DLL_PROCESS_ATTACH)
		uipriv_hInstance = hInstance;
	return TRUE;
}

static inline void setHInstance(void)
{
	// do nothing; set by DllMain() above
}

#else

// see https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
EXTERN_C IMAGE_DOS_HEADER __ImageBase;

static inline void setHInstance(void)
{
	uipriv_hInstance = (HINSTANCE) (&__ImageBase);
}

#endif

//HFONT hMessageFont;

#define wantedICCClasses ( \
	ICC_STANDARD_CLASSES |	/* user32.dll controls */		\
	ICC_PROGRESS_CLASS |		/* progress bars */			\
	ICC_TAB_CLASSES |			/* tabs */					\
	ICC_LISTVIEW_CLASSES |		/* table headers */			\
	ICC_UPDOWN_CLASS |		/* spinboxes */			\
	ICC_BAR_CLASSES |			/* trackbar */				\
	ICC_DATE_CLASSES |		/* date/time picker */		\
	0)

// TODO add format string warning detection to all these functions, where available
#define uiprivInitReturnHRESULT(err, msg, hr) uiprivInitReturnErrorf(err, "%s: 0x%08I32X", msg, hr)

static DWORD mainThread;

bool uiprivSysInit(void *options, uiInitError *err)
{
	STARTUPINFOW si;
	HICON hDefaultIcon;
	HCURSOR hDefaultCursor;
	INITCOMMONCONTROLSEX icc;
	HRESULT hr;

	setHInstance();
	uipriv_nCmdShow = SW_SHOWDEFAULT;
	GetStartupInfoW(&si);
	if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
		uipriv_nCmdShow = si.wShowWindow;

	hr = uiprivHrLoadIconW(NULL, IDI_APPLICATION, &hDefaultIcon);
	if (hr != S_OK)
		return uiprivInitReturnHRESULT(err, "failed to load default icon", hr);
	hr = uiprivHrLoadCursorW(NULL, IDC_ARROW, &hDefaultCursor);
	if (hr != S_OK)
		return uiprivInitReturnHRESULT(err, "failed to load default cursor", hr);

	hr = uiprivInitUtilWindow(hDefaultIcon, hDefaultCursor);
	if (hr != S_OK)
		return uiprivInitReturnHRESULT(err, "failed to initialize the utility window", hr);

	ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
	icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
	icc.dwICC = wantedICCClasses;
	if (InitCommonControlsEx(&icc) == 0) {
		DWORD lasterr;

		lasterr = GetLastError();
		if (lasterr == 0)
			return uiprivInitReturnErrorf(err, "InitCommonControlsEx() failed, but didn't specify why. This usually means you forgot the Common Controls v6 manifest; refer to the libui documentation for instructions.");
		hr = HRESULT_FROM_WIN32(lasterr);
		return uiprivInitReturnHRESULT(err, "InitCommonControlsEx() failed", hr);
	}

/*	hr = CoInitialize(NULL);
	if (hr != S_OK && hr != S_FALSE)
		return uiprivInitReturnHRESULT(err, "CoInitialize() failed", hr);
	// TODO initialize COM security
	// TODO turn off COM exception handling
*/
	mainThread = GetCurrentThreadId();
	return true;
}

void uiMain(void)
{
	MSG msg;
	HRESULT hr;

	if (!uiprivCheckInitializedAndThread())
		return;
	for (;;) {
		hr = uiprivHrGetMessageW(&msg, NULL, 0, 0);
		if (hr == S_FALSE)		// WM_QUIT
			return;
		if (hr != S_OK)
			uiprivInternalError("GetMessageW() failed in uiMain(): 0x%08I32X", hr);
		// TODO IsDialogMessage()
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
}

void uiQuit(void)
{
	if (!uiprivCheckInitializedAndThread())
		return;
	PostQuitMessage(0);
}

void uiprivSysQueueMain(void (*f)(void *data), void *data)
{
	HRESULT hr;

	hr = uiprivHrPostMessageW(uiprivUtilWindow, uiprivUtilWindowMsgQueueMain, (WPARAM) f, (LPARAM) data);
	if (hr != S_OK)
		uiprivInternalError("PostMessageW() failed in uiQueueMain(): 0x%08I32X", hr);
}

bool uiprivSysCheckThread(void)
{
	return GetCurrentThreadId() == mainThread;
}

void uiprivReportError(const char *prefix, const char *msg, const char *suffix, bool internal)
{
	// Print to both stderr and the debugger; the former handles the case of a console and the latter handles the case of not.
	// TODO find a reliable way to check if stderr is connected (the mere presence of a console window is not one)
	fprintf(stderr, "*** %s: %s. %s\n", prefix, msg, suffix);
	// Use OutputDebugStringA() directly because these *are* runtime MBCS strings.
	OutputDebugStringA(prefix);
	OutputDebugStringA(": ");
	OutputDebugStringA(msg);
	OutputDebugStringA(". ");
	OutputDebugStringA(suffix);
	OutputDebugStringA("\n");
	DebugBreak();
	abort();		// we shouldn't reach here
}