#include "error.h"

#include <util/generic/strfcpy.h>

#include <cerrno>
#include <cstring>

#if defined(_win_)
    #include <util/string/strip.h>
    #include <util/network/socket.h>
    #include <util/generic/singleton.h>
    #include "winint.h"
#endif

void ClearLastSystemError() {
#if defined(_win_)
    SetLastError(0);
#else
    errno = 0;
#endif
}

int LastSystemError() {
#if defined(_win_)
    int ret = GetLastError();

    if (ret)
        return ret;

    ret = WSAGetLastError();

    if (ret)
        return ret;
    // when descriptors number are over maximum, errno set in this variable
    ret = *(_errno());
    return ret;

#else
    return errno;
#endif
}

#if defined(_win_)
namespace {
    struct TErrString {
        inline TErrString() noexcept {
            data[0] = 0;
        }

        char data[1024];
    };
}
#endif

const char* LastSystemErrorText(int code) {
#if defined(_win_)
    TErrString& text(*Singleton<TErrString>());
    LastSystemErrorText(text.data, sizeof(text.data), code);

    return text.data;
#else
    return strerror(code);
#endif
}

#ifdef _win_
static char* Strip(char* s) {
    size_t len = strlen(s);
    const char* ptr = s;
    Strip(ptr, len);
    if (ptr != s)
        memmove(s, ptr, len);
    s[len] = 0;
    return s;
}
#endif // _win_

void LastSystemErrorText(char* str, size_t size, int code) {
#if defined(_win_)
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, code, 0, str, DWORD(size), 0);
    Strip(str);
#elif defined(_sun_)
    strfcpy(str, strerror(code), size);
#elif defined(_freebsd_) || defined(_darwin_) || defined(_musl_) || defined(_bionic_)
    strerror_r(code, str, size);
#elif defined(_linux_) | defined(_cygwin_)
    char* msg = strerror_r(code, str, size);
    strncpy(str, msg, size);
#else
    #error port me gently!
#endif
}