#include "WinsockServer.h"

#include "WinsockSocket.h"
#include "WinsockInterface.h"

#include "FileLogger.h"

#include <thread>

WinsockServer::~WinsockServer()
{

}

void WinsockServer::listen(const Address& address, onConnectionSuccessFunc connectionSuccessFunc, onConnectionFailedFunc connectionFailedFunc)
{
    if (!mWinsockInterface)
    {
        mWinsockInterface = std::make_unique<WinsockInterface>();
        mWinsockInterface->initializeWinsock();
    }

    mConnectionCallback = connectionSuccessFunc;
    mFailedCallback = connectionFailedFunc;

    auto server_socket = std::make_unique<WinsockSocket>(address.mHost, address.mPort);

    auto on_connection = [this](SOCKET handle)
    {
        auto socket = std::make_unique<WinsockSocket>(handle);
        this->onConnection(std::move(socket));
    };
    server_socket->doListen(on_connection);

    if (server_socket->getState().mBindStatus == Socket::State::BindStatus::FAILED)
    {
        WinsockServer::Result result;
        result.mStatus = WinsockServer::Result::Status::FAILED;
        result.mErrorCode = server_socket->getState().mErrorCode;
        result.mErrorMessage = server_socket->getState().mErrorMessage;
        mFailedCallback(result);
    }
}

void WinsockServer::shutDown()
{
    mThreads.removeMarked();
    mThreads.joinAndClearAll();
}

void WinsockServer::onConnection(std::unique_ptr<WinsockSocket> s)
{
    // House-keeping first - clean up any finished threads
    MLOG_INFO("Before thread cleanup: " << mThreads.size());

    mThreads.removeMarked();

    MLOG_INFO("After thread cleanup: " << mThreads.size());

    auto worker_func = [this](std::unique_ptr<WinsockSocket> s)
    {
        MLOG_INFO("Spawned thread for new connection");

        mConnectionCallback(s.get());

        MLOG_INFO("Finished thread for new connection");
        this->onThreadComplete(std::this_thread::get_id());
    };

    auto worker = std::make_unique<std::thread>(worker_func, std::move(s));
    mThreads.add(std::move(worker));
};

void WinsockServer::onThreadComplete(std::thread::id id)
{
    mThreads.markForRemoval(id);
}
