#include <Pinger/Pinger.hpp>
#include <Protocol/Protocol.hpp>
#include <Utilities/Socket.hpp>
#include <thread>

Pinger::Pinger(std::string addr) : addr(addr)
{
    std::shared_ptr<PingState> state = std::make_shared<PingState>();
    this->state = state;

    std::thread([state, addr]()
    {
        SOCKET sock = connectSocket(addr, state->error);
        if (sock == INVALID_SOCKET)
        {
            state->ready = true;
            return;
        }

        Uint32 startTicks = SDL_GetTicks();

        state->resolvedAddr = getSocketRemoteAddr(sock);

        // Send a Ping greeting.
        Protocol::ClientGreetingHeader clientHeader(Protocol::ClientGreetingCode::Ping);
        if (!writeSocket(sock, (char*) &clientHeader, sizeof(clientHeader)))
        {
            closesocket(sock);
            state->error = "Connection interrupted.";
            state->ready = true;
            return;
        };

        // Read the server greeting.
        Protocol::ServerGreetingHeader serverHeader;
        if (!readSocket(sock, (char*) &serverHeader, sizeof(serverHeader)))
        {
            closesocket(sock);
            state->error = "Connection interrupted.";
            state->ready = true;
            return;
        };

        // Check that the greeting is valid.
        if (!serverHeader.isValid())
        {
            closesocket(sock);
            state->error = "Invalid server greeting.";
            state->ready = true;
            return;
        };

        // Check that it's a Pong.
        if (serverHeader.code != Protocol::ServerGreetingCode::Pong)
        {
            closesocket(sock);
            state->error = "Invalid server greeting.";
            state->ready = true;
            return;
        }

        char motto[33];
        if (!readSocket(sock, motto, 32))
        {
            closesocket(sock);
            state->error = "Connection interrupted.";
            state->ready = true;
            return;
        }

        motto[32] = 0;

        state->timeMS = SDL_GetTicks() - startTicks;
        state->motto = std::string(motto);
        state->serverVersion = serverHeader.version;
        state->ready = true;
    }).detach();
}

Pinger::State Pinger::getState()
{
    if (!state->ready)
    {
        return Pinger::State::Pinging;
    }

    if (!state->error.empty())
    {
        return Pinger::State::Error;
    }

    return Pinger::State::Success;
}

const std::string& Pinger::getError()
{
    return state->error;
}

const std::string& Pinger::getMotto()
{
    return state->motto;
}

Uint32 Pinger::getTimeMS()
{
    return state->timeMS;
}

Version Pinger::getServerVersion()
{
    return state->serverVersion;
}

const std::string& Pinger::getAddr()
{
    return addr;
}

const std::string& Pinger::getResolvedAddr()
{
    return state->resolvedAddr;
}