/*
 * 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 "NodeSearchWidget.hpp"

#include <QCloseEvent>
#include <QMessageBox>
#include <QPalette>
#include <QVBoxLayout>
#include <QtGlobal>

#include "ComboMulti.hpp"
#include "CustomListWidget.hpp"
#include "Highlighter.hpp"
#include "NodeQuery.hpp"
#include "NodeQueryEngine.hpp"
#include "NodeQueryHandler.hpp"
#include "NodeQueryResult.hpp"
#include "NodeQueryResultModel.hpp"
#include "ServerFilter.hpp"
#include "UiLog.hpp"
#include "VNState.hpp"
#include "ViewerUtil.hpp"

#define _UI_NODESEARCHWIDGET_DEBUG

//======================================================
//
// NodeQueryWidget
//
//======================================================

NodeSearchWidget::NodeSearchWidget(QWidget* parent) : QWidget(parent) {
    setupUi(this);

    //--------------------
    // Show/hide
    //--------------------

    // Show hide def panel
    connect(defPanelTb_, SIGNAL(toggled(bool)), this, SLOT(slotShowDefPanel(bool)));

    editor_->show();

    // connect(editor_,SIGNAL(queryEnabledChanged(bool)),
    //		this,SLOT(slotQueryEnabledChanged(bool)));

    // Show hide query
    connect(queryPanelTb_, SIGNAL(toggled(bool)), this, SLOT(slotShowQueryPanel(bool)));

    QFont showf;
    showf.setBold(true);
    showf.setPointSize(showf.pointSize() - 1);
    showLabel_->setFont(showf);
    showLabel_->setText("<font color=\'#565656\'>Show:</font>");

    // Find button
    findPb_->setProperty("startSearch", "1");
    QPalette pal = findPb_->palette();
    QColor col(230, 245, 253);
    pal.setColor(QPalette::Button, col);
    findPb_->setPalette(pal);

    connect(findPb_, SIGNAL(clicked()), this, SLOT(slotFind()));

    connect(stopPb_, SIGNAL(clicked()), this, SLOT(slotStop()));

    // Clear
    connect(clearPb_, SIGNAL(clicked()), editor_, SLOT(slotClear()));

    // Close button
    connect(closePb_, SIGNAL(clicked()), this, SLOT(slotClose()));

    //--------------------------------
    // Result tree/model
    //--------------------------------

    model_ = new NodeQueryResultModel(this);
    resTree_->setSourceModel(model_);

    connect(resTree_, SIGNAL(selectionChanged(VInfo_ptr)), this, SIGNAL(selectionChanged(VInfo_ptr)));

    connect(resTree_, SIGNAL(infoPanelCommand(VInfo_ptr, QString)), this, SIGNAL(infoPanelCommand(VInfo_ptr, QString)));

    //--------------------------------
    // Editor
    //--------------------------------

    connect(editor_, SIGNAL(rerunRequested()), this, SLOT(slotFind()));

    //--------------------------------
    // Query
    //--------------------------------

    query_ = new NodeQuery("tmp");

    //--------------------
    // Query engine
    //--------------------

    engine_ = new NodeQueryEngine(this);

    connect(engine_,
            SIGNAL(found(QList<NodeQueryResultTmp_ptr>)),
            model_->data(),
            SLOT(add(QList<NodeQueryResultTmp_ptr>)));

    connect(engine_, SIGNAL(found(NodeQueryResultTmp_ptr)), model_->data(), SLOT(add(NodeQueryResultTmp_ptr)));

    connect(engine_, SIGNAL(started()), this, SLOT(slotQueryStarted()));

    connect(engine_, SIGNAL(finished()), this, SLOT(slotQueryFinished()));

    //-------------------
    // Progress
    //-------------------

    queryProgress_->hide();

    stopPb_->setEnabled(false);
}

NodeSearchWidget::~NodeSearchWidget() {
    delete query_;
}

void NodeSearchWidget::setServerFilter(ServerFilter* f) {
    editor_->setServerFilter(f);
}

void NodeSearchWidget::setRootNode(VInfo_ptr info) {
    editor_->setRootNode(info);
}

void NodeSearchWidget::slotShowDefPanel(bool b) {
    editor_->showDefPanel(b);
}

void NodeSearchWidget::slotShowQueryPanel(bool b) {
    editor_->showQueryPanel(b);
}

void NodeSearchWidget::slotQueryEnabledChanged(bool /*queryEnabled*/) {
    // if(!engine_->isRunning())
    //{
#if 0
    UiLog().dbg() << "NodeSearchWidget::slotQueryEnabledChanged -->" <<  std::string((queryEnabled?"true":"false"));
    findPb_->setEnabled(queryEnabled);
#endif
    //}
}

