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

#include <QItemSelectionModel>
#include <QSettings>
#include <QShowEvent>
#include <QStringListModel>
#include <QTimer>

#include "AttributeEditorFactory.hpp"
#include "CommandHandler.hpp"
#include "MainWindow.hpp"
#include "SessionHandler.hpp"
#include "TriggeredScanner.hpp"
#include "VAttribute.hpp"
#include "VAttributeType.hpp"
#include "VEventAttr.hpp"
#include "VNode.hpp"
#include "ecflow/node/Aspect.hpp"

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

    pathView_->addAction(actionLookUp_);
    pathView_->setSelectionMode(QAbstractItemView::ExtendedSelection);
    pathView_->setContextMenuPolicy(Qt::ActionsContextMenu);
}

EventEditor::EventEditor(VInfo_ptr info, QWidget* parent)
    : AttributeEditor(info, "event", parent),
      model_(nullptr),
      scanned_(false) {
    w_ = new EventEditorWidget(this);
    addForm(w_);

    VAttribute* a = info_->attribute();

    Q_ASSERT(a);
    Q_ASSERT(a->type());
    Q_ASSERT(a->type()->name() == "event");
    QStringList aData = a->data();

    if (aData.count() < 3)
        return;

    QString name = aData[1];
    oriVal_      = aData[2].toInt();

    w_->nameLabel_->setText(name);
    w_->statusSetRb_->setChecked(oriVal_ == 1);
    w_->statusClearRb_->setChecked(oriVal_ != 1);

    w_->messageLabel_->hide();
    w_->messageLabel_->setShowTypeTitle(false);

    connect(w_->actionLookUp_, SIGNAL(triggered()), this, SLOT(slotLookUp()));

    connect(
        w_->pathView_, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(slotDoubleClicked(const QModelIndex&)));

    connect(w_->statusSetRb_, SIGNAL(toggled(bool)), this, SLOT(slotStatusToggled(bool)));

    header_->setInfo(QString::fromStdString(info_->path()), "Event");

    checkButtonStatus();

    readSettings();

    // No reset button is allowed because we can perform irreversible changes!
    doNotUseReset();
}

EventEditor::~EventEditor() {
    writeSettings();
}

// This was the only way to guarantee that the trigger scanning is called
// after the dialogue appears on screen (showEvent did not work!)
void EventEditor::paintEvent(QPaintEvent* e) {
    QDialog::paintEvent(e);
    if (!scanned_) {
        QTimer::singleShot(0, this, SLOT(buildList()));
    }
}

// We only build the list when the dialogoue is already visible so that
// the progressbar could show the scanning process
void EventEditor::buildList() {
    if (!info_)
        return;

    VAttribute* a = info_->attribute();
    if (!a)
        return;

    if (!model_) {
        model_ = new QStringListModel(this);
        w_->pathView_->setModel(model_);
    }

    if (VNode* node = info_->node()) {
        auto* 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)));

        std::vector<std::string> v;
        node->triggeredByEvent(a->strName(), v, scanner);

        w_->messageLabel_->hide();

        // Update the model(=node list)
        QStringList lst;
        for (auto& s : v)
            lst << QString::fromStdString(s);

        setModelData(lst);

        delete scanner;

        scanned_ = true;
    }
}

void EventEditor::scanStarted() {
    w_->messageLabel_->showInfo("Scanning dependencies based on this event ...");
    w_->messageLabel_->startProgress(100);
}

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

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

void EventEditor::apply() {
    int intVal       = (w_->statusSetRb_->isChecked()) ? 1 : 0;
    std::string val  = (w_->statusSetRb_->isChecked()) ? "set" : "clear";
    std::string name = w_->nameLabel_->text().toStdString();

    std::vector<std::string> valCmd;
    VAttribute::buildAlterCommand(valCmd, "change", "event", name, val);

    if (oriVal_ != intVal) {
        CommandHandler::run(info_, valCmd);
    }
}

void EventEditor::resetValue() {
}

void EventEditor::slotStatusToggled(bool) {
    checkButtonStatus();
}

bool EventEditor::isValueChanged() {
    return (oriVal_ != (w_->statusSetRb_->isChecked() ? 1 : 0));
}

void EventEditor::nodeChanged(const std::vector<ecf::Aspect::Type>& aspect) {
    bool eventCh = (std::find(aspect.begin(), aspect.end(), ecf::Aspect::EVENT) != aspect.end());
    if (eventCh && info_) {
        VAttribute* a = info_->attribute();
        Q_ASSERT(a);

        QStringList aData = a->data();
        if (aData.count() < 3)
            return;

        oriVal_ = aData[2].toInt();
        w_->statusSetRb_->setChecked(oriVal_ == 1);

        // Update the model (=node list)
        // setModelData(lim->paths());
    }
}

void EventEditor::setModelData(QStringList lst) {
    Q_ASSERT(model_);

    bool hadData = (modelData_.isEmpty() == false);
    modelData_   = lst;
    model_->setStringList(modelData_);

    if (!modelData_.isEmpty()) {
        if (!hadData) {
            w_->pathView_->setCurrentIndex(model_->index(0, 0));
            w_->pathView_->setFocus(Qt::MouseFocusReason);
        }
    }
}
// Lookup in tree

void EventEditor::slotLookUp() {
    QModelIndex idx = w_->pathView_->currentIndex();
    lookup(idx);
}

void EventEditor::slotDoubleClicked(const QModelIndex& index) {
    lookup(index);
}

void EventEditor::lookup(const QModelIndex& idx) {
    if (!info_)
        return;

    Q_ASSERT(model_);

    std::string nodePath = model_->data(idx, Qt::DisplayRole).toString().toStdString();

    VInfo_ptr ni = VInfo::createFromPath(info_->server(), nodePath);
    if (ni) {
        MainWindow::lookUpInTree(ni);
    }
}

void EventEditor::writeSettings() {
    SessionItem* cs = SessionHandler::instance()->current();
    Q_ASSERT(cs);
    QSettings settings(QString::fromStdString(cs->qtSettingsFile("EventEditor")), QSettings::NativeFormat);

    // We have to clear it so that should not remember all the previous values
    settings.clear();

    settings.beginGroup("main");
    settings.setValue("size", size());
    settings.endGroup();
}

void EventEditor::readSettings() {
    SessionItem* cs = SessionHandler::instance()->current();
    Q_ASSERT(cs);
    QSettings settings(QString::fromStdString(cs->qtSettingsFile("EventEditor")), QSettings::NativeFormat);

    settings.beginGroup("main");
    if (settings.contains("size")) {
        resize(settings.value("size").toSize());
    }
    else {
        resize(QSize(420, 400));
    }

    settings.endGroup();
}

static AttributeEditorMaker<EventEditor> makerStr("event");
