/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "TriggerItemWidget.hpp"

#include "Highlighter.hpp"
#include "ServerHandler.hpp"
#include "TriggeredScanner.hpp"
#include "VNode.hpp"
#include "VSettings.hpp"

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    #include <QGuiApplication>
#endif

#include <QButtonGroup>

//========================================================
//
// TriggerItemWidget
//
//========================================================

TriggerItemWidget::TriggerItemWidget(QWidget* parent) : QWidget(parent) {
    // This item will listen to any changes in nodes
    handleAnyChange_ = true;

    // We will not keep the contents when the item becomes unselected
    // unselectedFlags_.clear();

    setupUi(this);

    // Scanner
    scanner_ = new TriggeredScanner(this);

    connect(scanner_, SIGNAL(scanStarted()), this, SLOT(scanStarted()));

    connect(scanner_, SIGNAL(scanFinished()), this, SLOT(scanFinished()));

    connect(scanner_, SIGNAL(scanProgressed(int)), this, SLOT(scanProgressed(int)));

    triggerTable_->setTriggeredScanner(scanner_);
    triggerGraph_->setTriggeredScanner(scanner_);

    // Messages
    messageLabel_->hide();
    messageLabel_->setShowTypeTitle(false);

    // Dependency info inti must precede dependency init
    // since they depend on each other

    // dependency info is off by default
    dependInfoTb_->setChecked(false);
    on_dependInfoTb__toggled(false);

    // Dependency is off by default
    // dependTb_->setProperty("triggerDepend","1");
    dependTb_->setChecked(false);
    on_dependTb__toggled(false);

    // Expression
    exprTb_->setChecked(true);

    // The document becomes the owner of the highlighter
    new Highlighter(exprTe_->document(), "trigger");
    exprTe_->setReadOnly(true);
    exprTe_->setBackgroundVisible(true);

    // Set the height of the trigger expression display area
    QFont fTe;
    fTe.setBold(true);
    QFontMetrics fm(fTe);
    exprTe_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    exprHeight_      = fm.size(0, "A\nA\nA").height() + fm.height() / 2;
    exprEmptyHeight_ = fm.size(0, "A").height() + fm.height() / 2;
    exprTe_->setFixedHeight(exprEmptyHeight_);

    // view mode - we show the table by default
    modeGroup_ = new QButtonGroup(this);
    modeGroup_->addButton(tableTb_, TableModeIndex);
    modeGroup_->addButton(graphTb_, GraphModeIndex);
    tableTb_->setChecked(true);
    modeStacked_->setCurrentIndex(TableModeIndex);

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    connect(
        modeGroup_, SIGNAL(buttonToggled(QAbstractButton*, bool)), this, SLOT(slotChangeMode(QAbstractButton*, bool)));
#else
    connect(modeGroup_, SIGNAL(buttonToggled(int, bool)), this, SLOT(slotChangeMode(int, bool)));
#endif
    zoomSlider_->setMaximumWidth(120);
    triggerGraph_->setZoomSlider(zoomSlider_);
    zoomLabel_->setProperty("graphTitle", "1");
    infoTb_->hide();
    showGraphButtons(false);

    // table
    connect(triggerTable_, SIGNAL(depInfoWidgetClosureRequested()), this, SLOT(slotHandleDefInfoWidgetClosure()));

    connect(triggerTable_, SIGNAL(linkSelected(VInfo_ptr)), this, SLOT(slotLinkSelected(VInfo_ptr)));

    connect(triggerTable_,
            SIGNAL(infoPanelCommand(VInfo_ptr, QString)),
            this,
            SLOT(slotInfoPanelCommand(VInfo_ptr, QString)));

    connect(triggerTable_,
            SIGNAL(dashboardCommand(VInfo_ptr, QString)),
            this,
            SLOT(slotDashboardCommand(VInfo_ptr, QString)));

    // graph
    connect(triggerGraph_, SIGNAL(linkSelected(VInfo_ptr)), this, SLOT(slotLinkSelected(VInfo_ptr)));

    connect(triggerGraph_,
            SIGNAL(infoPanelCommand(VInfo_ptr, QString)),
            this,
            SLOT(slotInfoPanelCommand(VInfo_ptr, QString)));

    connect(triggerGraph_,
            SIGNAL(dashboardCommand(VInfo_ptr, QString)),
            this,
            SLOT(slotDashboardCommand(VInfo_ptr, QString)));
}

TriggerItemWidget::~TriggerItemWidget() = default;

QWidget* TriggerItemWidget::realWidget() {
    return this;
}

