/***************************** LICENSE START ***********************************

 Copyright 2013 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQEditor.h"

#include <QtGlobal>
#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QCloseEvent>
#include <QDateTime>
#include <QDebug>
#include <QDialogButtonBox>
#include <QDrag>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPainter>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSettings>
#include <QStyle>
#include <QStyleOption>
#include <QStackedLayout>
#include <QStackedWidget>
#include <QToolBar>
#include <QToolButton>
#include <QVBoxLayout>


#include "EditorDrawerFactory.h"
#include "Folder.h"
#include "IconClass.h"
#include "MvLog.h"
#include "MvMiscellaneous.h"
#include "MvRequest.h"
#include "Path.h"
#include "Request.h"

#include "MvQEditorDrawerPanel.h"
#include "MvQFolderModel.h"
#include "MvQTemplateDrawer.h"
#include "MvQIconProvider.h"
#include "MvQStreamOper.h"
#include "MvQTheme.h"
#include "MvQMethods.h"
#include "MvQIconMimeData.h"
#include "DocHighlighter.h"
#include "MessageLabel.h"

//#define DEBUG_MV_MVQEDITOR__



class MvQEditorState : public QObject
{
public:
    MvQEditorState(MvQEditor* owner);
    ~MvQEditorState() override = default;

    virtual void handleChangeView(QAction*)=0;
    virtual bool handleSave()=0;
    virtual void handleOk()=0;
    virtual void handleClose();
    virtual void handleReset(){}

protected:
    void concludeOk();
    void concludeSave();
    void concludeReset();

    MvQEditor* owner_{nullptr};
};


class MvQEditorGuiState : public MvQEditorState
{
public:
    MvQEditorGuiState(MvQEditor* owner, bool doReset);
    void handleChangeView(QAction*) override;
    bool handleSave() override;
    void handleOk() override;
    void handleReset() override;
};

class MvQEditorTextState : public MvQEditorState
{
public:
    MvQEditorTextState(MvQEditor* owner);
    void handleChangeView(QAction*) override;
    bool handleSave() override;
    void handleOk() override;
    void handleReset() override;
private:
    bool saveFailed_{false};
};

class MvQEditorPythonState : public MvQEditorState
{
public:
    MvQEditorPythonState(MvQEditor* owner);
    void handleChangeView(QAction*) override;
    bool handleSave() override {return true;}
    void handleOk() override;
    void handleReset() override {}
};

//=========================================
//
// MvQEditorState
//
//=========================================

MvQEditorState::MvQEditorState(MvQEditor* owner): owner_(owner) {}

void MvQEditorState::handleClose()
{
    owner_->writeSettings();
    owner_->savePb_->setEnabled(false);
    owner_->resetPb_->setEnabled(false);
    owner_->close();
    owner_->notifyObserverClose();
    owner_->editDone();

    owner_->concludeReject();
}

void MvQEditorState::concludeOk()
{
    owner_->writeSettings();
    owner_->current_->modified();
    owner_->notifyObserverApply();

    owner_->savePb_->setEnabled(false);
    owner_->resetPb_->setEnabled(false);
    owner_->close();
    owner_->editDone();

    owner_->concludeAccept();
}

void MvQEditorState::concludeSave()
{
    owner_->current_->modified();
    owner_->notifyObserverApply();

    owner_->savePb_->setEnabled(false);
    owner_->resetPb_->setEnabled(false);
    owner_->viewAg_->setEnabled(true);
}

void MvQEditorState::concludeReset()
{
    owner_->savePb_->setEnabled(false);
    owner_->resetPb_->setEnabled(false);
    owner_->viewAg_->setEnabled(true);
}

//=========================================
//
// MvQEditorGuiState
//
//=========================================

MvQEditorGuiState::MvQEditorGuiState(MvQEditor* owner, bool doReset) : MvQEditorState(owner)
{
    owner_->viewSw_->setCurrentIndex(MvQEditor::LineViewMode);
    if (doReset) {
        owner_->ignoreChange_ = true;
        owner_->reset();
        owner_->ignoreChange_ = false;
    }
}

void MvQEditorGuiState::handleChangeView(QAction* ac)
{
    if (ac == owner_->guiAc_) {
        return;
    } else {
        handleSave();
        if (ac == owner_->textAc_) {
            owner_->transitionTo(new MvQEditorTextState(owner_));
        } else if (ac == owner_->pyAc_) {
            owner_->transitionTo(new MvQEditorPythonState(owner_));
        }
    }
}

void MvQEditorGuiState::handleOk()
{
    owner_->ignoreChange_ = true;
    owner_->apply();
    owner_->ignoreChange_ = false;
    concludeOk();
}

bool MvQEditorGuiState::handleSave()
{
    owner_->ignoreChange_ = true;
    owner_->apply();
    owner_->ignoreChange_ = false;
    concludeSave();
    return true;
}

void MvQEditorGuiState::handleReset()
{
    owner_->ignoreChange_ = true;
    owner_->reset();
    owner_->ignoreChange_ = false;
    concludeReset();
}

//=========================================
//
// MvQEditorTextState
//
//=========================================

MvQEditorTextState::MvQEditorTextState(MvQEditor* owner) : MvQEditorState(owner)
{
    owner_->viewSw_->setCurrentIndex(MvQEditor::TextViewMode);
    owner_->ignoreChange_ = true;
    owner_->textPanel_->load();
    owner_->ignoreChange_ = false;
}

void MvQEditorTextState::handleChangeView(QAction* ac)
{
    if (ac ==  owner_->textAc_) {
        if (saveFailed_ == true) {
            owner_->viewAg_->setEnabled(false);
        }
        return;
    } else {
        saveFailed_ = !handleSave();
        // save can fail since the text is editable and can contain an ill-formatted request
        if (!saveFailed_) {
            owner_->viewAg_->setEnabled(true);
            if (ac == owner_->guiAc_) {
                owner_->transitionTo(new MvQEditorGuiState(owner_, true));
            } else if (ac == owner_->pyAc_) {
                owner_->transitionTo(new MvQEditorPythonState(owner_));
            }
        } else {
            // we are not allowing to change the view mode if the request is
            // not properly formatted
            owner_->textAc_->setChecked(true);
        }
    }
}

void MvQEditorTextState::handleOk()
{
    if (owner_->textPanel_->check()) {
        owner_->textPanel_->save();
    }
    concludeOk();
}

bool MvQEditorTextState::handleSave()
{
    if (owner_->textPanel_->check()) {
        owner_->textPanel_->save();
    } else {
        return false;
    }
    concludeSave();
    return true;
}

void MvQEditorTextState::handleReset()
{
    owner_->textPanel_->load();
    concludeReset();
}

//=========================================
//
// MvQEditorPythonState
//
//=========================================

MvQEditorPythonState::MvQEditorPythonState(MvQEditor* owner) : MvQEditorState(owner)
{
    owner_->viewSw_->setCurrentIndex(MvQEditor::PythonViewMode);
    owner_->ignoreChange_ = true;
    owner_->pyPanel_->load();
    owner_->ignoreChange_ = false;
}

void MvQEditorPythonState::handleChangeView(QAction* ac)
{
    if (ac == owner_->guiAc_) {
        owner_->transitionTo(new MvQEditorGuiState(owner_, false));
    } else if (ac == owner_->textAc_) {
        owner_->transitionTo(new MvQEditorTextState(owner_));
    }
}

void MvQEditorPythonState::handleOk()
{
    concludeOk();
}

//===========================
//
// MvQEditorDragLabel
//
//===========================

MvQEditorDragLabel::MvQEditorDragLabel(QPixmap pix, int maxHeight, QWidget* parent) :
    QLabel(parent),
    oriPix_(pix)
{
    setPixmap(oriPix_);
    setFrameShape(QFrame::Box);
    setFrameShape(QFrame::StyledPanel);
    setObjectName(QString::fromUtf8("editorHeaderIcon"));
    setMaximumHeight(maxHeight);
    setMouseTracking(true);

    highlightPix_ = pix;
    QPainter painter(&highlightPix_);
    QPen pen = MvQTheme::pen("iconeditor", "drag_label_border");
    pen.setWidth(2);
    painter.setPen(pen);
    painter.drawRect(QRect(0, 0,
                           highlightPix_.width(), highlightPix_.height())
                         .adjusted(1, 1, -1, -1));
}

void MvQEditorDragLabel::setIconObject(IconObject* obj)
{
    obj_ = obj;
}

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
void MvQEditorDragLabel::enterEvent(QEnterEvent* event)
#else
void MvQEditorDragLabel::enterEvent(QEvent* event)
#endif
{
    setPixmap(highlightPix_);
    QLabel::enterEvent(event);
}

void MvQEditorDragLabel::leaveEvent(QEvent* event)
{
    setPixmap(oriPix_);
    QLabel::leaveEvent(event);
}

void MvQEditorDragLabel::mousePressEvent(QMouseEvent* event)
{
    if (!obj_) {
        QLabel::mousePressEvent(event);
        return;
    }

    canDrag_ = false;
    if (event->button() == Qt::LeftButton) {
        startPos_ = event->pos();
        canDrag_ = true;
    }

    QLabel::mousePressEvent(event);
}

void MvQEditorDragLabel::mouseMoveEvent(QMouseEvent* event)
{
    if (obj_ && canDrag_ && event->buttons() & Qt::LeftButton) {
        int distance = (event->pos() - startPos_).manhattanLength();
        if (distance >= QApplication::startDragDistance()) {
            canDrag_ = false;
            performDrag(Qt::CopyAction, event->pos());
        }
        return;
    }

    QLabel::mouseMoveEvent(event);
}

void MvQEditorDragLabel::performDrag(Qt::DropAction dropAction, QPoint scenePos)
{
    if (obj_) {
        QDrag* drag = buildDrag(obj_, scenePos);
        if (drag) {
            emit dragStarted();
            // We always initiate a copy action
            drag->exec(Qt::CopyAction, dropAction);
        }
        else {
            qDebug() << "MvQEditorDragLabel::performDrag -> Could not build drag object!!!";
        }
    }

    canDrag_ = false;
}

QDrag* MvQEditorDragLabel::buildDrag(IconObject* dragObj, QPoint /*dragPos*/)
{
    if (dragObj) {
        auto* drag = new QDrag(this);

        // Hotspot is the bottom right corner of the dragged object's pixmap
        // we overwrite it if dragPos is null!! We use normalised coordinates
        QPointF hotSpotInPix(1, 1);

        // It has to have as many elements as there are in objLst
        QList<QPoint> pixDistanceLst;
        // QPointF hotSpotInPix=0.;

        drag->setPixmap(oriPix_);

        QList<IconObject*> objLst;
        objLst << dragObj;

        // Create and set drag mime data
        auto* mimeData = new MvQIconMimeData(nullptr,
                                             dragObj, hotSpotInPix,
                                             objLst, pixDistanceLst);

        mimeData->setFromEditor(true);
        mimeData->setFromHelper(false);
        drag->setMimeData(mimeData);

        return drag;
    }

    return nullptr;
}

