/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

#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());
    }
}
