/***************************** 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 "FeatureStore.h"

#include <QtGlobal>
#include <QDebug>
#include <QDir>
#include <QFontMetrics>
#include <QFormLayout>
#include <QItemSelectionModel>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#include <QMenu>

#include "IconProvider.h"
#include "MvRequest.h"
#include "MvMiscellaneous.h"
#include "MvQFeatureItem.h"
#include "MvQFeatureCommand.h"
#include "MvQJson.h"
#include "MvQMethods.h"
#include "MvQTheme.h"
#include "MvQStreamOper.h"
#include "MvException.h"
#include "MvLog.h"
#include "WsDecoder.h"
#include "WsType.h"
#include "MvScanFileType.h"

#include "ui_FeatureStoreWidget.h"
#include "ui_FeatureStoreAddDialog.h"
#include "ui_FeatureStoreEditDialog.h"

const QSize FeatureStoreItem::pixSize_{48, 28};
const QString FeatureStoreItem::pixSuffix_{".png"};

FeatureStore* FeatureStore::instance_{nullptr};

//=================================================================
//
// FeatureStoreItem
//
//=================================================================

FeatureStoreItem::FeatureStoreItem(QString fileName, QString dirPath, bool local) :
    fileName_(fileName),
    dirPath_(dirPath),
    saved_(true),
    local_(local)
{
    loadJson();
    type_ = WsDecoder::getWsType(doc_);
    toolTip_ = WsDecoder::toolTip(doc_);
    geoLocked_ = WsDecoder::geoLocked(doc_);
    clearJson();

    auto pix = QPixmap(pixFilePath());
    setPixmap(pix);
}

FeatureStoreItem::FeatureStoreItem(QString fileName, MvQFeatureItemPtr fItem, QString dirPath, bool local) :
    type_(fItem->feature()),
    fileName_(fileName),
    toolTip_(fItem->toolTip()),
    dirPath_(dirPath),
    geoLocked_(fItem->isGeoLocked()),
    local_(local)
{
    // generate json
    QJsonObject d;
    fItem->toJson(d);
    doc_.setObject(d);

    // generate pixmap
    auto pix = fItem->toPixmap();
    setPixmap(pix);

    saveToDisk();
    clearJson();
}

FeatureStoreItem::~FeatureStoreItem()
{
    if (!saved_) {
        saveToDisk();
    }
}

FeatureStoreItemPtr FeatureStoreItem::make(QString fileName, QString dirPath, bool local)
{
    // NOTE: We should use make_shared() here. However, there seems to be no simple way to use a
    // non-public constructor with make_shared() in C++14.
    return FeatureStoreItemPtr(new FeatureStoreItem(fileName, dirPath, local));
}

FeatureStoreItemPtr FeatureStoreItem::make(QString fileName, MvQFeatureItemPtr fItem, QString dirPath, bool local)
{
    return FeatureStoreItemPtr(new FeatureStoreItem(fileName, fItem, dirPath, local));
}

void FeatureStoreItem::setPixmap(QPixmap p)
{
    pix_ = MvQ::fitPixmapToSize(pixSize_, p, MvQTheme::colour("uplot", "ws_store_bg"));
}

void FeatureStoreItem::addToSceneAtGeoCoord()
{
    loadJson();
    WsDecoder::setGeoLocked(doc_, geoLocked_);
    MvQFeatureHandler::Instance()->addFromJson(doc_);
    clearJson();
}

void FeatureStoreItem::addToSceneAtClick()
{
    loadJson();
    WsDecoder::setGeoLocked(doc_, false);
    MvQFeatureHandler::Instance()->registerToAddAtClick(doc_);
    clearJson();
}

void FeatureStoreItem::loadJson()
{
    if (doc_.isEmpty()) {
        QFile f(jsonFilePath());
        if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
            doc_ = QJsonDocument::fromJson(f.readAll());
        }
    }
}

void FeatureStoreItem::clearJson()
{
    if (saved_) {
        doc_ = {};
    }
}

void FeatureStoreItem::saveToDisk()
{
    // loadJson();
    if (!doc_.isEmpty()) {
        saved_ = false;
        QFile f(jsonFilePath());
        if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
            if (f.write(doc_.toJson()) > 0) {
                saved_ = true;

                //        if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
                //            if (f.write(doc_.toJson()) <= 0) {
                //                throw MvException("Could not save item into file " + f.fileName().toStdString() + "!");
                //            }
                //        } else {
                //             throw MvException("Cannot open file " + f.fileName().toStdString() +  "for writing!");
                //        }
            }
        }
        auto pix = pix_.scaledToHeight(64, Qt::SmoothTransformation);
        pix.save(pixFilePath());
    }
    clearJson();
}

void FeatureStoreItem::removeFromDisk()
{
    QFile::remove(jsonFilePath());
    QFile::remove(pixFilePath());
    saved_ = true;
}

void FeatureStoreItem::update(QString fileName, QString toolTip, bool geoLocked)
{
    bool contentChanged = (toolTip_ != toolTip || geoLocked != geoLocked_);
    if (contentChanged) {
        loadJson();
    }

    if (fileName_ != fileName) {
        QString err;
        if (FeatureStore::Instance()->checkName(fileName, err, local_) == FeatureStore::NameNoError) {
            auto oldJson = jsonFilePath();
            auto oldPix = pixFilePath();
            fileName_ = fileName;
            auto newJson = jsonFilePath();
            auto newPix = pixFilePath();
            QFile::rename(oldJson, newJson);
            QFile::rename(oldPix, newPix);
        }
        else {
            // the caller routine must check the validity of name!
            throw MvException(MV_FN_INFO +
                              "\n\nInternal error: cannot handle invalid item name=\"" + fileName.toStdString() +
                              "\"! " + err.toStdString());
        }
    }

    if (contentChanged) {
        if (toolTip_ != toolTip) {
            toolTip_ = toolTip;
            WsDecoder::setToolTip(doc_, toolTip_);
        }
        if (geoLocked != geoLocked_) {
            geoLocked_ = geoLocked;
            WsDecoder::setGeoLocked(doc_, geoLocked_);
        }
        saveToDisk();
    }
}

void FeatureStoreItem::setGeoLocked(bool b)
{
    if (b != geoLocked_) {
        geoLocked_ = b;
        loadJson();
        WsDecoder::setGeoLocked(doc_, geoLocked_);
        saveToDisk();
    }
}

QString FeatureStoreItem::jsonFilePath() const
{
    QDir dir(dirPath_);
    return dir.absoluteFilePath(fileName_);
}

QString FeatureStoreItem::pixFilePath() const
{
    QDir dir(dirPath_);
    return dir.absoluteFilePath(fileName_ + pixSuffix_);
}

// void FeatureStoreItem::makeLabel()
//{
//     return fileName_.left(fileName_.lastIndexOf("."));
// }

//=================================================================
//
// FeatureStore
//
//=================================================================

FeatureStore::FeatureStore() :
    localDir_(QString::fromStdString(metview::localFeatureItemsLibraryDir())),
    extraDir_(QString::fromStdString(metview::extraFeatureItemsLibraryDir()))
{
}

FeatureStore* FeatureStore::Instance()
{
    if (!instance_) {
        instance_ = new FeatureStore();
    }
    return instance_;
}

void FeatureStore::load()
{
    load(localDir_, true);
    load(extraDir_, false);
    broadcastInitEnd();
}

void FeatureStore::load(QString dirPath, bool local)
{
    // Search directory to get all the items
    if (dirPath.isEmpty()) {
        return;
    }

    QDir dir(dirPath);
    if (!dir.exists() && !dir.mkpath(dir.absolutePath())) {
        MvLog().warn() << "Cannot not create directory for weather symbol user library =" << dir.absolutePath();
        return;
    }

    broadcastChangeBegin();

    // Add items
    QStringList fileList = dir.entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Readable);
    for (const auto& fileName : fileList) {
        if (!fileName.endsWith(".png")) {
            QString cls = QString::fromStdString(ScanFileType(dir.absoluteFilePath(fileName).toStdString().c_str()));
            //            MvLog().dbg() << "fileName=" << fileName << " cls=" << cls;
            if (cls.startsWith("WS_")) {
                QString err;
                if (FeatureStore::Instance()->checkName(fileName, err, local) == FeatureStore::NameNoError) {
                    auto item = FeatureStoreItem::make(fileName, dirPath, local);
                    items_ << item;
                }
                else {
                    MvLog().warn() << "Cannot load weather symbol=" << fileName << " from user library = "
                                   << dir.absolutePath() << ".\n\n"
                                   << err;
                }
            }
        }
    }

    broadcastChangeEnd();
}

void FeatureStore::add(MvQFeatureItemPtr item)
{
    FeatureStoreAddDialog d(item);
    d.exec();
}

void FeatureStore::add(QString fileName, MvQFeatureItemPtr fItem)
{
    Q_ASSERT(fItem);

    auto oriItem = find(fileName, true);

    // create new item
    auto item = FeatureStoreItem::make(fileName, fItem, localDir_, true);
    Q_ASSERT(item);

    broadcastChangeBegin();
    if (oriItem) {
        items_.removeOne(oriItem);
    }
    items_ << item;
    broadcastChangeEnd();
}

FeatureStoreItemPtr FeatureStore::find(QString fileName, bool local) const
{
    for (auto v : items_) {
        if (v->fileName() == fileName && v->isLocal() == local) {
            return v;
        }
    }
    return nullptr;
}

void FeatureStore::edit(FeatureStoreItemPtr item)
{
    broadcastChangeBegin();
    FeatureStoreEditDialog d(item);
    d.exec();
    broadcastChangeEnd();
}

void FeatureStore::remove(FeatureStoreItemPtr item)
{
    if (item) {
        broadcastChangeBegin();
        item->removeFromDisk();
        items_.removeOne(item);
        broadcastChangeEnd();
    }
}

FeatureStore::NameErrorState FeatureStore::checkName(QString name, QString& err, bool local) const
{
    static const QString invalidCharacters{"/\\[]()*?~'\""};
    static const QString invalidStartCharacters{"#."};

    if (name.isEmpty()) {
        err = "Name cannot be empty!";
        return NameEmptyError;
    }
    else if (invalidStartCharacters.contains(name[0]) || invalidCharacters.contains(name[0])) {
        err = QString("Name=\'" + name +
                      R"(' starts with invalid character='%1'. The following characters are not allowed to use: %2)")
                  .arg(name[0])
                  .arg(invalidStartCharacters + invalidCharacters);
        return NameStartError;
    }
    else {
        for (auto c : invalidCharacters) {
            if (name.contains(c)) {
                err = "Invalid character in name=\'" + name +
                      "\' ! The following characters are not allowed to use: " +
                      invalidCharacters;
                return NameInvalidCharError;
            }
        }
        if (find(name, local)) {
            err = "Name=\'" + name + "\' already exists!";
            return NameDuplicateError;
        }
    }
    return NameNoError;
}

void FeatureStore::addObserver(FeatureStoreObserver* o)
{
    auto it = std::find(observers_.begin(), observers_.end(), o);
    if (it == observers_.end()) {
        observers_.push_back(o);
    }
}

void FeatureStore::removeObserver(FeatureStoreObserver* o)
{
    auto it = std::find(observers_.begin(), observers_.end(), o);
    if (it != observers_.end()) {
        observers_.erase(it);
    }
}

void FeatureStore::broadcast(ObsFunc proc)
{
    for (auto o : observers_)
        (o->*proc)();
}

void FeatureStore::broadcastChangeBegin()
{
    broadcast(&FeatureStoreObserver::notifyStoreChangeBegin);
}

void FeatureStore::broadcastChangeEnd()
{
    broadcast(&FeatureStoreObserver::notifyStoreChangeEnd);
}

void FeatureStore::broadcastInitEnd()
{
    broadcast(&FeatureStoreObserver::notifyStoreInitEnd);
}

//==================================================================
//
// FeatureStoreModel
//
//==================================================================

FeatureStoreModel::FeatureStoreModel(QObject* parent) :
    QAbstractItemModel(parent)
{
    FeatureStore::Instance()->addObserver(this);
    auto id = IconProvider::add(":/uPlot/geolock.svg", "geolock");
    lockPix_ = IconProvider::pixmap(id, 12);
    id = IconProvider::add(":/uPlot/geolock_empty.svg", "geolock_empty");
    emptyLockPix_ = IconProvider::pixmap(id, 12);
    typeColour_ = MvQTheme::colour("uplot", "ws_store_type_fg");
}

void FeatureStoreModel::notifyStoreChangeBegin()
{
    beginResetModel();
}

void FeatureStoreModel::notifyStoreChangeEnd()
{
    endResetModel();
}

void FeatureStoreModel::notifyStoreInitEnd()
{
    emit initFinished();
}

int FeatureStoreModel::columnCount(const QModelIndex& /*parent*/) const
{
    return 2;
}