//================================================
//
// MvQEditorHeader
//
//================================================

MvQEditorHeader::MvQEditorHeader(MvQEditor* owner, QPixmap pix) :
    QWidget(owner),
    owner_(owner)
{
    auto* hb = new QHBoxLayout(this);
    hb->setSpacing(0);
    hb->setContentsMargins(0, 0, 0, 0);

    QFont font;
    font.setBold(true);
    QFontMetrics fm(font);
    int txth = 3 * fm.height() + 2 * fm.leading() + 8;
    txth = (txth > 32) ? txth : 32;

    // Pixmap label - can be dragged into a plot window
    pixLabel_ = new MvQEditorDragLabel(pix, txth, this);
    hb->addWidget(pixLabel_);

    connect(pixLabel_, SIGNAL(dragStarted()),
            owner_, SLOT(pixLabelDragStarted()));

    stackLayout_ = new QStackedLayout();
    hb->addLayout(stackLayout_);

    // Info panel
    auto* w = new QWidget(this);
    auto* labelHb = new QHBoxLayout(w);
    labelHb->setSpacing(0);
    labelHb->setContentsMargins(2, 1, 2, 1);
    stackLayout_->addWidget(w);

    label_ = new QLabel("", this);
    // label_->setObjectName(QString::fromUtf8("editorHeaderLabel"));
    label_->setProperty("type", "info");
    label_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    label_->setAutoFillBackground(true);
    label_->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
    label_->setFrameShape(QFrame::Box);
    label_->setFrameShape(QFrame::StyledPanel);
    label_->setMargin(2);
    label_->setMaximumHeight(txth);
    labelHb->addWidget(label_);

    renameTb_ = new QToolButton(this);
    renameTb_->setAutoRaise(true);
    // renameTb_->setText(tr("Rename"));
    renameTb_->setToolTip(tr("Click to rename icon"));
    renameTb_->setIcon(QPixmap(QString::fromUtf8(":/desktop/edit.svg")));
    labelHb->addWidget(renameTb_);

    connect(renameTb_, SIGNAL(clicked(bool)),
            this, SLOT(slotStartEdit(bool)));

    // Editor
    w = new QWidget(this);
    auto* editHb = new QHBoxLayout(w);
    editHb->setSpacing(0);
    editHb->setContentsMargins(2, 1, 2, 1);
    stackLayout_->addWidget(w);

    edit_ = new QLineEdit(this);
    editHb->addWidget(edit_);

    // Rename buttonBox_

    auto* acceptTb = new QToolButton(this);
    acceptTb->setAutoRaise(true);
    acceptTb->setIcon(QPixmap(QString::fromUtf8(":/desktop/accept.svg")));
    editHb->addWidget(acceptTb);

    auto* cancelTb = new QToolButton(this);
    cancelTb->setAutoRaise(true);
    cancelTb->setIcon(QPixmap(QString::fromUtf8(":/desktop/cancel.svg")));
    editHb->addWidget(cancelTb);

    connect(acceptTb, SIGNAL(clicked(bool)),
            this, SLOT(slotAcceptEdit(bool)));

    connect(cancelTb, SIGNAL(clicked(bool)),
            this, SLOT(slotCancelEdit(bool)));

    setProperty("editor", "true");
}

