#include "UnixSocketServer.h"

#include "BerkeleySocket.h"

#include "FileLogger.h"

#include <thread>

UnixSocketServer::~UnixSocketServer()
{

}

void UnixSocketServer::listen(const Address& address, onConnectionSuccessFunc connectionSuccessFunc, onConnectionFailedFunc connectionFailedFunc)
{
    mConnectionCallback = connectionSuccessFunc;
    mFailedCallback = connectionFailedFunc;

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

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

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

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

void UnixSocketServer::onConnection(std::unique_ptr<BerkeleySocket> 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<BerkeleySocket> 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 UnixSocketServer::onThreadComplete(std::thread::id id)
{
    mThreads.markForRemoval(id);
}