int FeatureStoreModel::rowCount(const QModelIndex& index) const
{
    if (!index.isValid())
        return FeatureStore::Instance()->items().count();
    else
        return 0;
}

QVariant FeatureStoreModel::data(const QModelIndex& index, int role) const
{
    if (index.row() < 0 || index.row() >= FeatureStore::Instance()->items().count())
        return {};

    auto item = indexToItem(index);
    Q_ASSERT(item);

    if (index.column() == 0) {
        if (role == Qt::DecorationRole) {
            return item->isGeoLocked() ? lockPix_ : emptyLockPix_;
        }
    }
    else if (index.column() == 1) {
        switch (role) {
            case Qt::DisplayRole:
                return item->fileName() + " ";
            case Qt::DecorationRole:
                return item->pixmap();
            case Qt::ToolTipRole: {
                QString txt = MvQ::formatBoldText("Name: ") + item->fileName();
                if (item->type()) {
                    txt += "<br>" + MvQ::formatBoldText("Class: ") + item->type()->reqVerb() +
                           "<br>" + MvQ::formatBoldText("Type: ") + item->type()->name();
                }
                txt += "<br>" + MvQ::formatBoldText("Storage: ") +
                       (item->isLocal() ? "local" : "extra");
                if (!item->toolTip().isEmpty()) {
                    txt += "<br>" + MvQ::formatBoldText("Desc: ") + item->toolTip();
                }
                return txt;
            }
            default:
                break;
        }
    }

    return {};
}