void MvQEditorHeader::slotStartEdit(bool)
{
    if (stackLayout_->currentIndex() == 0) {
        stackLayout_->setCurrentIndex(1);
        if (owner_->current())
            edit_->setText(QString::fromStdString(owner_->current()->name()));
        else
            edit_->clear();
    }
}

void MvQEditorHeader::slotAcceptEdit(bool)
{
    if (stackLayout_->currentIndex() == 1) {
        owner_->rename(edit_->text());
        stackLayout_->setCurrentIndex(0);
    }
}

void MvQEditorHeader::slotCancelEdit(bool)
{
    if (stackLayout_->currentIndex() == 1) {
        stackLayout_->setCurrentIndex(0);
        edit_->clear();
    }
}

void MvQEditorHeader::updateLabel(IconObject* obj)
{
    if (!obj)
        return;

    auto subCol = MvQTheme::subText();
    QString str = MvQ::formatText("Icon name: ", subCol) + QString::fromStdString(obj->name()) + "<br>";
    str += MvQ::formatText("Folder: ", subCol);
    str += QString::fromStdString(obj->parent()->fullName()) + "<br>";
    str += MvQ::formatText("Type: ", subCol);
    str += QString::fromStdString(obj->className());

#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
    QDateTime dt;
    dt.setSecsSinceEpoch(obj->path().lastModified());
#else
    QDateTime dt = QDateTime::fromTime_t(obj->path().lastModified());
#endif
    str += MvQ::formatText(" Modified: ", subCol);
    str += dt.toString("yyyy-MM-dd hh:mm");

    if (obj->locked())
        str += MvQ::formatText(" Status: ", subCol) +
               MvQ::formatText("Read only", Qt::red);

    label_->setText(str);
    pixLabel_->setIconObject(obj);

    lock(obj->locked());
}