void NodeSearchWidget::slotFind() {
#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << "NodeSearchWidget::slotFind -->";
#endif

    // Avoid double clicking
    if (!findPb_->isEnabled()) {
        UiLog().dbg() << "<-- NodeSearchWidget::slotFind - search is already running";
        return;
    }

#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << " isRunning=" << engine_->isRunning();
#endif

    adjustColumns();

    // Clear the results
    model_->clearData();

    assert(!engine_->isRunning());
    assert(findPb_->isEnabled());
    assert(!stopPb_->isEnabled());

    // We set the button state in advance as if the engine were running
    adjustButtonState(true);

    elapsed_.restart();
    if (!engine_->runQuery(editor_->query(), editor_->allServers())) {
        elapsed_.invalidate();
        // if we are here we could not start the query and we need to reset the button state
        adjustButtonState();
    }
#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << "<-- NodeSearchWidget::slotFind";
#endif
}

void NodeSearchWidget::slotStop() {
    // It is a blocking call!
    engine_->stopQuery();
    assert(!engine_->isRunning());
    adjustButtonState();
}

void NodeSearchWidget::slotClose() {
    slotStop();
    Q_EMIT closeClicked();
}

void NodeSearchWidget::slotQueryStarted() {
#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << "NodeSearchWidget::slotQueryStarted -->";
#endif
    adjustButtonState();

    queryProgress_->setRange(0, 0);
    queryProgress_->show();

    progressLabel_->setText("Search in progress ...");
#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << "<-- slotQueryStarted";
#endif
}

void NodeSearchWidget::slotQueryFinished() {
#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << "NodeSearchWidget::slotQueryFinished -->";
    UiLog().dbg() << "  Search finished. Total node scanned: " << engine_->scannedCount();
#endif

    adjustButtonState();

    queryProgress_->hide();
    queryProgress_->setRange(0, 1);
    queryProgress_->setValue(1);

    QString s = "<b>" + QString::number(model_->rowCount()) + "</b> items found in " +
                QString::number(elapsed_.elapsed() * 0.001, 'f', 1) + " s";

    elapsed_.invalidate();
    QColor col(90, 92, 92);
    if (engine_->wasMaxReached()) {
        s += " (stopped due to <b><u><font color=\'" + col.name() + "\'>maxnum reached!</font></u></b>)";
    }
    else if (engine_->wasStopped()) {
        s += " (query was <b><u><font color=\'" + col.name() + "\'>interrupted!</font></u></b>)";
    }
    progressLabel_->setText(s);

#ifdef _UI_NODESEARCHWIDGET_DEBUG
    UiLog().dbg() << " isRunning=" << engine_->isRunning();
    UiLog().dbg() << "<-- NodeSearchWidget::slotQueryFinished";
#endif
}

void NodeSearchWidget::adjustButtonState() {
    adjustButtonState(engine_->isRunning());
}

void NodeSearchWidget::adjustButtonState(bool engineRunning) {
    if (engineRunning) {
        findPb_->setEnabled(false);
        stopPb_->setEnabled(true);
        editor_->setEnabled(false);
    }
    else {
        findPb_->setEnabled(true);
        stopPb_->setEnabled(false);
        editor_->setEnabled(true);
    }

    UiLog().dbg() << "NodeSearchWidget::adjustButtonState -->";
    UiLog().dbg() << " findTb_: " << findPb_->isEnabled();
    UiLog().dbg() << "<-- adjustButtonState";
}

void NodeSearchWidget::adjustColumns() {
    if (!columnsAdjusted_) {
        columnsAdjusted_ = true;

        // We preset the column width. Setting it dynamically can be expensive
        // for a large number of rows (> 1M)
        QFont f;
        QFontMetrics fm(f);
        resTree_->setColumnWidth(0, ViewerUtil::textWidth(fm, "serverserverserse"));
        resTree_->setColumnWidth(1, ViewerUtil::textWidth(fm, "/suite/family1/family2/longtaskname1"));
        resTree_->setColumnWidth(2, ViewerUtil::textWidth(fm, "suspendedAA"));
        resTree_->setColumnWidth(3, ViewerUtil::textWidth(fm, "familyAA"));
        resTree_->setColumnWidth(4, ViewerUtil::textWidth(fm, "2017-Mar-07 15:45:56AA"));
    }
}

void NodeSearchWidget::writeSettings(QSettings& settings) {
    settings.setValue("defPanel", editor_->isDefPanelVisible());
    settings.setValue("queryPanel", editor_->isQueryPanelVisible());
    QStringList colW;
    for (int i = 0; i < resTree_->model()->columnCount() - 1; i++)
        colW << QString::number(resTree_->columnWidth(i));

    settings.setValue("resColumnWidth", colW);
}

void NodeSearchWidget::readSettings(const QSettings& settings) {
    if (settings.contains("defPanel")) {
        defPanelTb_->setChecked(settings.value("defPanel").toBool());
    }
    if (settings.contains("queryPanel")) {
        queryPanelTb_->setChecked(settings.value("queryPanel").toBool());
    }
    if (settings.contains("resColumnWidth")) {
        QStringList lst = settings.value("resColumnWidth").toStringList();
        for (int i = 0; i < lst.count(); i++)
            resTree_->setColumnWidth(i, lst[i].toInt());

        if (lst.count() >= 4)
            columnsAdjusted_ = true;
    }
}