QVariant FeatureStoreModel::headerData(int section, Qt::Orientation ori, int role) const
{
    if (ori != Qt::Horizontal) {
        return {};
    }

    if (role == Qt::DisplayRole) {
        switch (section) {
            case 0:
                return tr("GL");
            case 1:
                return tr("Item");
            default:
                return {};
        }
    }
    else if (role == Qt::ToolTipRole) {
        switch (section) {
            case 0:
                return tr("Geo-locked status");
            case 1:
                return tr("Item");
            default:
                return {};
        }
    }
    return {};
}

QModelIndex FeatureStoreModel::index(int row, int column, const QModelIndex& /*parent */) const
{
    return createIndex(row, column);
}

QModelIndex FeatureStoreModel::parent(const QModelIndex& /*index*/) const
{
    return {};
}

FeatureStoreItemPtr FeatureStoreModel::indexToItem(const QModelIndex& index) const
{
    if (!index.isValid() || index.column() < 0 || index.row() >= FeatureStore::Instance()->items().count())
        return nullptr;

    return FeatureStore::Instance()->items()[index.row()];
}

//======================================
//
// FeatureStoreFilterModel
//
//======================================

void FeatureStoreFilterModel::setFilterStr(QString t)
{
    QString newStr = t.simplified();
    if (newStr != filterStr_) {
        filterStr_ = newStr;
        invalidateFilter();
    }
}