void MvQEditorHeader::lock(bool b)
{
    renameTb_->setEnabled(!b);
}

void MvQEditorHeader::paintEvent(QPaintEvent*)
{
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

//================================================
//
// MvQEditorTextPanel
//
//================================================

bool MvQEditorTextPanel::showEditWarning_ = true;

MvQEditorTextPanel::MvQEditorTextPanel(const IconClass& c, Editor* owner, QWidget* parent) :
    QWidget(parent),
    class_(c),
    owner_(owner)
{
    // needed to initialise editWarning
    readSettings();

    auto* vb = new QVBoxLayout;
    setLayout(vb);

    vb->setSpacing(0);
    vb->setContentsMargins(0, 0, 0, 0);

    // labels
//    auto* labelLayout = new QHBoxLayout();
//    vb->addLayout(labelLayout);

    // Info label
    auto* iLayout = new QHBoxLayout();
    vb->addLayout(iLayout);
    infoLabel_ = new MessageLabel(this);
    infoLabel_->showInfo("This is the contents of the file storing your icon settings.");
    iLayout->addWidget(infoLabel_,1);
//    vb->addWidget(infoLabel_);

    // Warning label
    if (showEditWarning_) {
        warnLabel_ = new MessageLabel(this);
        warnLabel_->showWarning("Please only edit this text at <b> your own <b>risk</b>!");
        warnLabel_->showCustomButton("Do not show again");
        connect(warnLabel_, SIGNAL(customButtonClicked()),
                this, SLOT(doNotShowWarningAgain()));

        vb->addWidget(warnLabel_);
    }

    // Message label
    auto* msgLayout = new QHBoxLayout();
    vb->addLayout(msgLayout);

    msgLabel_ = new MessageLabel(this);
    msgLayout->addWidget(msgLabel_, 1);

    // Text editor
    te_ = new QPlainTextEdit(this);
    vb->addWidget(te_, 1);

    QFont f = MvQ::findMonospaceFont();
    f.setPointSize(te_->font().pointSize());
    te_->setFont(f);

    // The document becomes the owner of the highlighter
    new DocHighlighter(te_->document(), "request");

    connect(te_, SIGNAL(textChanged()),
            this, SLOT(slotTextChanged()));
}

void MvQEditorTextPanel::setObject(IconObject* obj)
{
    current_ = obj;

    if (obj->locked() || !obj->storedAsRequestFile()) {
        te_->setReadOnly(true);
    }
    else {
        te_->setReadOnly(false);
    }

    te_->update();
}


void MvQEditorTextPanel::loadRequestFile()
{
    beingLoaded_ = true;
    QFile f(QString::fromStdString(current_->path().str()));
    if (f.open(QFile::ReadOnly | QFile::Text)) {
        QTextStream rs(&f);
        te_->setPlainText(rs.readAll());
    }
    else {
        te_->clear();
    }
    beingLoaded_ = false;
}

bool MvQEditorTextPanel::check(bool fromSave)
{
    if (!current_ || !current_->storedAsRequestFile()) {
        correct_ = true;
        return correct_;
    }

    QString s = te_->toPlainText();
    Request r = Request::fromText(s.toStdString());
    QString verb;

#ifdef DEBUG_MV_MVQEDITOR__
    MvLog().dbg() << "TEXT --->";
    r.print();
    MvLog().dbg() << "<--- TEXT";
#endif

    if (r) {
        verb = QString(r.getVerb());
    }

    QString ic = QString::fromStdString(current_->editorClass().name());
    // QString ic=QString::fromStdString(current_->iconClass().name());

    QString msg;
    bool failed = false;
    if (verb.simplified().isEmpty()) {
        msg = "Ill formatted request!<br>You can either correct the request text or revert the "
               "icon back to the last saved state using <b>Reset</b>.";
        failed = true;
    } else if (verb.compare(ic, Qt::CaseInsensitive) != 0) {
        msg = "Request type=<b>" + verb + "</b> in the text does not match icon type=<b>" + ic + "</b>!<br> "
        "Please change the request type to <b>" + ic + "</b> or revert the icon back to "
        "the last saved state using <b>Reset</b>.";
        failed = true;
    }

    if (failed) {
        QString s;
        if (fromSave)
            s += "<u>Cannot save file!</u>";
        else
            s += "<u>Broken request!</u>";

        s += "<br>" + msg + "</td></tr></table>";
        msgLabel_->showError(s);
    }
    else {
        msgLabel_->clear();
        msgLabel_->hide();
    }

    correct_ = (!failed);

    return correct_;
}


void MvQEditorTextPanel::slotTextChanged()
{
    if (!beingLoaded_) {
        owner_->changed();
        msgLabel_->hide();
    }
}

void MvQEditorTextPanel::save()
{
    FILE* f = fopen(current_->path().str().c_str(), "w");
    if (f == nullptr) {
        // marslog(LOG_EROR|LOG_PERR,"%s",tmp);
        return;
    }
#ifdef DEBUG_MV_MVQEDITOR__
    qDebug() << te_->toPlainText() << current_->path().str().c_str();
#endif
    fprintf(f, "%s\n", te_->toPlainText().toStdString().c_str());
    fclose(f);
}

void MvQEditorTextPanel::load()
{
    loadRequestFile();
    check(false);
}

void MvQEditorTextPanel::doNotShowWarningAgain()
{
    showEditWarning_ = false;
    if (warnLabel_) {
        warnLabel_->hide();
        writeSettings();
    }
}

void MvQEditorTextPanel::readSettings()
{
    static bool alreadyRead = false;
    if (!alreadyRead) {
        QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MvQEditorTextPanel");
        showEditWarning_ = settings.value("showEditWarning", showEditWarning_).toBool();
        alreadyRead = true;
    }
}

void MvQEditorTextPanel::writeSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MvQEditorTextPanel");
    settings.clear();
    settings.setValue("showEditWarning", showEditWarning_);
}

