#include "socketserver.h"

#include <netinet/in.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

#include <QMutex>
#include <QVector>
#include <QThread>
#include <QDebug>

SocketServer* SocketServer::_self = nullptr;

SocketServer* SocketServer::self(QObject *parent)
{
    static QMutex mutex;
    mutex.lock();
    if(!_self)
        _self = new SocketServer(parent);
    mutex.unlock();
    return _self;
}

SocketServer::SocketServer(QObject *parent)
    :QObject(nullptr)
{

}

SocketServer::~SocketServer()
{

}

void SocketServer::run()
{
    qDebug() << "Starting daemon server...";
    signal(SIGPIPE, processSignal);
    m_socket = ::socket(AF_UNIX, SOCK_STREAM, 0);

    if (this->m_socket == -1) {
        throw std::runtime_error{"Error starting server: Can not create socket"};
    }
    struct sockaddr_un addr = {};
    addr.sun_family         = AF_UNIX;
    memset(&addr.sun_path[ 0 ], 0, sizeof(addr.sun_path));
    SOCKET_NAME.copy(&addr.sun_path[ 0 ], SOCKET_NAME.size(), 0);

    auto socketAddr = reinterpret_cast<sockaddr *>(&addr); // NOLINT
    if (bind(this->m_socket, socketAddr, sizeof(addr)) == -1) {
        throw std::runtime_error{"Error starting server: Can not bind socket"};
    }

    if (::listen(this->m_socket, 10) == -1) {
        throw std::runtime_error{"Error starting server: Socket listen"};
    }

    std::thread connectThread{[ this ]() {
        while (true) {
            struct sockaddr_in clientAddr       = {};
            socklen_t          clientAddrLength = sizeof(clientAddr);
            auto               socketAddrIn = reinterpret_cast<sockaddr *>(&clientAddr); // NOLINT
            int                client       = accept(this->m_socket, socketAddrIn, &clientAddrLength);

            if (client != -1) {
                qDebug() << "New client connected with ID: " << client;
                this->m_clients.push_back(client);
            }
        }
    }};
    connectThread.detach();
}

void SocketServer::send(Event event)
{
    QVector<int> disconnectedClients;
    for(int client : m_clients)
    {
        int written = write(client, &event, sizeof(Event));
        if (written < 0) {
            qDebug() << "Error sending message to daemon with ID " << client << ". daemon disconnected";
            disconnectedClients.push_back(client);
        }
    }

    // Disconnect dead clients
    for (auto client : disconnectedClients) {
        m_clients.erase(std::remove(m_clients.begin(), m_clients.end(), client), m_clients.end());
    }
}