void TriggerItemWidget::reload(VInfo_ptr info) {
    assert(active_);

    if (suspended_)
        return;

    clearContents();

    if (info && info->server() && info->server()->isDisabled()) {
        setEnabled(false);
        return;
    }
    else {
        setEnabled(true);
    }

    // set the info
    adjust(info);

    // messageLabel_->hide();

    // Info must be a node
    load();
}

void TriggerItemWidget::load() {
    clearTriggers();

    if (info_ && info_->isNode() && info_->node()) {
        VNode* n = info_->node();
        Q_ASSERT(n);

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
        // Display trigger expression
        std::string te, ce;
        n->triggerExpr(te, ce);
        QString txt = QString::fromStdString(te);
        if (txt.isEmpty()) {
            txt = tr("No trigger expression is available for the selected node!");
            if (exprTe_->height() != exprEmptyHeight_) {
                exprTe_->setFixedHeight(exprEmptyHeight_);
            }
        }
        else if (exprTe_->height() != exprHeight_) {
            exprTe_->setFixedHeight(exprHeight_);
        }
        exprTe_->setPlainText(txt);

        if (modeGroup_->checkedId() == TableModeIndex) {
            loadTable();
        }
        else {
            loadGraph();
        }

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QGuiApplication::restoreOverrideCursor();
#endif
    }
}

void TriggerItemWidget::loadTable() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
    triggerTable_->clear();
    triggerTable_->setInfo(info_, dependency());

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::restoreOverrideCursor();
#endif
}

void TriggerItemWidget::loadGraph() {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif

    triggerGraph_->clear();
    triggerGraph_->setInfo(info_, dependency());

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::restoreOverrideCursor();
#endif
}

void TriggerItemWidget::clearContents() {
    InfoPanelItem::clear();
    exprTe_->clear();
    triggerTable_->clear();
    triggerGraph_->clear();

    if (!active_)
        triggerTable_->clearSelection();
}

void TriggerItemWidget::clearTriggers() {
    exprTe_->clear();
    triggerTable_->clear();
    triggerGraph_->clear();
}

void TriggerItemWidget::rerender() {
    if (modeGroup_->checkedId() == TableModeIndex) {
        //
    }
    else {
        triggerGraph_->rerender();
    }
}

void TriggerItemWidget::updateState(const FlagSet<ChangeFlag>& flags) {
    if (flags.isSet(SuspendedChanged)) {
        // If we are here this item is active but not selected!

        // When it becomes suspended we need to clear everything since the
        // tree is probably cleared at this point
        if (suspended_) {
            clearTriggers();
        }
        // When we leave the suspended state we need to reload everything
        else {
            load();
        }
    }
    else if (flags.isSet(SelectedChanged)) {
        if (selected_ && active_) {
            if (!info_ || !info_->node()) {
                load();
            }
            else {
                rerender();
            }
        }
    }

    checkActionState();
}

void TriggerItemWidget::checkActionState() {
    if (suspended_) {
        dependTb_->setEnabled(false);
        return;
    }

    dependTb_->setEnabled(true);
}

void TriggerItemWidget::on_dependTb__toggled(bool b) {
    load();

    // when we activate the dependencies we always show the
    // dependency details as well
    if (b) {
        dependInfoTb_->setEnabled(true);
        if (dependInfoTb_->isChecked() == false) {
            dependInfoTb_->setChecked(true);
        }
    }
    else {
        dependInfoTb_->setChecked(false);
        dependInfoTb_->setEnabled(false);
    }
}

void TriggerItemWidget::on_dependInfoTb__toggled(bool b) {
    triggerTable_->slotShowDependencyInfo(b);
}

void TriggerItemWidget::slotHandleDefInfoWidgetClosure() {
    dependInfoTb_->setChecked(false);
}

void TriggerItemWidget::on_exprTb__toggled(bool b) {
    exprTe_->setVisible(b);
}

bool TriggerItemWidget::dependency() const {
    return dependTb_->isChecked();
}

void TriggerItemWidget::slotLinkSelected(VInfo_ptr info) {
    InfoPanelItem::linkSelected(info);
}

void TriggerItemWidget::slotInfoPanelCommand(VInfo_ptr info, QString cmd) {
    InfoPanelItem::relayInfoPanelCommand(info, cmd);
}

void TriggerItemWidget::slotDashboardCommand(VInfo_ptr info, QString cmd) {
    InfoPanelItem::relayDashboardCommand(info, cmd);
}

#if 0
void TriggerItemWidget::infoProgressStart(const std::string& text,int max)
{
    messageLabel_->showInfo(QString::fromStdString(text));
    messageLabel_->startProgress(max);
}

void TriggerItemWidget::infoProgressUpdate(const std::string& text,int value)
{
    messageLabel_->progress(QString::fromStdString(text),value);
}