//================================================
//
// MvQEditorPythonPanel
//
//================================================

MvQEditorPythonPanel::MvQEditorPythonPanel(const IconClass& c, Editor* owner, QWidget* parent) :
    QWidget(parent),
    class_(c),
    owner_(owner)
{
    auto* vb = new QVBoxLayout;
    setLayout(vb);

    vb->setSpacing(0);
    vb->setContentsMargins(0, 0, 0, 0);

    // Warning label
    auto* warnLayout = new QHBoxLayout();
    vb->addLayout(warnLayout);

    warnLabel_ = new MessageLabel(this);
    warnLabel_->showInfo("This is the (read-only) preview of the Python code generated from the icon.");
    warnLayout->addWidget(warnLabel_, 1);

    // Message label
    auto* msgLayout = new QHBoxLayout();
    vb->addLayout(msgLayout);

    msgLabel_ = new MessageLabel(this);
    msgLayout->addWidget(msgLabel_, 1);

    // Text editor
    te_ = new QPlainTextEdit(this);
    te_->setReadOnly(true);
    vb->addWidget(te_, 1);

    QFont f = MvQ::findMonospaceFont();
    f.setPointSize(te_->font().pointSize());
    te_->setFont(f);

    // The document becomes the owner of the highlighter
    new DocHighlighter(te_->document(), "python");
}

void MvQEditorPythonPanel::setObject(IconObject* obj)
{
    current_ = obj;
}