bool FeatureStoreFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
    if (filterStr_.isEmpty())
        return true;

    QModelIndex idxName = sourceModel()->index(sourceRow, 2, sourceParent);
    return sourceModel()->data(idxName).toString().contains(filterStr_, Qt::CaseInsensitive);
}

//=================================================================
//
// FeatureStoreView
//
//=================================================================

void FeatureStoreView::contextMenuEvent(QContextMenuEvent* e)
{
    auto idx = indexAt(viewport()->mapFromGlobal(e->globalPos()));
    emit contextMenuRequested(idx);
    if (idx.isValid()) {
        QMenu::exec(actions(), e->globalPos());
    }
}

//=================================================================
//
// FeatureStoreWidget
//
//=================================================================

FeatureStoreWidget::FeatureStoreWidget(QWidget* parent) :
    QWidget(parent),
    ui_(new Ui::FeatureStoreWidget)
{
    ui_->setupUi(this);

    filterModel_ = new FeatureStoreFilterModel(this);
    model_ = new FeatureStoreModel(this);
    filterModel_->setSourceModel(model_);
    filterModel_->setDynamicSortFilter(true);

    ui_->view->setModel(filterModel_);
    ui_->view->sortByColumn(1, Qt::AscendingOrder);

#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
    ui_->filterLe->setClearButtonEnabled(true);
#endif

    auto sep = new QAction(this);
    sep->setSeparator(true);
    auto sep1 = new QAction(this);
    sep1->setSeparator(true);
    ui_->view->addAction(ui_->actionAddAtGeo);
    ui_->view->addAction(ui_->actionAddAtClick);
    ui_->view->addAction(sep);
    ui_->view->addAction(ui_->actionEdit);
    ui_->view->addAction(sep1);
    ui_->view->addAction(ui_->actionDelete);

    ui_->addGeoTb->setDefaultAction(ui_->actionAddAtGeo);
    ui_->addClickTb->setDefaultAction(ui_->actionAddAtClick);

    connect(ui_->view, SIGNAL(clicked(const QModelIndex&)),
            this, SLOT(slotClicked(const QModelIndex&)));

    connect(ui_->view, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(slotDoubleClicked(const QModelIndex&)));

    connect(ui_->filterLe, SIGNAL(textChanged(QString)),
            this, SLOT(slotFilter(QString)));

    connect(ui_->actionEdit, SIGNAL(triggered()),
            this, SLOT(editItem()));

    connect(ui_->actionAddAtGeo, SIGNAL(triggered()),
            this, SLOT(addToSceneAtGeo()));

    connect(ui_->actionAddAtClick, SIGNAL(triggered()),
            this, SLOT(addToSceneAtClick()));

    connect(ui_->actionDelete, SIGNAL(triggered()),
            this, SLOT(removeItem()));

    connect(ui_->view, SIGNAL(contextMenuRequested(QModelIndex)),
            this, SLOT(viewContextMenuRequested(QModelIndex)));

    connect(ui_->view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
            this, SLOT(selectionChanged(const QModelIndex&, const QModelIndex&)));

    ui_->actionAddAtGeo->setEnabled(false);
    ui_->actionAddAtClick->setEnabled(false);
}

