/***************************************************************************
 *   Copyright (C) 2020 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   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/>  *
 ***************************************************************************/
/** @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("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);
}

void SKGDebugPluginWidget::onExecuteSqlOrder(bool iInTransaction)
{
    SKGTRACEINFUNC(10)
    SKGError err;
    int exp = ui.kExplainCmb->currentIndex();
    if (exp > 2) {
        // 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 {
        // SQL execution
        QString text = ui.kSQLInput->currentText();
        if (exp == 1) {
            text = "EXPLAIN " % text;
        } else if (exp == 2) {
            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 > 2);
    ui.kSQLInput->setVisible(exp < 3);
}

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