void MvQEditorPythonPanel::load()
{
    // create a temporary file
    Path temp(::marstmp());
    std::string outpath = temp.str();

    std::string lang = "python";
    std::string numSpaces = "4";
    std::string cmd = "$METVIEW_BIN/mvimportDesktop \"" + current_->fullName() + "\" " + outpath +
            " \"" + class_.name() + "\" \"" + numSpaces + "\" " + lang;

    system(cmd.c_str());

    // qDebug() << st;

    // insert text to the Macro editor
    QFile file(temp.str().c_str());
    bool start=true;
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QString t = file.readAll();
        QString r;
        for (auto row: t.split('\n')) {
             if (!row.startsWith("#")) {
                 // skip empty rows atop
                 if (start) {
                    if (row.simplified().isEmpty()) {
                        continue;
                    } else {
                        start = false;
                    }
                 }
                 r += row + "\n";
             }
        }
        te_->setPlainText(r);
    }

    // delete temporary file
    temp.remove();
}

//================================================
//
// MvQEditor
//
//================================================

MvQEditor::MvQEditor(const IconClass& name, const std::string& kind, QWidget* parent) :
    QDialog(parent),
    Editor(name, kind)
{
    // Set Window icon
    setWindowIcon(MvQIconProvider::pixmap(name, 24));

    auto* vb = new QVBoxLayout;
    vb->setSpacing(0);
    vb->setContentsMargins(0, 0, 0, 0);
    setLayout(vb);

    // Header
    headerPanel_ = new MvQEditorHeader(this, MvQIconProvider::pixmap(name, 32));
    vb->addWidget(headerPanel_);

    //-------------------
    // Toolbar
    //-------------------

    toolBarLayout_ = new QHBoxLayout();
    toolBarLayout_->setSpacing(0);
    toolBarLayout_->setContentsMargins(0, 0, 0, 0);

    // Widget to provide bg for the toolbars
    auto* w = new QWidget(this);
    w->setProperty("editor", "true");
    w->setLayout(toolBarLayout_);
    vb->addWidget(w);

    toolbarLeft_ = new QToolBar(this);
    toolbarLeft_->setIconSize({16,16});
    toolBarLayout_->addWidget(toolbarLeft_);

    toolbarRight_ = new QToolBar(w);
    toolbarRight_->setIconSize({16,16});
    toolBarLayout_->addWidget(toolbarRight_);

    auto* spacer = new QWidget(this);
    spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    sepAc_ = toolbarRight_->addWidget(spacer);

    toolBarLayout_->addWidget(toolbarRight_);
    toolBarLayout_->addWidget(toolbarRight_);

    auto modeLabel = new QLabel(this);
    modeLabel->setText(tr("View mode:"));
    toolbarRight_->insertWidget(nullptr, modeLabel);

    // Gui edit
    guiAc_ = new QAction(this);
    guiAc_->setIcon(QPixmap(":/desktop/gui_edit.svg"));
    guiAc_->setCheckable(true);
    guiAc_->setToolTip(tr("<b>GUI</b> view mode"));
    toolbarRight_->addAction(guiAc_);

    // Text edit
    textAc_ = new QAction(this);
    textAc_->setIcon(QPixmap(":/desktop/text_edit.svg"));
    textAc_->setCheckable(true);
    textAc_->setToolTip(tr("<b>Text</b> view mode"));
    toolbarRight_->addAction(textAc_);

    // Python
    pyAc_ = new QAction(this);
    pyAc_->setIcon(QPixmap(":/desktop/python.svg"));
    pyAc_->setCheckable(true);
    pyAc_->setToolTip(tr("<b>Python</b> view mode"));
    toolbarRight_->addAction(pyAc_);

    viewAg_ = new QActionGroup(this);
    viewAg_->setExclusive(true);
    viewAg_->addAction(guiAc_);
    viewAg_->addAction(textAc_);
    viewAg_->addAction(pyAc_);

    guiAc_->setChecked(true);

    connect(viewAg_, SIGNAL(triggered(QAction*)),
            this, SLOT(slotChangeView(QAction*)));


    toolbarRight_->addSeparator();

    // Documentation
    webAc_ = new QAction(this);
    webAc_->setIcon(QPixmap(":/desktop/web_link.svg"));
    webAc_->setToolTip(tr("Documentation"));
    toolbarRight_->addAction(webAc_);

    connect(webAc_, SIGNAL(triggered()),
            this, SLOT(slotWebDoc()));

    //---------------------
    // Central part
    //---------------------

    viewSw_ = new QStackedWidget(this);
    vb->addWidget(viewSw_, 1);

    // Panel layout
    w = new QWidget(this);
    centralLayout_ = new QVBoxLayout;
    centralLayout_->setSpacing(0);
    centralLayout_->setContentsMargins(1, 1, 1, 1);
    w->setLayout(centralLayout_);
    viewSw_->addWidget(w);

    // Text widget
    textPanel_ = new MvQEditorTextPanel(name, this, this);
    viewSw_->addWidget(textPanel_);

    // Python widget
    pyPanel_ = new MvQEditorPythonPanel(name, this, this);
    viewSw_->addWidget(pyPanel_);

    viewSw_->setCurrentIndex(0);

    //--------------------
    // Drawer
    //--------------------

    drawerPanel_ = new MvQEditorDrawerPanel(this);

    vb->addWidget(drawerPanel_);

    drawerPanel_->shrink();

    //--------------------
    // Buttonbox
    //--------------------

    buttonBox_ = new QDialogButtonBox(this);

    savePb_ = buttonBox_->addButton(QDialogButtonBox::Save);
    okPb_ = buttonBox_->addButton(QDialogButtonBox::Ok);
    cancelPb_ = buttonBox_->addButton(QDialogButtonBox::Cancel);
    resetPb_ = buttonBox_->addButton(QDialogButtonBox::Reset);

    savePb_->setDefault(true);
    savePb_->setEnabled(false);
    resetPb_->setEnabled(false);

    /*QCheckBox* stayOpenCb= new QCheckBox(tr("Stay open"),this);
    stayOpenCb->setChecked(false);
    buttonBox_->addButton(stayOpenCb,QDialogButtonBox::YesRole);*/

    // buttonBox_->addButton(QDialogButtonBox::Close);

    connect(buttonBox_, SIGNAL(clicked(QAbstractButton*)),
            this, SLOT(slotButtonClicked(QAbstractButton*)));

    vb->addWidget(buttonBox_);

    // to ignore ESC key
    installEventFilter(this);

    // initialise state
    transitionTo(new MvQEditorGuiState(this, false));
}


