#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>

#include "runtime.h"

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#define DbgBreak DebugBreak()
#elif defined(__GNUC__)
#define DbgBreak __builtin_trap()
#else
/* Not available */
#define DbgBreak
#endif

/* TODO(Kevin): Log to file, show error message box, ... */

#ifdef _WIN32
void Win32ErrorToString(DWORD last_error, char *out, int bufsize) {
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
                       FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                   NULL,
                   last_error,
                   0,
                   out,
                   bufsize,
                   NULL);
}
#endif

static bool DisplayErrorBox(const char *text) {
#ifdef _WIN32
    WCHAR msg[256];
    rtUTF8ToWStr(text, msg, RT_ARRAY_COUNT(msg));

#ifdef RT_ERROR_REPORT_DEBUGBREAK
    return MessageBoxW(NULL, msg, L"Error", MB_OKCANCEL | MB_ICONERROR) == IDCANCEL;
#else
    MessageBoxW(NULL, msg, L"Error", MB_ICONERROR);
    return false;
#endif /* RT_ERROR_REPORT_DEBUGBREAK */
#else
    return false;
#endif
}

static void LogOut(const char *text) {
#ifdef _WIN32
    WCHAR msg[512];
    rtUTF8ToWStr(text, msg, RT_ARRAY_COUNT(msg));
    OutputDebugStringW(msg);
#endif
    fprintf(stderr, "%s", text);
}

RT_DLLEXPORT void rtReportError(const char *subsystem, const char *fmt, ...) {
    char buf[512];

    int at = rtSPrint(buf, RT_ARRAY_COUNT(buf) - 1, "[%s] ", subsystem);
    va_list ap;
    va_start(ap, fmt);
    at += rtVSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, fmt, ap);
    va_end(ap);
    at += rtSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at - 1, "\n");

    LogOut(buf);
    if (DisplayErrorBox(buf)) {
        DbgBreak;
    }
}

RT_DLLEXPORT void rtLog(const char *subsystem, const char *fmt, ...) {
    char buf[512];

    int at = rtSPrint(buf, RT_ARRAY_COUNT(buf), "[%s] ", subsystem);
    va_list ap;
    va_start(ap, fmt);
    at += rtVSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at, fmt, ap);
    va_end(ap);
    at += rtSPrint(&buf[at], RT_ARRAY_COUNT(buf) - at, "\n");

    LogOut(buf);
}
