#include <Core/HAL/Win32.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Networking/Socket.h>
#include <Core/Networking/SocketPlatformImpl.h>

namespace Juliet::SocketImpl
{
    sockaddr_in CreateAddress(uint32 address, uint16 port)
    {
        auto addr            = sockaddr_in();
        addr.sin_addr.s_addr = htonl(address);
        addr.sin_family      = AF_INET;
        addr.sin_port        = htons(port);

        return addr;
    }

    SOCKET GetInvalidSocketHandle()
    {
        return INVALID_SOCKET;
    }

    void Close(SOCKET socketHandle)
    {
        ::closesocket(socketHandle);
    }

    const char* GetErrorString()
    {
        switch (WSAGetLastError())
        {
        case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK";
        case WSAEALREADY: return "WSAEALREADY";
        case WSAECONNABORTED: return "WSAECONNABORTED";
        case WSAECONNRESET: return "WSAECONNRESET";
        case WSAETIMEDOUT: return "WSAETIMEDOUT";
        case WSAENETRESET: return "WSAENETRESET";
        case WSAENOTCONN: return "WSAENOTCONN";
        case WSAEISCONN: return "WSAEISCONN";
        default: return "Other"; // TODO: Include the error number inside the string
        }
    }

    Socket::Status GetErrorStatus()
    {
        switch (WSAGetLastError())
        {
        case WSAEWOULDBLOCK: return Socket::Status::NotReady;
        case WSAEALREADY: return Socket::Status::NotReady;
        case WSAECONNABORTED: return Socket::Status::Disconnected;
        case WSAECONNRESET: return Socket::Status::Disconnected;
        case WSAETIMEDOUT: return Socket::Status::Disconnected;
        case WSAENETRESET: return Socket::Status::Disconnected;
        case WSAENOTCONN: return Socket::Status::Disconnected;
        case WSAEISCONN: return Socket::Status::Done; // when connecting a non-blocking socket
        default: return Socket::Status::Error;
        }
    }

} // namespace Juliet::SocketImpl

namespace Juliet
{
    // Windows needs its socket dll to be initialized for the whole process
    struct WSAAutoRelease
    {
        WSAAutoRelease()
        {
            WORD    wVersionRequested = MAKEWORD(2, 2);
            WSADATA wsaData;
            if (WSAStartup(wVersionRequested, &wsaData) != 0)
            {
                Log(LogLevel::Error, LogCategory::Core, "Failed to startup WinSock2");
                return;
            }
        }

        ~WSAAutoRelease() { WSACleanup(); }
    };

    WSAAutoRelease gWSAautoRelease;
} // namespace Juliet