MvQEditor::~MvQEditor() = default;


bool MvQEditor::eventFilter(QObject* obj, QEvent* event)
{
    if (dynamic_cast<MvQEditor*>(obj) == this &&
        event->type() == QEvent::KeyPress && ((QKeyEvent*)event)->key() == Qt::Key_Escape) {
        return true;
    }
    return false;
}


void MvQEditor::setupLineFilter()
{
//    auto label = new QLabel(this);
//    label->setPixmap(QPixmap(":/desktop/filter_decor.svg"));
//    toolbarLeft_->insertWidget(nullptr, label);

    filterLe_ = new QLineEdit(this);
    filterLe_->setPlaceholderText("Filter ...");
#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    filterLe_->setClearButtonEnabled(true);
#endif

    toolbarLeft_->insertWidget(nullptr, filterLe_);

    filterCountLabel_ = new QLabel(this);
    filterCountLabel_->setProperty("filterCount", "1");
    toolbarLeft_->insertWidget(nullptr, filterCountLabel_);

    connect(filterLe_, SIGNAL(textChanged(QString)),
            this, SLOT(slotFilterItems(QString)));
}

void MvQEditor::updateFilterCountLabel()
{
    if (filterLe_) {
        if (filterLe_->text().isEmpty()) {
            filterCountLabel_->clear();
        }
        else {
            int cnt = filterNonMatchCount();
            if (cnt == 0) {
                filterCountLabel_->clear();
            }
            else {
                filterCountLabel_->setText("(" + QString::number(cnt) + " hidden)");
            }
        }
    }
}

void MvQEditor::setupShowDisabledActions()
{
    showDisabledAc_ = new QAction(this);
    showDisabledAc_->setText(tr("Show disabled parameters"));
    showDisabledAc_->setCheckable(true);
    showDisabledAc_->setChecked(false);
    showDisabledAc_->setIcon(QPixmap(":/desktop/show_disabled.svg"));
    toolbarRight_->addAction(showDisabledAc_);

    MvRequest r = MvApplication::getExpandedPreferences();
    if (const char* c = r("SHOW_DISABLED_PARAMETERS_IN_ICON_EDITORS")) {
        if (strcmp(c, "ON") == 0)
            showDisabledAc_->setChecked(true);
    }

    connect(showDisabledAc_, SIGNAL(triggered(bool)),
            this, SLOT(slotShowDisabled(bool)));
}

bool MvQEditor::showDisabled() const
{
    return (showDisabledAc_) ? showDisabledAc_->isChecked() : false;
}

void MvQEditor::showIt()
{
    show();

    // Mimimize the drawer
    drawerPanel_->shrink();
}

void MvQEditor::addToToolBar(QList<QAction*> lst)
{
    toolbarRight_->insertActions(sepAc_, lst);
}

// IconObserver method
void MvQEditor::iconChanged(IconObject* obj)
{
    if (obj == current_) {
        headerPanel_->updateLabel(current_);
        updateWindowTitle();
    }
}

void MvQEditor::changed()
{
    if (!temporary_ && !ignoreChange_) {
        savePb_->setEnabled(true);
        resetPb_->setEnabled(true);
//        viewAg_->setEnabled(false);
        updateFilterCountLabel();
    }

    // resetPb_->setEnabled(true);
}

