
#include <windows.h>
#include <profileapi.h>

namespace drvutil {

static void strerror_safe(int errnum, char errbuf[256]) {
    int status = strerror_s(errbuf, 256, errnum);
    if (status != 0) {
        snprintf(errbuf, 256, "unknown errno %d", errnum);
    }
}

std::string strerror_str(int errnum) {
    char buf[256];
    strerror_safe(errnum, buf);
    return buf;
}

class MonoClock {
public:
    double freq_;
    LONGLONG base_;
    inline LONGLONG qpc() {
        LARGE_INTEGER x;
        BOOL status = QueryPerformanceCounter(&x);
        assert(status != 0);
        return x.QuadPart;
    }
    MonoClock() {
        LARGE_INTEGER x;
        BOOL status = QueryPerformanceFrequency(&x);
        assert(status != 0);
        freq_ = 1.0 / double(x.QuadPart);
        base_ = qpc();
    }
    double get() {
        return (qpc() - base_) * freq_;
    }
};

static MonoClock monoclock;
double get_monotonic_clock() {
    return monoclock.get();
}

void console_write(const std::u32string &cps) {
    if (cps.size() == 0) return;
    // Convert to wstring.   Any character not representable as a single wchar_t
    // is replaced with a box.  It's not ideal, but it's pretty good.
    std::wstring ws(cps.size(), 0);
    for (int i = 0; i < int(cps.size()); i++) {
        char32_t c = cps[i];
        if (drvutil::is_single_wchar_t(c)) ws[i] = (wchar_t)c;
        else ws[i] = 0x2610;
    }
    HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
    assert(hstdout != INVALID_HANDLE_VALUE);
    DWORD nwrote;
    std::wstring_view v(ws);
    while (v.size() > 0) {
        int nwrite = v.size();
        if (nwrite > 10000) nwrite = 10000;
        assert(WriteConsoleW(hstdout, v.data(), nwrite, &nwrote, nullptr));
        assert(nwrote > 0);
        v.remove_prefix(nwrote);
    }
}

std::u32string console_read() {
    HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
    assert(hstdin != INVALID_HANDLE_VALUE);
    INPUT_RECORD inrecords[512];
    DWORD nread, nevents;
    if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) {
        if (int(nevents) > 0) {
            if (int(nevents) > 512) nevents = 512;
            ReadConsoleInputW(hstdin, inrecords, nevents, &nread);
            std::u32string result(nread, 0);
            int len = 0;
            for (int i = 0; i < int(nread); i++) {
                const INPUT_RECORD &inr = inrecords[i];
                if (inr.EventType != KEY_EVENT) continue;
                const KEY_EVENT_RECORD &key = inr.Event.KeyEvent;
                if (!key.bKeyDown) continue;
                result[len++] = key.uChar.UnicodeChar;
            }
            return result.substr(0, len);
        }
    }
    return std::u32string();
}

} // namespace drvutil