#include "MessageServer.h"

#include <QDebug>


MessageServer::MessageServer(QObject *parent) :
    QTcpServer(parent)
{
}

MessageServer::~MessageServer(){
    stop();
}

bool MessageServer::start()
{
    if(isListening()){
        qDebug() << "MessageServer already listening:" << m_host << m_port;
        return false;
    }

    auto address = QHostAddress();
    if(m_host.isEmpty() || !address.setAddress(m_host)){
        qDebug() << "MessageServer address invalid:" << m_host << m_port;
        return false;
    }

    if(m_port <= 0){
        qDebug() << "MessageServer port invalid:" << m_host << m_port;
        return false;
    }

    bool listening = listen(address, m_port);
    qDebug() << "MessageServer listening:" << listening << m_host << m_port;

    return listening;
}

void MessageServer::stop()
{
    // disconnect all clients
    foreach(auto client, m_clients){
        client->close();
    }

    // then close the server
    close();
}

void MessageServer::setServer(QString host, quint16 port){
    bool listening = isListening();
    if(listening && (m_host != host || m_port != port)){
        stop();
    }

    m_host = host;
    m_port = port;

    if(listening){
        start();
    }
}

void MessageServer::setPause(bool paused)
{
    m_paused = paused;

    if(paused){
        pauseAccepting();
    } else {
        resumeAccepting();
    }
}

void MessageServer::setMaxConnections(int n){
    // set the maximum number of connections allowed
    m_maxConnections = n;

    // then, prune old ones greater than the max (fifo)
    pruneConnections();
}

int MessageServer::activeConnections(){
   int i = 0;
   foreach(auto client, m_clients){
       if(client->isConnected()) i++;
   }
   return i;
}

void MessageServer::pruneConnections(){
    // keep only the n most recent connections (fifo)
    if(m_maxConnections && m_maxConnections < activeConnections()){
        for(int i = m_maxConnections; i < activeConnections(); i++){
            auto client = m_clients.first();
            client->close();
            m_clients.removeFirst();
        }
    }
}

void MessageServer::send(const Message &message){
    foreach(auto client, m_clients){
        if(!client->awaitingResponse(message.id())){
            continue;
        }
        client->send(message);
    }
}

void MessageServer::incomingConnection(qintptr handle)
{
    qDebug() << "MessageServer incomingConnection" << handle;

    auto client = new Client(this, this);
    client->setSocket(handle);

#if JS8_MESSAGESERVER_IS_SINGLE_CLIENT
    while(!m_clients.isEmpty()){
        auto client = m_clients.first();
        client->close();
        m_clients.removeFirst();
    }
#endif

    if(m_maxConnections && m_maxConnections <= activeConnections()){
        qDebug() << "MessageServer connections full, dropping incoming connection";
        client->send(Message("API.ERROR", "Connections Full"));
        client->close();
        return;
    }

    m_clients.append(client);
}

Client::Client(MessageServer * server, QObject *parent):
    QObject(parent),
    m_server {server}
{
    setConnected(true);
}

void Client::setSocket(qintptr handle){
    m_socket = new QTcpSocket(this);

    connect(m_socket, &QTcpSocket::disconnected, this, &Client::onDisconnected);
    connect(m_socket, &QTcpSocket::readyRead, this, &Client::readyRead);

    m_socket->setSocketDescriptor(handle);
}

void Client::setConnected(bool connected){
    m_connected = connected;
}

void Client::close(){
    if(!m_socket){
        return;
    }

    m_socket->close();
    m_socket = nullptr;
}

void Client::send(const Message &message){
    if(!isConnected()){
        return;
    }

    if(!m_socket){
        return;
    }

    if(!m_socket->isOpen()){
        qDebug() << "client socket isn't open";
        return;
    }

    qDebug() << "client writing" << message.toJson();
    m_socket->write(message.toJson());
    m_socket->write("\n");
    m_socket->flush();

    // remove if needed
    if(m_requests.contains(message.id())){
        m_requests.remove(message.id());
    }
}

void Client::onDisconnected(){
    qDebug() << "MessageServer client disconnected";
    setConnected(false);
}

void Client::readyRead(){
    qDebug() << "MessageServer client readyRead";

    while(m_socket->canReadLine()){
        auto msg = m_socket->readLine().trimmed();
        qDebug() << "-> Client" << m_socket->socketDescriptor() << msg;

        if(msg.isEmpty()){
            return;
        }

        QJsonParseError e;
        QJsonDocument d = QJsonDocument::fromJson(msg, &e);
        if(e.error != QJsonParseError::NoError){
            send({"API.ERROR", "Invalid JSON (unparsable)"});
            return;
        }

        if(!d.isObject()){
            send({"API.ERROR", "Invalid JSON (not an object)"});
            return;
        }

        Message m;
        m.read(d.object());
        auto id = m.ensureId();
        m_requests[id] = m;

        emit m_server->message(m);
    }
}
