/*
 * xmlconsole.cpp - dialog for interacting manually with XMPP
 * Copyright (C) 2001-2002  Justin Karneges, Remko Troncon
 *
 * 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 2
 * 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 "xmlconsole.h"

#include "BasicXMLSyntaxHighlighter/BasicXMLSyntaxHighlighter.h"
#include "iconset.h"
#include "iris/xmpp_client.h"
#include "psiaccount.h"
#include "psicon.h"
#include "psicontactlist.h"
#include "textutil.h"

#include <QCheckBox>
#include <QHBoxLayout>
#include <QLayout>
#include <QMessageBox>
#include <QPushButton>
#include <QScrollBar>
#include <QTextEdit>
#include <QTextFrame>
#include <QVBoxLayout>

//----------------------------------------------------------------------------
// XmlConsole
//----------------------------------------------------------------------------
XmlConsole::XmlConsole(PsiAccount *_pa) : QWidget()
{
    ui_.setupUi(this);
    setWindowIcon(IconsetFactory::icon("psi/xml").icon());
    pa = _pa;
    pa->dialogRegister(this);
    connect(pa, SIGNAL(updatedAccount()), SLOT(updateCaption()));
    connect(pa->client(), SIGNAL(xmlIncoming(const QString &)), SLOT(client_xmlIncoming(const QString &)));
    connect(pa->client(), SIGNAL(xmlOutgoing(const QString &)), SLOT(client_xmlOutgoing(const QString &)));
    connect(pa->psi(), SIGNAL(accountCountChanged()), this, SLOT(updateCaption()));
    updateCaption();

    prompt = nullptr;

    ui_.te->setUndoRedoEnabled(false);
    ui_.te->setReadOnly(true);
    ui_.te->setAcceptRichText(false);
    BasicXMLSyntaxHighlighter *highlighter = new BasicXMLSyntaxHighlighter(ui_.te);
    Q_UNUSED(highlighter);

    connect(ui_.pb_clear, SIGNAL(clicked()), SLOT(clear()));
    connect(ui_.pb_input, SIGNAL(clicked()), SLOT(insertXml()));
    connect(ui_.pb_close, SIGNAL(clicked()), SLOT(close()));
    connect(ui_.pb_dumpRingbuf, SIGNAL(clicked()), SLOT(dumpRingbuf()));

    resize(560, 400);
}

XmlConsole::~XmlConsole() { pa->dialogUnregister(this); }

void XmlConsole::clear() { ui_.te->clear(); }

void XmlConsole::updateCaption()
{
    if (pa->psi()->contactList()->enabledAccounts().count() > 1)
        setWindowTitle(pa->name() + ": " + tr("XMPP Console"));
    else
        setWindowTitle(tr("XMPP Console"));
}

void XmlConsole::enable() { ui_.ck_enable->setChecked(true); }

bool XmlConsole::filtered(const QString &str) const
{
    if (ui_.ck_enable->isChecked()) {
        // Only do parsing if needed
        if (!ui_.le_jid->text().isEmpty() || !ui_.ck_iq->isChecked() || !ui_.ck_message->isChecked()
            || !ui_.ck_presence->isChecked() || !ui_.ck_sm->isChecked()) {
            QDomDocument doc;
            if (!doc.setContent(str))
                return true;

            QDomElement e  = doc.documentElement();
            QString     tn = e.tagName();
            if ((tn == "iq" && !ui_.ck_iq->isChecked()) || (tn == "message" && !ui_.ck_message->isChecked())
                || (tn == "presence" && !ui_.ck_presence->isChecked())
                || ((tn == "a" || tn == "r") && !ui_.ck_sm->isChecked()))
                return true;

            if (!ui_.le_jid->text().isEmpty()) {
                Jid  jid(ui_.le_jid->text());
                bool hasResource = !jid.resource().isEmpty();
                if (!jid.compare(e.attribute("to"), hasResource) && !jid.compare(e.attribute("from"), hasResource))
                    return true;
            }
        }
        return false;
    }
    return true;
}

void XmlConsole::dumpRingbuf()
{
    QList<PsiAccount::xmlRingElem> buf        = pa->dumpRingbuf();
    bool                           enablesave = ui_.ck_enable->isChecked();
    ui_.ck_enable->setChecked(true);
    QString stamp;
    for (const PsiAccount::xmlRingElem &el : std::as_const(buf)) {
        stamp = "<!-- TS:" + el.time.toString(Qt::ISODate) + "-->";
        if (el.type == PsiAccount::RingXmlOut) {
            client_xmlOutgoing(stamp + el.xml);
        } else {
            client_xmlIncoming(stamp + el.xml);
        }
    }
    ui_.ck_enable->setChecked(enablesave);
}

void XmlConsole::addRecord(bool incoming, const QString &str)
{
    if (filtered(str))
        return;
    auto *textEdit    = ui_.te;
    auto *scrollBar   = textEdit->verticalScrollBar();
    int   prevSPos    = scrollBar->value();
    bool  wasAtBottom = (prevSPos == scrollBar->maximum());
    auto  prevCur     = textEdit->textCursor();

    QTextFrameFormat f;
    f.setBackground(incoming ? QColorConstants::Svg::lemonchiffon : QColorConstants::Svg::lightpink);
    textEdit->moveCursor(QTextCursor::End);
    textEdit->textCursor().insertFrame(f);
    textEdit->insertPlainText(str);

    if (!wasAtBottom) {
        textEdit->setTextCursor(prevCur);
        scrollBar->setValue(prevSPos);
    } else {
        scrollBar->setValue(scrollBar->maximum());
    }
}

void XmlConsole::client_xmlIncoming(const QString &str) { addRecord(true, str); }

void XmlConsole::client_xmlOutgoing(const QString &str) { addRecord(false, str); }

void XmlConsole::insertXml()
{
    if (prompt)
        bringToFront(prompt);
    else {
        prompt = new XmlPrompt(this);
        connect(prompt, SIGNAL(textReady(const QString &)), SLOT(xml_textReady(const QString &)));
        prompt->show();
    }
}

void XmlConsole::xml_textReady(const QString &str) { pa->client()->send(str); }

//----------------------------------------------------------------------------
// XmlPrompt
//----------------------------------------------------------------------------
XmlPrompt::XmlPrompt(QWidget *parent) : QDialog(parent)
{
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowTitle(tr("XML Input"));

    QVBoxLayout *vb1 = new QVBoxLayout(this);
    vb1->setContentsMargins(8, 8, 8, 8);

    te = new QTextEdit(this);
    te->setAcceptRichText(false);
    vb1->addWidget(te);
    BasicXMLSyntaxHighlighter *highlighter = new BasicXMLSyntaxHighlighter(te);
    Q_UNUSED(highlighter);

    QHBoxLayout *hb1 = new QHBoxLayout;
    vb1->addLayout(hb1);
    QPushButton *pb;

    pb = new QPushButton(tr("&Transmit"), this);
    pb->setDefault(true);
    connect(pb, SIGNAL(clicked()), SLOT(doTransmit()));
    hb1->addWidget(pb);
    hb1->addStretch(1);

    pb = new QPushButton(tr("&Close"), this);
    connect(pb, SIGNAL(clicked()), SLOT(close()));
    hb1->addWidget(pb);

    resize(800, 600);
}

XmlPrompt::~XmlPrompt() { }

void XmlPrompt::doTransmit()
{
    QString str = te->toPlainText();
    // Validate input
    QDomDocument doc;
    if (!doc.setContent(str)) {
        QMessageBox::warning(this, tr("Malformed XML"), tr("You have entered malformed XML input."));
        return;
    }

    emit textReady(str);
    close();
}