void MvQEditor::edit()
{
    if (!current_)
        return;

    // Set default button status
    savePb_->setEnabled(false);
    resetPb_->setEnabled(false);

    // Populate drawers if needed
    if (!drawerFilled_) {
        std::map<std::string, EditorDrawer*> drawers = EditorDrawerFactory::create(this);
        for (auto& drawer : drawers) {
            drawerPanel_->addDrawer(drawer.second->widget(),
                                    drawer.second->name());
        }
        drawerFilled_ = true;
    }

    // Read the previous size
    readSettings();

    // Header
    headerPanel_->updateLabel(current_);

    // Title
    updateWindowTitle();

    // Text
    textPanel_->setObject(current_);
    pyPanel_->setObject(current_);

    // If request is broken we switch to text mode
    if (!current_->checkRequest()) {
        textPanel_->load();
        viewAg_->setEnabled(false);
        textAc_->setChecked(true);
        slotChangeView(textAc_);
        // textAc_->toggled();
    }
    // otherwise we start with gui mode
    else {
        viewAg_->setEnabled(true);
        guiAc_->setChecked(true);
        reset();
        slotChangeView(guiAc_);
        viewAg_->setEnabled(true);
    }

    savePb_->setEnabled(false);
    resetPb_->setEnabled(false);

    okPb_->setEnabled(!current_->locked());
}

//bool MvQEditor::isTextMode() const
//{
//    return (viewSw_->currentIndex() == TextViewMode);
//}

void MvQEditor::slotTextChanged()
{
    changed();
}

void MvQEditor::pixLabelDragStarted()
{
    state_->handleSave();
}

void MvQEditor::raiseIt()
{
    if (isMinimized())
        showNormal();

    raise();
}

void MvQEditor::closeIt()
{
    state_->handleClose();
}

void MvQEditor::rename(QString text)
{
    if (!current_)
        return;

    if (!text.isEmpty()) {
        auto n = text.toStdString();
        if (n != current_->name()) {
            current_->editor(nullptr);
            current_->rename(n);
            current_->editor(this);
        }
        headerPanel_->updateLabel(current_);
        updateWindowTitle();
    }
}

void MvQEditor::slotWebDoc()
{
    std::string err;
    std::string fnName = class_.name();
    metview::toLower(fnName);
    std::string url = "https://metview.readthedocs.io/en/latest/gen_files/icon_functions/" +
            fnName + ".html#id0";
    if (!metview::openInBrowser(url, err)) {
        QMessageBox::warning(this, "Icon editor - Metview",
                             "Failed to show documentation in web browser!<br><br>" + QString::fromStdString(err));
    }
}

void MvQEditor::slotChangeView(QAction* ac)
{
    state_->handleChangeView(ac);
}

void MvQEditor::slotButtonClicked(QAbstractButton* button)
{
    if (!button)
        return;

    if (buttonBox_->standardButton(button) == QDialogButtonBox::Cancel) {
        state_->handleClose();
    }
    else if (buttonBox_->standardButton(button) == QDialogButtonBox::Ok) {
        state_->handleOk();
    }
    else if (buttonBox_->standardButton(button) == QDialogButtonBox::Save) {
        state_->handleSave();
    }
    else if (buttonBox_->standardButton(button) == QDialogButtonBox::Reset) {
        state_->handleReset();
    }
}

void MvQEditor::concludeAccept()
{
    QDialog::accept();
}

void MvQEditor::concludeReject()
{
    QDialog::reject();
}

void MvQEditor::temporary()
{
    Editor::temporary();
    savePb_->setEnabled(false);
    viewAg_->setEnabled(false);
    headerPanel_->lock(true);
}

void MvQEditor::closeEvent(QCloseEvent* event)
{
    state_->handleClose();
    event->accept();
}

void MvQEditor::updateWindowTitle()
{
    QString txt = QString::fromStdString(current_->name());
    if (current_->parent())
        txt += " - " + QString::fromStdString(current_->parent()->fullName());

    txt += " - Metview";
    setWindowTitle(txt);
}

void MvQEditor::writeSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MV4-Desktop-Editor-" + QString::fromStdString(class_.name()));

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

    writeSettings(settings);
}

void MvQEditor::readSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, "MV4-Desktop-Editor-" + QString::fromStdString(class_.name()));

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

    settings.endGroup();

    readSettings(settings);
}

void MvQEditor::transitionTo(MvQEditorState* state)
{
#ifdef UI_OUTPUTDIRWIDGET_DEBUG_
    UiLog().dbg() << UI_FN_INFO << "state=" << typeid(*state).name();
#endif
    if (state_ != nullptr) {
        state_->deleteLater();
    }
    state_ = state;
}