void FeatureStoreWidget::slotClicked(const QModelIndex& idx)
{
    if (auto item = model_->indexToItem(filterModel_->mapToSource(idx))) {
        MvLog().dbg() << "idx=" << idx << " name" << item->fileName();
        if (idx.column() == 0) {
            item->setGeoLocked(!item->isGeoLocked());
            ui_->view->update(idx);
        }
        checkActionState(item);
    }
}

void FeatureStoreWidget::slotDoubleClicked(const QModelIndex& idx)
{
    if (auto item = model_->indexToItem(filterModel_->mapToSource(idx))) {
        FeatureStore::Instance()->edit(item);
    }
}

void FeatureStoreWidget::slotFilter(QString t)
{
    filterModel_->setFilterStr(t);
}

void FeatureStoreWidget::addToSceneAtGeo()
{
    if (auto item = currentItem()) {
        item->addToSceneAtGeoCoord();
    }
}

void FeatureStoreWidget::addToSceneAtClick()
{
    if (auto item = currentItem()) {
        item->addToSceneAtClick();
    }
}

void FeatureStoreWidget::editItem()
{
    if (auto item = currentItem()) {
        FeatureStore::Instance()->edit(item);
    }
}

void FeatureStoreWidget::removeItem()
{
    if (auto item = currentItem()) {
        if (QMessageBox::question(this,
                                  "Delete item",
                                  QString("Do you want to delete item \"%1\"").arg(item->fileName())) == QMessageBox::Yes) {
            FeatureStore::Instance()->remove(item);
        }
    }
}

