// 25 november 2015
#ifdef __cplusplus
#error msbuild is being dumb and making this a C++ file
#endif
#include "unmanaged.h"

// TODO this won't work if initAlloc() failed

#define initErrorFormat L"error %s: %s%s%s %I32u (0x%I32X)%s"
#define initErrorArgs wmessage, sysmsg, beforele, label, value, value, afterle

static const char *initerr(const char *message, const WCHAR *label, DWORD value)
{
	// TODO
	return "";
#if 0
	WCHAR *sysmsg;
	BOOL hassysmsg;
	WCHAR *beforele;
	WCHAR *afterle;
	int n;
	WCHAR *wmessage;
	WCHAR *wstr;
	const char *str;

	if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, value, 0, (LPWSTR) (&sysmsg), 0, NULL) != 0) {
		hassysmsg = TRUE;
		beforele = L" (";
		afterle = L")";
	} else {
		hassysmsg = FALSE;
		sysmsg = L"";
		beforele = L"";
		afterle = L"";
	}
	wmessage = toUTF16(message);
	n = _scwprintf(initErrorFormat, initErrorArgs);
	wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]");
	snwprintf(wstr, n + 1, initErrorFormat, initErrorArgs);
	str = toUTF8(wstr);
	uiFree(wstr);
	if (hassysmsg)
		if (LocalFree(sysmsg) != NULL)
			logLastError("error freeing system message in loadLastError()");
	uiFree(wmessage);
	return str;
#endif
}

static const char *loadLastError(const char *message)
{
	return initerr(message, L"GetLastError() ==", GetLastError());
}

static const char *loadHRESULT(const char *message, HRESULT hr)
{
	return initerr(message, L"HRESULT", (DWORD) hr);
}

// On the subject of CoInitialize(), or why this isn't in main.cpp:
// If we don't set up the current thread otherwise, the first time .net tries to call out to unmanaged code, it will automatically set up a MTA for COM.
// This is not what we want; we need a STA instead.
// Since we're not in control of main(), we can't stick a [STAThread] on it, so we have to do it ourselves.
// This is a separate .c file for two reasons:
// 1) To avoid the unmanaged jump that a call to CoInitialize() would do (it seems to detect a call to CoInitialize()/CoInitializeEx() but let's not rely on it)
// 2) To avoid mixing Windows API headers with .net
// See also http://stackoverflow.com/questions/24348205/how-do-i-solve-this-com-issue-in-c

uiInitOptions options;

const char *uiInit(uiInitOptions *o)
{
	HRESULT hr;

	options = *o;

	if (initAlloc() == 0)
		return loadLastError("error initializing memory allocations");

	// TODO https://msdn.microsoft.com/en-us/library/5s8ee185%28v=vs.71%29.aspx use CoInitializeEx()?
	hr = CoInitialize(NULL);
	if (hr != S_OK && hr != S_FALSE)
		return loadHRESULT("initializing COM", hr);

	// now do the rest of initialization on the managed side
	initWPF();

	return NULL;
}

void uiUninit(void)
{
	uninitWPF();
	CoUninitialize();
	uninitTypes();
	uninitAlloc();
}

void uiFreeInitError(const char *err)
{
	uiFree((void *) err);
}