#include <QTcpSocket>
#include <QtXml>
#include <QtDebug>
#include <QSqlTableModel>
#include <QSqlRecord>

#include "FldigiTCPServer.h"
#include "logformat/AdiFormat.h"
#include "debug.h"

MODULE_IDENTIFICATION("qlog.core.fldigi");

FldigiTCPServer::FldigiTCPServer(QObject *parent) :
    QTcpServer(parent)
{
    FCT_IDENTIFICATION;

    listen(QHostAddress::Any, 8421);
}

void FldigiTCPServer::incomingConnection(qintptr socket)
{
    FCT_IDENTIFICATION;

    QTcpSocket* sock = new QTcpSocket(this);
    connect(sock, &QTcpSocket::readyRead, this, &FldigiTCPServer::readClient);
    connect(sock, &QTcpSocket::disconnected, this, &FldigiTCPServer::discardClient);
    sock->setSocketDescriptor(socket);
}

void FldigiTCPServer::readClient() {
    FCT_IDENTIFICATION;

    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());

    if ( !socket )
    {
        qCWarning(runtime) << "socket is null";
        return;
    }

    QString data = QString(socket->readAll());
    qCDebug(runtime) << data;

    int split = data.indexOf("\r\n\r\n", 0);
    data.remove(0, split+4);

    /* WORKAROUND - FLDIGI has probably an issue. It seems that XML
     * that is generated by FLDIGI contains Processing Instruction (clientid)
     * without mandatory space */
    data.replace("<?clientid=", "<?clientid =");

    QXmlStreamReader xml(data);
    processMethodCall(socket, xml);

    socket->close();
    if (socket->state() == QTcpSocket::UnconnectedState) socket->deleteLater();
}

void FldigiTCPServer::discardClient() {
    FCT_IDENTIFICATION;

    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
    if ( socket ) socket->deleteLater();
}

void FldigiTCPServer::processMethodCall(QTcpSocket* sock, QXmlStreamReader& xml) {
    FCT_IDENTIFICATION;

    QByteArray response;

    while (!xml.atEnd() && !xml.hasError()) {
        xml.readNextStartElement();
        if (xml.name() == QString("methodCall"))
        {
            qCDebug(runtime) << "method call";
        }

        if (xml.name() == QString("methodName"))
        {
            qCDebug(runtime) << "methodName";
            QString method = xml.readElementText();

            if (method == "log.add_record") {
                qCDebug(runtime) << "log.add_record";
                QString param = parseParam(xml);
                if (!param.isEmpty()) {
                    response = addRecord(param);
                }
            }
            else if (method == "system.listMethods") {
                qCDebug(runtime) << "system.listMethods";
                response = listMethods();
            }
        }
    }

    qCDebug(runtime) << response;

    QTextStream out(sock);

    if (!response.isEmpty()) {
        out << "HTTP/1.1 200 OK\r\n";
        out << "Content-Type: text/xml; charset=utf-8\r\n";
        out << "Content-Length: " << response.length() << "\r\n";
        out << "\r\n";
        out << response;
    }
    else {
        out << "HTTP/1.1 400 Internal Error\r\n";
    }
}

QString FldigiTCPServer::parseParam(QXmlStreamReader& xml) {
    FCT_IDENTIFICATION;

    while (!xml.atEnd() && !xml.hasError()) {
        xml.readNextStartElement();
        if (xml.name() == QString("value"))
        {
            return xml.readElementText();
        }
    }

    return QString();
}

QByteArray FldigiTCPServer::listMethods() {
    FCT_IDENTIFICATION;

    QByteArray out;

    QXmlStreamWriter xml(&out);
    xml.writeStartDocument();
    xml.writeStartElement("methodResponse");
    xml.writeStartElement("params");
    xml.writeStartElement("param");
    xml.writeStartElement("value");
    xml.writeStartElement("array");
    xml.writeStartElement("data");

    xml.writeTextElement("value", "log.add_record");
    xml.writeTextElement("value", "system.listMethods");

    xml.writeEndDocument();

    return out;
}

QByteArray FldigiTCPServer::addRecord(QString data) {
    FCT_IDENTIFICATION;

    qCDebug(function_parameters) << data;

    QByteArray out;

    QXmlStreamWriter xml(&out);
    xml.writeStartDocument();
    xml.writeStartElement("methodResponse");
    xml.writeStartElement("params");
    xml.writeStartElement("param");
    xml.writeStartElement("value");
    xml.writeEndDocument();

    QSqlTableModel model;
    model.setTable("contacts");
    model.removeColumn(model.fieldIndex("id"));

    QSqlRecord record = model.record(0);

    QTextStream in(&data);
    AdiFormat adif(in);

    adif.importNext(record);

    emit addContact(record);

    return out;
}