FeatureStoreItemPtr FeatureStoreWidget::currentItem() const
{
    return model_->indexToItem(filterModel_->mapToSource(ui_->view->currentIndex()));
}

void FeatureStoreWidget::viewContextMenuRequested(const QModelIndex& idx)
{
    checkActionState(model_->indexToItem(filterModel_->mapToSource(idx)));
}

void FeatureStoreWidget::checkActionState(FeatureStoreItemPtr item)
{
    ui_->actionAddAtGeo->setEnabled(item != nullptr);
    ui_->actionAddAtClick->setEnabled(item && !item->isGeoLocked());
}

void FeatureStoreWidget::selectionChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
{
    checkActionState(model_->indexToItem(filterModel_->mapToSource(current)));
}

void FeatureStoreWidget::writeSettings(QSettings& settings)
{
    settings.beginGroup("FeatureStoreWidget");

    MvQ::saveTreeColumnWidth(settings, "treeColumnWidth", ui_->view);

    settings.endGroup();
}

void FeatureStoreWidget::readSettings(QSettings& settings)
{
    settings.beginGroup("FeatureStoreWidget");

    if (settings.contains("treeColumnWidth")) {
        MvQ::initTreeColumnWidth(settings, "treeColumnWidth", ui_->view);
        columnWidthInitialised_ = true;
    }

    settings.endGroup();
}

//==================================================================
//
// FeatureAddDialog
//
//==================================================================

FeatureStoreAddDialog::FeatureStoreAddDialog(MvQFeatureItemPtr item, QWidget* parent) :
    QDialog(parent),
    ui_(new Ui::FeatureStoreAddDialog),
    item_(item)
{
    ui_->setupUi(this);
    // setAttribute(Qt::WA_DeleteOnClose);
}

void FeatureStoreAddDialog::accept()
{
    Q_ASSERT(item_);
    auto name = ui_->nameLe_->text();
    //    auto desc = ui_->descLe_->text();

    QString err;
    auto errSt = FeatureStore::Instance()->checkName(name, err, true);
    if (errSt == FeatureStore::NameDuplicateError) {
        if (QMessageBox::question(this,
                                  "Save item",
                                  QString("A saved weather symbol with the specified name \'%1\' already exists. Do you want to overwrite it?").arg(name)) != QMessageBox::Yes) {
            return;
        }
    }
    else if (errSt != FeatureStore::NameNoError) {
        QMessageBox::critical(this, "Save weather symbol", err);
        return;
    }

    FeatureStore::Instance()->add(name, item_);
    item_ = nullptr;
    QDialog::accept();
}

//==================================================================
//
// FeatureEditDialog
//
//==================================================================

FeatureStoreEditDialog::FeatureStoreEditDialog(FeatureStoreItemPtr item, QWidget* parent) :
    QDialog(parent),
    ui_(new Ui::FeatureStoreEditDialog),
    item_(item)
{
    Q_ASSERT(item_);
    ui_->setupUi(this);
    ui_->typeLabel->setProperty("type", "title");
    ui_->pixLabel->setPixmap(item->pixmap());

    QString t;
    if (auto ws = item->type()) {
        auto subCol = MvQTheme::subText();
        auto bold = MvQTheme::boldFileInfoTitle();
        t = MvQ::formatText("Class: ", subCol, bold) + ws->reqVerb() +
            MvQ::formatText(" Type: ", subCol, bold) + ws->name();
    }
    ui_->typeLabel->setText(t);
    ui_->nameLe->setText(item->fileName());
    ui_->geoLockCb->setChecked(item->isGeoLocked());
    ui_->tooltipTe->setPlainText(item->toolTip());
}

void FeatureStoreEditDialog::accept()
{
    Q_ASSERT(item_);

    auto name = ui_->nameLe->text();
    auto tt = ui_->tooltipTe->toPlainText();
    auto glock = ui_->geoLockCb->isChecked();

    try {
        item_->update(name, tt, glock);
        item_ = nullptr;
        QDialog::accept();
    }
    catch (MvException& e) {
        if (e.what()) {
            QMessageBox::critical(this, "Edit properties", e.what());
        }
    }
}