#endif

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void TriggerItemWidget::slotChangeMode(QAbstractButton*, bool)
#else
void TriggerItemWidget::slotChangeMode(int, bool)
#endif
{
    modeStacked_->setCurrentIndex(modeGroup_->checkedId());
    showGraphButtons(modeGroup_->checkedId() == GraphModeIndex);

    if (modeGroup_->checkedId() == TableModeIndex) {
        if (triggerTable_->info() != info_ || triggerTable_->dependency() != dependency()) {
            triggerGraph_->becameInactive();
            loadTable();
        }
    }
    else if (modeGroup_->checkedId() == GraphModeIndex) {
        if (triggerGraph_->info() != info_ || triggerGraph_->dependency() != dependency()) {
            loadGraph();
        }
    }
}

void TriggerItemWidget::showGraphButtons(bool b) {
    dependInfoTb_->setVisible(!b);
    zoomLabel_->setVisible(b);
    zoomSlider_->setVisible(b);
}

void TriggerItemWidget::scanStarted() {
    messageLabel_->showInfo("Mapping trigger connections in the whole tree ...");
    messageLabel_->startProgress(100);
}

void TriggerItemWidget::scanFinished() {
    messageLabel_->stopProgress();
    messageLabel_->hide();
}

void TriggerItemWidget::scanProgressed(int value) {
    std::string text = "";
    messageLabel_->progress(QString::fromStdString(text), value);
}

void TriggerItemWidget::writeSettings(VComboSettings* vs) {
    vs->beginGroup("triggers");
    vs->putAsBool("dependency", dependency());
    vs->putAsBool("dependencyInfo", dependInfoTb_->isChecked());
    vs->putAsBool("expression", exprTb_->isChecked());
    vs->put("mode", modeGroup_->checkedId());

    triggerTable_->writeSettings(vs);
    triggerGraph_->writeSettings(vs);
    vs->endGroup();
}

void TriggerItemWidget::readSettings(VComboSettings* vs) {
    vs->beginGroup("triggers");

    dependTb_->setChecked(vs->getAsBool("dependency", dependency()));

// dependInfoTb_ is initialised by dependTb_ !!
#if 0
    if(dependTb_->isChecked())
    {
        dependInfoTb_->setChecked(vs->getAsBool("dependencyInfo",dependInfoTb_->isChecked()));
    }
#endif

    exprTb_->setChecked(vs->getAsBool("expression", exprTb_->isChecked()));

    int mode = vs->get<int>("mode", 0);
    if (mode == TableModeIndex) {
        tableTb_->setChecked(true);
    }
    else if (mode == GraphModeIndex) {
        graphTb_->setChecked(true);
    }

    triggerTable_->readSettings(vs);
    triggerGraph_->readSettings(vs);
    vs->endGroup();
}

//-------------------------
// Update
//-------------------------

void TriggerItemWidget::nodeChanged(const VNode* n, const std::vector<ecf::Aspect::Type>& aspect) {
    if (!info_ || !info_->isNode())
        return;

    // If the triggers are not scanned there must have been a major change and
    // we need to reload the item
    if (!info_->node()->root()->triggeredScanned()) {
        if (selected_ && active_) {
            load();
        }
        else {
            clearContents();
        }
        return;
    }

    // For certain changes we need to reload the triggers
    for (auto it : aspect) {
        if (it == ecf::Aspect::ADD_REMOVE_ATTR || it == ecf::Aspect::EXPR_TRIGGER) {
            if (selected_ && active_) {
                load();
            }
            else {
                clearContents();
            }
            return;
        }
    }

    // For other changes we only reload the triggers if the change happened to an item in the collected triggers
    //    for(auto it : aspect)
    //    {
    //        if(it == ecf::Aspect::NODE_VARIABLE || it == ecf::Aspect::METER || it == ecf::Aspect::LIMIT ||
    //                it == ecf::Aspect::EVENT)
    //        {
    //           if (modeGroup_->checkedId() ==  TableModeIndex) {
    //               if (triggerTable_->contains(n)) {
    //                   load();
    //                   return;
    //               }
    //           } else if (modeGroup_->checkedId() ==  TableModeIndex) {
    //               if (triggerGraph_->contains(n)) {
    //                   load();
    //                   return;
    //               }
    //           }
    //        }
    //    }

    // We do not track further  changes when the item is not selected
    if (!selected_ || !active_)
        return;

    // For the rest of the changes we rerender the collected items that might have changed
    if (modeGroup_->checkedId() == TableModeIndex)
        triggerTable_->nodeChanged(n, aspect);
    else if (modeGroup_->checkedId() == GraphModeIndex)
        triggerGraph_->nodeChanged(n, aspect);
}

static InfoPanelItemMaker<TriggerItemWidget> maker1("triggers");
