/***************************************************************************
 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
 * SPDX-License-Identifier: GPL-3.0-or-later
 ***************************************************************************/
/** @file
 * This file is a plugin for debug.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgdebugpluginwidget.h"

#include <qdom.h>
#include <qjsengine.h>
#include <qscriptengine.h>

#include "skgdocument.h"
#include "skgmainpanel.h"
#include "skgservices.h"
#include "skgtraces.h"
#include "skgtransactionmng.h"

SKGDebugPluginWidget::SKGDebugPluginWidget(QWidget* iParent, SKGDocument* iDocument)
    : SKGTabPage(iParent, iDocument)
{
    SKGTRACEINFUNC(10)
    if (iDocument == nullptr) {
        return;
    }

    ui.setupUi(this);

    // Set icons
    ui.kSQLPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run")));
    ui.kSQLTransactionPushButton->setIcon(SKGServices::fromTheme(QStringLiteral("system-run")));
    ui.kRefreshViewsAndIndexes->setIcon(SKGServices::fromTheme(QStringLiteral("view-refresh")));

    // Fill combo box
    ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("Execute an SQL query", "Execute"));
    ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("Execute an SQL queries (one per line)", "Execute multi queries"));
    ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("help-hint")), i18nc("Explain an SQL query", "Explain"));
    ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("games-hint")), i18nc("Explain the SQL query plan", "Explain query plan"));
    ui.kExplainCmb->addItem(SKGServices::fromTheme(QStringLiteral("media-playback-start")), i18nc("Execute script", "Execute script [%1]", "javascript"));
    ui.kInput->setVisible(false);

    // Set level trace
    ui.kTraceLevel->setValue(SKGTraces::SKGLevelTrace);

    // Set profiling mode
    ui.kEnableProfilingChk->setCheckState(SKGTraces::SKGPerfo ? Qt::Checked : Qt::Unchecked);

    // Init debug page
    QStringList tables;
    ui.kSQLInput->addItem(QStringLiteral("SELECT * FROM sqlite_master;"));
    iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type in ('table', 'view')"), tables);
    int nb = tables.count();
    for (int i = 0; i < nb; ++i) {
        ui.kSQLInput->addItem("SELECT * FROM " % tables.at(i) % ';');
    }
    ui.kSQLInput->addItem(QStringLiteral("ANALYZE;"));
    ui.kSQLInput->addItem(QStringLiteral("PRAGMA integrity_check;"));
    for (int i = 0; i < nb; ++i) {
        ui.kSQLInput->addItem("PRAGMA table_info(" % tables.at(i) % ");");
        ui.kSQLInput->addItem("PRAGMA index_list(" % tables.at(i) % ");");
    }

    iDocument->getDistinctValues(QStringLiteral("sqlite_master"), QStringLiteral("name"), QStringLiteral("type='index'"), tables);
    nb = tables.count();
    for (int i = 0; i < nb; ++i) {
        ui.kSQLInput->addItem("PRAGMA index_info(" % tables.at(i) % ");");
    }
    connect(ui.kTraceLevel, &QSlider::valueChanged, this, &SKGDebugPluginWidget::onTraceLevelModified);
    connect(ui.kEnableProfilingChk, &QCheckBox::stateChanged, this, &SKGDebugPluginWidget::onProfilingModeChanged);
    connect(ui.kExplainCmb, static_cast<void (SKGComboBox::*)(int)>(&SKGComboBox::currentIndexChanged), this, &SKGDebugPluginWidget::onModeChanged);
    connect(ui.kSQLPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrder);
    connect(ui.kSQLTransactionPushButton, &QPushButton::clicked, this, &SKGDebugPluginWidget::onExecuteSqlOrderInTransaction);
    connect(ui.kRefreshViewsAndIndexes, &QPushButton::clicked, this, &SKGDebugPluginWidget::onRefreshViewsAndIndexes);
}

SKGDebugPluginWidget::~SKGDebugPluginWidget()
{
    SKGTRACEINFUNC(10)
}

QString SKGDebugPluginWidget::getState()
{
    SKGTRACEINFUNC(10)
    QDomDocument doc(QStringLiteral("SKGML"));
    QDomElement root = doc.createElement(QStringLiteral("parameters"));
    doc.appendChild(root);

    root.setAttribute(QStringLiteral("explain"), ui.kExplainCmb->currentIndex());
    root.setAttribute(QStringLiteral("enableProfiling"), ui.kEnableProfilingChk->checkState() == Qt::Checked ? QStringLiteral("Y") : QStringLiteral("N"));
    root.setAttribute(QStringLiteral("levelTraces"), ui.kTraceLevel->value());
    root.setAttribute(QStringLiteral("sqlOrder"), ui.kSQLInput->currentText());

    return doc.toString();
}

void SKGDebugPluginWidget::setState(const QString& iState)
{
    SKGTRACEINFUNC(10)
    QDomDocument doc(QStringLiteral("SKGML"));
    doc.setContent(iState);
    QDomElement root = doc.documentElement();

    QString explain = root.attribute(QStringLiteral("explain"));
    QString enableProfiling = root.attribute(QStringLiteral("enableProfiling"));
    QString levelTraces = root.attribute(QStringLiteral("levelTraces"));
    QString sqlOrder = root.attribute(QStringLiteral("sqlOrder"));
    QString sqlResult = root.attribute(QStringLiteral("sqlResult"));

    if (!explain.isEmpty()) {
        ui.kExplainCmb->setCurrentIndex(SKGServices::stringToInt(explain == QStringLiteral("Y") ? QStringLiteral("1") : explain));
    }
    if (!enableProfiling.isEmpty()) {
        ui.kEnableProfilingChk->setCheckState(enableProfiling == QStringLiteral("Y") ? Qt::Checked : Qt::Unchecked);
    }
    if (!levelTraces.isEmpty()) {
        ui.kTraceLevel->setValue(SKGServices::stringToInt(levelTraces));
    }
    ui.kSQLInput->setText(sqlOrder);
    ui.kSQLResult->setPlainText(sqlResult);
}

void SKGDebugPluginWidget::onExecuteSqlOrderInTransaction()
{
    onExecuteSqlOrder(true);
}

SKGError SKGDebugPluginWidget::executeSqlOrders(const QStringList& iSQL, QString& oOutput)
{
    SKGError err;
    int nb = iSQL.count();
    for (int i = 0; i < nb; ++i) {
        auto sql = iSQL[i].trimmed();
        if (!sql.isEmpty()) {
            oOutput += sql + '\n';

            QString oResult;
            double time = SKGServices::getMicroTime();
            err = getDocument()->dumpSelectSqliteOrder(sql, oResult);
            time = SKGServices::getMicroTime() - time;

            oOutput += oResult;
            oOutput += i18nc("Display the execution time needed by an SQL query", "\nExecution time: %1 ms", SKGServices::doubleToString(time));
            oOutput += "\n\n";
        }
    }

    IFKO(err) {
        oOutput += err.getFullMessageWithHistorical();
    }
    return err;
}

void SKGDebugPluginWidget::onExecuteSqlOrder(bool iInTransaction)
{
    SKGTRACEINFUNC(10)
    SKGError err;
    int exp = ui.kExplainCmb->currentIndex();
    if (exp == 4) {
        // Script execution
        ui.kSQLResult->clear();
        QJSEngine myEngine;
        // skgresult.setText(skgdocument.getUniqueIdentifier())
        // skgerror=skgdocument.sendMessage(QStringLiteral("Hello"))
        // skgerror=skgdocument.sendMessage(QStringLiteral("Hello"))
        // skgmainpanel.closeAllOtherPages(skgmainpanel.currentPage())
        auto t = myEngine.globalObject();
        t.setProperty(QStringLiteral("skgresult"), myEngine.newQObject(ui.kSQLResult));
        t.setProperty(QStringLiteral("skgdocument"), myEngine.newQObject(getDocument()));
        // t.setProperty(QStringLiteral("skgerror"), myEngine.newQObject(&err));
        t.setProperty(QStringLiteral("skgmainpanel"), myEngine.newQObject(SKGMainPanel::getMainPanel()));

        // Finally execute the scripting code.
        myEngine.evaluate(ui.kInput->toPlainText());
    } else if (exp == 1) {
        // SQL multi lines
        auto sqls = ui.kInput->toPlainText().split('\n');
        QString oResultGlobal;
        if (iInTransaction) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Display an SQL command from the debug plugin", "SQL command from debug plugin"), err)
            IFOKDO(err, err = SKGDebugPluginWidget::executeSqlOrders(sqls, oResultGlobal))
        } else {
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            err = SKGDebugPluginWidget::executeSqlOrders(sqls, oResultGlobal);
            QApplication::restoreOverrideCursor();
        }

        IFKO(err) {
            oResultGlobal += err.getFullMessageWithHistorical();
        }
        ui.kSQLResult->setPlainText(oResultGlobal);
    } else {
        // SQL execution
        QString text = ui.kSQLInput->currentText();
        if (exp == 2) {
            text = "EXPLAIN " % text;
        } else if (exp == 3) {
            text = "EXPLAIN QUERY PLAN " % text;
        }
        QString oResult;
        double time = SKGServices::getMicroTime();
        if (iInTransaction) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Display an SQL command from the debug plugin", "SQL command from debug plugin"), err)
            IFOKDO(err, getDocument()->dumpSelectSqliteOrder(text, oResult))
        } else {
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            err = getDocument()->dumpSelectSqliteOrder(text, oResult);
            QApplication::restoreOverrideCursor();
        }
        time = SKGServices::getMicroTime() - time;

        oResult += i18nc("Display the execution time needed by an SQL query", "\nExecution time: %1 ms", SKGServices::doubleToString(time));

        IFOK(err) {
            ui.kSQLResult->setPlainText(oResult);
        } else {
            ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical());
        }
    }
}

void SKGDebugPluginWidget::onTraceLevelModified()
{
    SKGTRACEINFUNC(10)
    SKGTraces::SKGLevelTrace = ui.kTraceLevel->value();
}

void SKGDebugPluginWidget::onModeChanged()
{
    SKGTRACEINFUNC(10)
    int exp = ui.kExplainCmb->currentIndex();
    ui.kInput->setVisible(exp == 4 || exp == 1);
    ui.kSQLInput->setVisible(!ui.kInput->isVisible());
}

void SKGDebugPluginWidget::onProfilingModeChanged()
{
    SKGTRACEINFUNC(10)
    SKGTraces::SKGPerfo = (ui.kEnableProfilingChk->checkState() == Qt::Checked);
}

void SKGDebugPluginWidget::onRefreshViewsAndIndexes()
{
    SKGTRACEINFUNC(10)
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    SKGError err;
    err = getDocument()->refreshViewsIndexesAndTriggers();
    IFKO(err) {
        ui.kSQLResult->setPlainText(err.getFullMessageWithHistorical());
    }
    QApplication::restoreOverrideCursor();
}


