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

 Copyright 2012 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 <QtGlobal>
#include <QApplication>
#include <QAction>
#include <QButtonGroup>
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDebug>
#include <QFile>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QSettings>
#include <QSpinBox>
#include <QSplitter>
#include <QStackedWidget>
#include <QStatusBar>
#include <QString>
#include <QStringList>
#include <QTabBar>
#include <QTextBrowser>
#include <QTextEdit>
#include <QToolButton>
#include <QVBoxLayout>

#include "GribExaminer.h"

#include "GribMetaData.h"
#include "MvKeyProfile.h"

#include "DocHighlighter.h"
#include "MvQAbout.h"
#include "MvQArrowSpinWidget.h"
#include "MvQFileInfoLabel.h"
#include "MvQFileListWidget.h"
#include "MvQGribDumpModel.h"
#include "MvQKeyProfileModel.h"
#include "MvQKeyProfileTree.h"
#include "MvQLogPanel.h"
#include "MvQMethods.h"
#include "MvQPanel.h"
#include "MvQTheme.h"
#include "MvQTreeView.h"
#include "MvQTreeExpandState.h"

#include "MessageControlPanel.h"
#include "MessageLabel.h"
#include "PlainTextWidget.h"
#include "StatusMsgHandler.h"

//===========================================================
//
// MvQGribMainPanel
//
//===========================================================

MvQGribMainPanel::MvQGribMainPanel(QWidget* parent) :
    QWidget(parent),
    data_(nullptr)
{
    messagePanel_ = new MvQMessagePanel(this);
    dumpPanel_ = new MvQGribDumpPanel(this);
    gotoPanel_ = new MessageControlPanel(false, false, this);

    auto* dataLayout = new QVBoxLayout(this);
    dataLayout->setContentsMargins(0, 0, 0, 0);
    dataLayout->setSpacing(0);
    dataLayout->addWidget(gotoPanel_);

    dataSplitter_ = new QSplitter(this);
    dataSplitter_->setOrientation(Qt::Horizontal);
    dataSplitter_->setOpaqueResize(false);
    dataLayout->addWidget(dataSplitter_, 1);

    // dataSplitter_->addWidget(filterPanel_);
    dataSplitter_->addWidget(messagePanel_);
    dataSplitter_->addWidget(dumpPanel_);

    connect(messagePanel_, SIGNAL(keyProfileChanged()),
            this, SIGNAL(keyProfileChanged()));

    connect(messagePanel_, SIGNAL(messageSelected(int)),
            this, SLOT(messageSelected(int)));

    connect(messagePanel_, SIGNAL(messageNumDetermined()),
            this, SIGNAL(messageNumDetermined()));

    connect(gotoPanel_, SIGNAL(messageChanged(int)),
            this, SLOT(messageChangedInGoto(int)));
}

void MvQGribMainPanel::init(GribMetaData* data)
{
    Q_ASSERT(!data_);
    data_ = data;

    gotoPanel_->resetMessageNum(0, false);

    messagePanel_->init(data_);
    dumpPanel_->init(data_);
}

void MvQGribMainPanel::clear()
{
}

MvKeyProfile* MvQGribMainPanel::keyProfile() const
{
    return messagePanel_->keyProfile();
}

void MvQGribMainPanel::loadKeyProfile(MvKeyProfile* prof)
{
    messagePanel_->loadKeyProfile(prof);
}


void MvQGribMainPanel::reloadData()
{
    gotoPanel_->resetMessageNum(0, false);
    messagePanel_->adjustProfile(true, -1);
}

void MvQGribMainPanel::messageChangedInGoto(int value)
{
    QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
    messagePanel_->selectMessage(value - 1);
    QApplication::restoreOverrideCursor();
}

// msgCnt: starts at 0
void MvQGribMainPanel::messageSelected(int msgCnt)
{
    emit newMessageSelected(msgCnt);

    // The message counter might not have been initialised. But at this point
    // we must know the correct message num so we can set the counter.
    if (gotoPanel_->messageValue() == 0)
        gotoPanel_->resetMessageNum(data_->messageNum(), false);

    // Set the message counter
    gotoPanel_->setMessageValue(msgCnt + 1, false);

    QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
    dumpPanel_->loadDumps(msgCnt);
    QApplication::restoreOverrideCursor();
}

void MvQGribMainPanel::writeSettings(QSettings& settings)
{
    settings.beginGroup("mainPanel");
    settings.setValue("dataSplitter", dataSplitter_->saveState());

    dumpPanel_->writeSettings(settings);
    settings.endGroup();
}

void MvQGribMainPanel::readSettings(QSettings& settings)
{
    settings.beginGroup("mainPanel");
    if (settings.contains("dataSplitter")) {
        dataSplitter_->restoreState(settings.value("dataSplitter").toByteArray());
    }

    dumpPanel_->readSettings(settings);
    settings.endGroup();
}


//===========================================================
//
// MvQGribDumpPanel
//
//===========================================================

MvQGribDumpPanel::MvQGribDumpPanel(QWidget* parent) :
    QWidget(parent),
    currentMsg_(-1),
    data_(nullptr)
{
    gribNameSpace_ << "Default"
                   << "geography"
                   << "ls"
                   << "mars"
                   << "parameter"
                   << "statistics"
                   << "time"
                   << "vertical";

    QWidget* w = nullptr;

    auto* dumpLayout = new QVBoxLayout(this);
    dumpLayout->setContentsMargins(0, 0, 0, 0);

    //---------------------------------
    // Tab widget for dump modes
    //---------------------------------

    dumpTab_ = new QTabWidget(this);
    dumpLayout->addWidget(dumpTab_);

    //---------------------------------
    // Namespace dump
    //--------------------------------

    // Layout
    w = new QWidget(this);
    auto* mvDumpLayout = new QVBoxLayout(w);
    mvDumpLayout->setContentsMargins(0, 2, 0, 0);
    mvDumpLayout->setSpacing(0);
    dumpTab_->addTab(w, tr("Namespaces"));
    Q_ASSERT(static_cast<int>(NamespaceTabIndex) == dumpTab_->count() - 1);
    dumpTab_->tabBar()->setTabData(dumpTab_->count() - 1, "namespace");

    //    auto wp = new MvQPanel(this);
    auto wp = new QWidget(this);
    auto* nsComboLayout = new QHBoxLayout(wp);
    nsComboLayout->setContentsMargins(2, 2, 0, 2);

    auto* nsLabel = new QLabel(tr(" ecCodes namespace:"), wp);
    // nsLabel->setProperty("panelStyle", "2");
    nsCombo_ = new QComboBox(this);
    nsLabel->setBuddy(nsCombo_);

    nsComboLayout->addWidget(nsLabel);
    nsComboLayout->addWidget(nsCombo_);
    nsComboLayout->addStretch(1);

    nsCombo_->addItems(gribNameSpace_);
    nsCombo_->setCurrentIndex(-1);
    mvDumpLayout->addWidget(wp);

    // filter
    mvDumpFilterHolder_ = new QWidget(this);
    auto filterHb = new QHBoxLayout(mvDumpFilterHolder_);
    filterHb->setContentsMargins(0, 0, 0, 0);
    filterHb->setSpacing(6);
    auto filterLabel = new QLabel(this);
    filterLabel->setPixmap(QPixmap(":examiner/filter.svg"));

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

    connect(mvDumpFilter_, SIGNAL(textChanged(QString)),
            this, SLOT(slotMvDumpFilter(QString)));

    filterHb->addWidget(filterLabel);
    filterHb->addWidget(mvDumpFilter_);
    mvDumpLayout->addWidget(mvDumpFilterHolder_);
    mvDumpLayout->addSpacing(3);

    // tree
    mvDumpModel_ = new MvQGribMvDumpModel(this);
    mvDumpSortModel_ = new QSortFilterProxyModel(this);
    mvDumpSortModel_->setDynamicSortFilter(true);
    mvDumpSortModel_->setFilterRole(Qt::UserRole);
    mvDumpSortModel_->setFilterFixedString("1");
    mvDumpSortModel_->setFilterKeyColumn(0);
    mvDumpSortModel_->setSourceModel(mvDumpModel_);

    mvDumpTree_ = new MvQTreeView(this, true);
    mvDumpTree_->setObjectName("mvDumpTree");
    mvDumpTree_->setSortingEnabled(true);
    mvDumpTree_->sortByColumn(0, Qt::AscendingOrder);
    mvDumpTree_->setAlternatingRowColors(true);
    mvDumpTree_->setAllColumnsShowFocus(true);
    mvDumpTree_->setModel(mvDumpSortModel_);
    mvDumpTree_->setRootIsDecorated(false);

    mvDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);

    mvDumpLayout->addWidget(mvDumpTree_, 1);

    //---------------------------------
    // Std dump
    //--------------------------------

    w = new QWidget(this);
    auto* stdDumpLayout = new QVBoxLayout(w);
    stdDumpLayout->setContentsMargins(0, 2, 0, 0);
    stdDumpLayout->setSpacing(0);
    dumpTab_->addTab(w, tr("Standard dump"));
    Q_ASSERT(static_cast<int>(StdTabIndex) == dumpTab_->count() - 1);
    dumpTab_->tabBar()->setTabData(dumpTab_->count() - 1, "std");

    // wp = new MvQPanel(this);
    wp = new QWidget(this);
    auto* stdControlLayout = new QHBoxLayout(wp);
    stdControlLayout->setContentsMargins(2, 2, 0, 2);
    stdDumpLayout->addWidget(wp);

    auto* stdTreeTb = new QToolButton(this);
    stdTreeTb->setCheckable(true);
    stdTreeTb->setText(tr("Tree"));

    auto* stdTextTb = new QToolButton(this);
    stdTextTb->setCheckable(true);
    stdTextTb->setText(tr("Text"));

    stdGroup_ = new QButtonGroup(this);
    stdGroup_->addButton(stdTreeTb, 0);
    stdGroup_->addButton(stdTextTb, 1);

    stdControlLayout->addWidget(stdTreeTb);
    stdControlLayout->addWidget(stdTextTb);
    stdControlLayout->addStretch(1);

    // Stacked
    stdDumpStacked_ = new QStackedWidget(this);
    stdDumpLayout->addWidget(stdDumpStacked_);

    stdDumpTree_ = new MvQTreeView(this, true);
    stdDumpModel_ = new MvQGribStdDumpModel(this);
    stdDumpTree_->setModel(stdDumpModel_);
    stdDumpTree_->setAlternatingRowColors(true);
    stdDumpTree_->setAllColumnsShowFocus(true);
    stdDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);
    stdDumpStacked_->addWidget(stdDumpTree_);

    stdDumpBrowser_ = new PlainTextWidget(this);
    stdDumpBrowser_->editor()->setReadOnly(true);
    stdDumpStacked_->addWidget(stdDumpBrowser_);

    // The document becomes the owner of the highlighter
    new DocHighlighter(stdDumpBrowser_->editor()->document(), "gribStdDump");

    connect(stdGroup_, SIGNAL(buttonClicked(QAbstractButton*)),
            this, SLOT(slotStdGroupClicked(QAbstractButton*)));

    stdExpand_ = new MvQTreeExpandState(stdDumpTree_);

    //---------------------------------
    // Wmo dump
    //--------------------------------

    w = new QWidget(this);
    auto* wmoDumpLayout = new QVBoxLayout(w);
    wmoDumpLayout->setContentsMargins(0, 2, 0, 0);
    wmoDumpLayout->setSpacing(0);
    dumpTab_->addTab(w, tr("Sections"));
    Q_ASSERT(static_cast<int>(WmoTabIndex) == dumpTab_->count() - 1);
    dumpTab_->tabBar()->setTabData(dumpTab_->count() - 1, "wmo");

    // wp = new MvQPanel(this);
    wp = new QWidget(this);
    auto* wmoControlLayout = new QHBoxLayout(wp);
    wmoControlLayout->setContentsMargins(2, 2, 0, 2);
    wmoDumpLayout->addWidget(wp);

    auto* wmoTreeTb = new QToolButton(this);
    wmoTreeTb->setCheckable(true);
    wmoTreeTb->setText(tr("Tree"));

    auto* wmoTextTb = new QToolButton(this);
    wmoTextTb->setCheckable(true);
    wmoTextTb->setText(tr("Text"));

    wmoGroup_ = new QButtonGroup(this);
    wmoGroup_->addButton(wmoTreeTb, 0);
    wmoGroup_->addButton(wmoTextTb, 1);

    wmoControlLayout->addWidget(wmoTreeTb);
    wmoControlLayout->addWidget(wmoTextTb);
    wmoControlLayout->addStretch(1);

    // Stacked
    wmoDumpStacked_ = new QStackedWidget(this);
    wmoDumpLayout->addWidget(wmoDumpStacked_);

    wmoDumpTree_ = new MvQTreeView(this, true);
    wmoDumpModel_ = new MvQGribWmoDumpModel(this);
    wmoDumpTree_->setModel(wmoDumpModel_);
    wmoDumpTree_->setObjectName("wmoDumpTree");
    wmoDumpTree_->setAlternatingRowColors(true);
    wmoDumpTree_->setAllColumnsShowFocus(true);
    wmoDumpTree_->setDragDropMode(QAbstractItemView::DragOnly);
    wmoDumpTree_->setColumnToDrag(1);

    wmoDumpStacked_->addWidget(wmoDumpTree_);

    wmoDumpBrowser_ = new PlainTextWidget(this);
    wmoDumpBrowser_->editor()->setReadOnly(true);
    wmoDumpStacked_->addWidget(wmoDumpBrowser_);

    // The document becomes the owner of the highlighter
    new DocHighlighter(wmoDumpBrowser_->editor()->document(), "gribWmoDump");

    connect(wmoGroup_, SIGNAL(buttonClicked(QAbstractButton*)),
            this, SLOT(slotWmoGroupClicked(QAbstractButton*)));

    wmoExpand_ = new MvQTreeExpandState(wmoDumpTree_);

    //--------------------------------
    // Table dump
    //--------------------------------

    w = new QWidget;
    auto vb = new QVBoxLayout(w);
    vb->setContentsMargins(0, 0, 0, 0);
    vb->setSpacing(0);

    tableTe_ = new QTextEdit(this);
    tableTe_->setReadOnly(true);

    vb->addWidget(tableTe_);

    dumpTab_->addTab(w, tr("Tables"));
    Q_ASSERT(static_cast<int>(TableTabIndex) == dumpTab_->count() - 1);
    dumpTab_->tabBar()->setTabData(dumpTab_->count() - 1, "table");
    dumpTab_->tabBar()->setTabToolTip(dumpTab_->count() - 1, "Shows details of GRIB tables used to decode the current message");

    //---------------------------------
    // Value dump
    //--------------------------------

    w = new QWidget(this);
    auto* valueDumpLayout = new QVBoxLayout(w);
    valueDumpLayout->setContentsMargins(0, 2, 0, 0);
    valueDumpLayout->setSpacing(0);
    dumpTab_->addTab(w, tr("Values"));
    Q_ASSERT(static_cast<int>(ValueTabIndex) == dumpTab_->count() - 1);
    dumpTab_->tabBar()->setTabData(dumpTab_->count() - 1, "value");

    // wp = new MvQPanel(this);
    wp = new QWidget(this);
    auto* valueControlLayout = new QHBoxLayout(wp);
    valueControlLayout->setContentsMargins(2, 2, 0, 2);
    valueDumpLayout->addWidget(wp);

    // Spin box for index selection
    valueRowLabel_ = new QLabel(tr(" Go to row:"), wp);
    // spinLabel->setProperty("panelStyle", "2");
    valueRowSpin_ = new QSpinBox(this);
    valueRowLabel_->setBuddy(valueRowSpin_);
    // QHBoxLayout* valueSpinLayout = new QHBoxLayout;
    valueControlLayout->addWidget(valueRowLabel_);
    valueControlLayout->addWidget(valueRowSpin_);

    valueDumpLabel_ = new QLabel(wp);
    // valueDumpLabel_->setProperty("panelStyle", "2");
    valueControlLayout->addWidget(valueDumpLabel_);
    valueControlLayout->addStretch(1);

    valueDumpMessageLabel_ = new MessageLabel(this);
    valueDumpMessageLabel_->setShowTypeTitle(false);
    valueDumpLayout->addWidget(valueDumpMessageLabel_);
    valueDumpMessageLabel_->hide();

    // valueDumpLayout->addLayout(valueSpinLayout);

    // Signals and slots
    connect(valueRowSpin_, SIGNAL(valueChanged(int)),
            this, SLOT(slotValueRowSpinChanged(int)));

    valueDumpModel_ = new MvQGribValueDumpModel(this);
    valueDumpSortModel_ = new QSortFilterProxyModel;
    valueDumpSortModel_->setSourceModel(valueDumpModel_);
    valueDumpSortModel_->setDynamicSortFilter(true);
    valueDumpSortModel_->setSortRole(Qt::UserRole);

    valueDumpTree_ = new MvQTreeView(this, false);
    valueDumpTree_->setObjectName("valueDumpTree");
    valueDumpTree_->setSortingEnabled(true);
    valueDumpTree_->sortByColumn(0, Qt::AscendingOrder);
    valueDumpTree_->setAllColumnsShowFocus(true);
    valueDumpTree_->setAlternatingRowColors(true);
    valueDumpTree_->setModel(valueDumpSortModel_);
    valueDumpTree_->setRootIsDecorated(false);
    valueDumpTree_->setUniformRowHeights(true);
    valueDumpTree_->setActvatedByKeyNavigation(true);

    valueDumpLayout->addWidget(valueDumpTree_, 1);

    connect(valueDumpTree_, SIGNAL(clicked(QModelIndex)),
            this, SLOT(slotSelectValueRow(QModelIndex)));

    connect(valueDumpTree_, SIGNAL(activated(const QModelIndex&)),
            this, SLOT(slotSelectValueRow(const QModelIndex&)));

    // dumpStacked_->addWidget(w);


    //----------------------------------------
    // Init
    //----------------------------------------

    QFont f;
    QFontMetrics fm(f);

    connect(dumpTab_, SIGNAL(currentChanged(int)),
            this, SLOT(slotCurrentDumpChanged(int)));

    dumpTab_->setCurrentIndex(0);  // Default is namespace dump

    // Init ns combo
    nsCombo_->setCurrentIndex(0);

    connect(nsCombo_, SIGNAL(currentTextChanged(const QString&)),
            this, SLOT(slotSetGribNameSpace(const QString&)));

    nsCombo_->setCurrentIndex(2);  // Default is namesapce "ls"

    // Adjust ns tree columns
    mvDumpTree_->setColumnWidth(0, MvQ::textWidth(fm, "geography.latitudeOfFirstGridPoint"));
    mvDumpTree_->setColumnWidth(1, MvQ::textWidth(fm, "Key type   "));

    stdGroup_->button(0)->setChecked(true);

    // Adjust std tree columns
    stdDumpTree_->setColumnWidth(0, MvQ::textWidth(fm, "latitudeOfFirstGridPointIndegrees"));
    stdDumpTree_->setColumnWidth(1, MvQ::textWidth(fm, "-180.0000000"));

    wmoGroup_->button(0)->setChecked(true);

    // Adjust wmo tree columns
    wmoDumpTree_->setColumnWidth(0, MvQ::textWidth(fm, "Section 5  23-23 "));
    wmoDumpTree_->setColumnWidth(1, MvQ::textWidth(fm, "latitudeOfFirstGridPointIndegrees"));

    // Adjust value tree columns
    valueDumpTree_->setColumnWidth(0, MvQ::textWidth(fm, "10000000 "));
    valueDumpTree_->setColumnWidth(1, MvQ::textWidth(fm, "Latitude  "));
    valueDumpTree_->setColumnWidth(1, MvQ::textWidth(fm, "Longitude  "));
}

MvQGribDumpPanel::~MvQGribDumpPanel()
{
    delete stdExpand_;
    delete wmoExpand_;
}

void MvQGribDumpPanel::init(GribMetaData* data)
{
    data_ = data;
}

void MvQGribDumpPanel::loadDumps(int msgCnt)
{
    Q_ASSERT(data_);

    StatusMsgHandler::instance()->show("Message: " + QString::number(msgCnt + 1), true);

    // StatusMsgHandler::instance()->show("Load message: " + QString::number(msgCnt+1));

    currentMsg_ = msgCnt;

    // At this point the first message scan has finished so we know the real  message
    // number. It is not known at the beginning because for multi message fields
    // grib_count_in_file that we use to estimate the message number does not
    // count the sub messages. So we need to updeate the file info label now.

    if (fileInfoLabelNeedsUpdating_) {
        // updateFileInfoLabel();
        fileInfoLabelNeedsUpdating_ = false;
    }

    // Save expand states in the trees
    if (wmoDumpModel_->hasData())
        wmoExpand_->save();

    if (stdDumpModel_->hasData())
        stdExpand_->save();

    clearDumps();

    if (currentMsg_ < 0 || data_->totalMessageNum() == 0) {
        messageCanBeDecoded_ = false;
        return;
    }

    Q_ASSERT(currentMsg_ >= 0);

    messageCanBeDecoded_ = true;

    mvDumpLoaded_ = false;
    wmoDumpLoaded_ = false;
    stdDumpLoaded_ = false;
    tableDumpLoaded_ = false;
    valueDumpLoaded_ = false;

    // Generate and read grib dumps
    switch (dumpTab_->currentIndex()) {
        case NamespaceTabIndex:
            loadMvDump();
            break;
        case StdTabIndex:
            loadStdDump();
            break;
        case WmoTabIndex:
            loadWmoDump();
            break;
        case TableTabIndex:
            loadTableDump();
            break;
        case ValueTabIndex:
            loadValueDump();
            break;
        default:
            break;
    }
}

void MvQGribDumpPanel::clearDumps()
{
    clearMvDump();
    clearWmoDump();
    clearStdDump();
    clearTableDump();
    clearValueDump();
}

void MvQGribDumpPanel::loadMvDump()
{
    Q_ASSERT(currentMsg_ >= 0);

    if (mvDumpLoaded_ || !messageCanBeDecoded_)
        return;

    StatusMsgHandler::instance()->task("Loading namespace dump");

    auto* dump = new GribMvDump();
    if (dump->read(data_->fileName(), data_->unfilteredMessageCnt(currentMsg_))) {
        // the model takes ownership of the dump
        mvDumpModel_->setDumpData(dump);
        StatusMsgHandler::instance()->done();
    }
    else {
        delete dump;
        mvDumpModel_->clear();
        StatusMsgHandler::instance()->failed();
    }

    mvDumpLoaded_ = true;
}

void MvQGribDumpPanel::clearMvDump()
{
    mvDumpModel_->clear();
    mvDumpLoaded_ = false;
}

void MvQGribDumpPanel::loadWmoDump()
{
    Q_ASSERT(currentMsg_ >= 0);

    if (wmoDumpLoaded_ || !messageCanBeDecoded_)
        return;

    StatusMsgHandler::instance()->task("Loading wmo dump");

    auto* dump = new GribWmoDump();
    if (dump->read(data_->fileName(), data_->unfilteredMessageCnt(currentMsg_))) {
        wmoDumpModel_->setDumpData(dump);
        wmoDumpBrowser_->editor()->setPlainText(QString(dump->text().c_str()));
        StatusMsgHandler::instance()->done();
        wmoExpand_->restore();
    }
    else {
        delete dump;
        wmoDumpModel_->clear();
        StatusMsgHandler::instance()->failed();
    }

    wmoDumpLoaded_ = true;
}

void MvQGribDumpPanel::clearWmoDump()
{
    if (!wmoDumpLoaded_)
        return;

    wmoDumpModel_->clear();
    wmoDumpBrowser_->clear();
    wmoDumpLoaded_ = false;
}


void MvQGribDumpPanel::loadStdDump()
{
    Q_ASSERT(currentMsg_ >= 0);

    if (stdDumpLoaded_ || !messageCanBeDecoded_)
        return;

    StatusMsgHandler::instance()->task("Loading default dump");

    auto* dump = new GribStdDump();
    if (dump->read(data_->fileName(), data_->unfilteredMessageCnt(currentMsg_))) {
        stdDumpModel_->setDumpData(dump);
        stdDumpBrowser_->editor()->setPlainText(QString(dump->text().c_str()));
        StatusMsgHandler::instance()->done();
        stdExpand_->restore();
    }
    else {
        delete dump;
        stdDumpModel_->clear();
        StatusMsgHandler::instance()->failed();
    }

    stdDumpLoaded_ = true;
}

void MvQGribDumpPanel::clearStdDump()
{
    if (!stdDumpLoaded_)
        return;

    stdDumpModel_->clear();
    stdDumpBrowser_->clear();
    stdDumpLoaded_ = false;
}

void MvQGribDumpPanel::loadTableDump()
{
    if (currentMsg_ == -1) {
        return;
    }

    Q_ASSERT(currentMsg_ >= 0);

    if (tableDumpLoaded_ || !messageCanBeDecoded_)
        return;

    StatusMsgHandler::instance()->task("Loading table dump");

    tableTe_->clear();

    auto hCol = MvQTheme::accentText();

    std::vector<std::string> masterDir, localDir;
    GribTableDump td;
    if (td.read(data_->fileName(),
                data_->unfilteredMessageCnt(currentMsg_), masterDir, localDir)) {
        StatusMsgHandler::instance()->done();

        QString s;
        s += "ecCodes used the following tables to decode the current message: <br>";
        s += "<h2><font color=\'" + hCol.name() + "\'>WMO tables</font></h2>";
        s += "<br>&nbsp;&nbsp;<b>Paths</b>: <ul>";
        for (const auto& p : masterDir) {
            s += "<li>" + QString::fromStdString(p) + "</li>";
        }
        s += "</ul>";
        //    s += "<br>&nbsp;&nbsp;<b>Element table</b>: element.table";
        //    s += "<br>&nbsp;&nbsp;<b>Sequence table</b>: sequence.def";

        if (!localDir.empty()) {
            s += "<h2><font color=\'" + hCol.name() + "\'>Local tables</font></h2>";
            s += "<br>&nbsp;&nbsp;<b>Paths:</b> <ul>";
            for (const auto& p : localDir) {
                s += "<li>" + QString::fromStdString(p) + "</li>";
            }
            s += "</ul>";
            //        s += "<br>&nbsp;&nbsp;<b>Element table</b>: element.table";
            //        s += "<br>&nbsp;&nbsp;<b>Sequence table</b>: sequence.def";
        }
        tableTe_->setHtml(s);
    }
    else {
        StatusMsgHandler::instance()->failed();
    }

    tableDumpLoaded_ = true;
}

void MvQGribDumpPanel::clearTableDump()
{
    if (!tableDumpLoaded_)
        return;

    tableTe_->clear();
    tableDumpLoaded_ = false;
}

void MvQGribDumpPanel::loadValueDump()
{
    Q_ASSERT(currentMsg_ >= 0);

    if (valueDumpLoaded_ || !messageCanBeDecoded_)
        return;

    StatusMsgHandler::instance()->task("Loading value dump");

    // Generate and read grib dump
    auto* dump = new GribValueDump();
    std::string errMsg, warnMsg;
    if (dump->read(data_->fileName(), data_->unfilteredMessageCnt(currentMsg_), errMsg, warnMsg)) {
        valueDumpMessageLabel_->hide();
        valueDumpModel_->setDumpData(dump);
        int num = dump->num();
        StatusMsgHandler::instance()->done();
        valueRowSpin_->setEnabled(true);
        if (num > 0) {
            valueRowSpin_->setRange(1, num);
        }
        else {
            valueRowSpin_->setRange(0, 0);
        }
        QString info = " (Number of points: " + QString::number(num) + ")";
        valueDumpLabel_->setText(info);
        valueRowLabel_->show();
        valueRowSpin_->show();
        valueDumpLabel_->show();
    }
    else {
        delete dump;
        valueDumpModel_->clear();
        StatusMsgHandler::instance()->failed();
        valueRowLabel_->hide();
        valueRowSpin_->hide();
        valueDumpLabel_->hide();
        if (!warnMsg.empty()) {
            valueDumpMessageLabel_->showWarning(QString::fromStdString(warnMsg));
        }
        else {
            valueDumpMessageLabel_->showError(QString::fromStdString(errMsg));
        }
    }

    // Update info
    valueDumpLoaded_ = true;
}

void MvQGribDumpPanel::clearValueDump()
{
    if (!valueDumpLoaded_)
        return;

    valueDumpModel_->clear();
    valueDumpLabel_->clear();
    valueDumpLoaded_ = false;
    valueRowLabel_->show();
    valueRowSpin_->show();
    valueDumpLabel_->show();
    valueDumpMessageLabel_->clear();
    valueDumpMessageLabel_->hide();
}

void MvQGribDumpPanel::slotStdGroupClicked(QAbstractButton*)
{
    if (stdGroup_->checkedId() != -1) {
        stdDumpStacked_->setCurrentIndex(stdGroup_->checkedId());
    }
}

void MvQGribDumpPanel::slotWmoGroupClicked(QAbstractButton*)
{
    if (wmoGroup_->checkedId() != -1) {
        wmoDumpStacked_->setCurrentIndex(wmoGroup_->checkedId());
    }
}

void MvQGribDumpPanel::slotCurrentDumpChanged(int /*index*/)
{
    if (!data_)
        return;

    // Generate and read grib dumps
    switch (dumpTab_->currentIndex()) {
        case NamespaceTabIndex:
            loadMvDump();
            break;
        case StdTabIndex:
            loadStdDump();
            break;
        case WmoTabIndex:
            loadWmoDump();
            break;
        case TableTabIndex:
            loadTableDump();
            break;
        case ValueTabIndex:
            loadValueDump();
            break;
        default:
            break;
    }
}

void MvQGribDumpPanel::slotSetGribNameSpace(const QString& ns)
{
    if (mvDumpModel_) {
        mvDumpFilterHolder_->setVisible(ns == "Default");
        mvDumpModel_->setGribNameSpace(ns);
    }
}

void MvQGribDumpPanel::slotMvDumpFilter(QString txt)
{
    if (mvDumpModel_) {
        mvDumpModel_->setFilter(txt);
    }
}

void MvQGribDumpPanel::slotValueRowSpinChanged(int rowNumber)
{
    if (ignoreValueRowSpinChangeSignal_)
        return;

    QModelIndex srcIndex = valueDumpModel_->index(rowNumber - 1, 0);
    valueDumpTree_->setCurrentIndex(valueDumpSortModel_->mapFromSource(srcIndex));
}

void MvQGribDumpPanel::slotSelectValueRow(const QModelIndex& index)
{
    QModelIndex srcIndex = valueDumpSortModel_->mapToSource(index);

    int cnt = valueDumpModel_->data(valueDumpModel_->index(srcIndex.row(), 0)).toInt();
    if (cnt > 0) {
        ignoreValueRowSpinChangeSignal_ = true;
        valueRowSpin_->setValue(cnt);
        ignoreValueRowSpinChangeSignal_ = false;
    }
}

#if 0
void GribExaminer::slotMessageSpinChanged(int value)
{
    //Override cursor
    QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));

#if 0
    slotSelectMessage(value-1);
#endif

    QApplication::restoreOverrideCursor();

}
#endif

void MvQGribDumpPanel::writeSettings(QSettings& settings)
{
    settings.beginGroup("dump");
    MvQ::saveTabId(settings, "dumpMode", dumpTab_);

    settings.setValue("gribNameSpace", nsCombo_->currentText());
    MvQ::saveTreeColumnWidth(settings, "mvTreeColumnWidth", mvDumpTree_);

    settings.setValue("stdMode", stdGroup_->checkedId());
    MvQ::saveTreeColumnWidth(settings, "stdTreeColumnWidth", stdDumpTree_);

    settings.setValue("wmoMode", wmoGroup_->checkedId());
    MvQ::saveTreeColumnWidth(settings, "wmoTreeColumnWidth", wmoDumpTree_);

    MvQ::saveTreeColumnWidth(settings, "valueTreeColumnWidth", valueDumpTree_);

    settings.endGroup();
}

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

    MvQ::initTabId(settings, "dumpMode", dumpTab_);

    MvQ::initComboBox(settings, "gribNameSpace", nsCombo_);
    MvQ::initTreeColumnWidth(settings, "mvTreeColumnWidth", mvDumpTree_);

    MvQ::initButtonGroup(settings, "stdMode", stdGroup_);
    MvQ::initTreeColumnWidth(settings, "stdTreeColumnWidth", stdDumpTree_);

    MvQ::initButtonGroup(settings, "wmoMode", wmoGroup_);
    MvQ::initTreeColumnWidth(settings, "wmoTreeColumnWidth", wmoDumpTree_);

    MvQ::initTreeColumnWidth(settings, "valueTreeColumnWidth", valueDumpTree_);

    settings.endGroup();
}

//==========================================================
//
// GribExaminer
//
//==========================================================

GribExaminer::GribExaminer(QWidget* parent) :
#ifdef ECCODES_UI
    MvQAbstractMessageExaminer("codes_ui", MvQAbstractMessageExaminer::GribType, parent)
#else
    MvQAbstractMessageExaminer("GribExaminer", MvQAbstractMessageExaminer::GribType, parent)
#endif
{
    setAttribute(Qt::WA_DeleteOnClose);

#ifdef ECCODES_UI
    winTitleBase_ = "codes_ui";
    settingsName_ = "codesui-grib";
#else
    winTitleBase_ = "Grib Examiner (Metview)";
    settingsName_ = "mv-GribExaminer";
#endif
    setWindowTitle(winTitleBase_);

    // Initial size
    setInitialSize(1100, 800);

    // Init
    messageType_ = "GRIB";

    // gribNameSpace_ << "Default" << "geography" << "ls" << "mars"
    //		<< "parameter" << "statistics" << "time" << "vertical";


    fileInfoLabelNeedsUpdating_ = true;
    // ignoreValueRowSpinChangeSignal_=false;

    mainPanel_ = new MvQGribMainPanel(this);
    centralSplitter_->addWidget(mainPanel_);

    connect(mainPanel_, SIGNAL(keyProfileChanged()),
            this, SLOT(slotKeyProfileChanged()));

    connect(mainPanel_, SIGNAL(newMessageSelected(int)),
            logPanel_, SLOT(newMessageLoaded(int)));

    connect(mainPanel_, SIGNAL(messageNumDetermined()),
            this, SLOT(updateFileInfoLabel()));

    // Set up panel
    // setupGotoPanel();
    // setupDumpPanel();

    //----------------------------
    // Setup menus and toolbars
    //----------------------------

    setupMenus(menuItems_);

    //-------------------------
    // Settings
    //-------------------------

    readSettings();
}

GribExaminer::~GribExaminer()
{
    saveKeyProfiles(true);
    writeSettings();
}

void GribExaminer::initMain(MvMessageMetaData* data)
{
    Q_ASSERT(mainPanel_);
    auto* grib = dynamic_cast<GribMetaData*>(data);
    mainPanel_->init(grib);
}

void GribExaminer::initDumps()
{
}

void GribExaminer::initAllKeys()
{
    // Get all keys
    auto* prof = new MvKeyProfile("Metview keys");
    prof->addKey(new MvKey("MV_Index", "Index", "Message index"));
    allKeys_ << prof;

    // GribMetaData *grib=static_cast<GribMetaData*>(data_);

    QStringList gribNameSpace;
    gribNameSpace << "Default"
                  << "geography"
                  << "ls"
                  << "mars"
                  << "parameter"
                  << "statistics"
                  << "time"
                  << "vertical";

    Q_ASSERT(data_);
    auto* gd = dynamic_cast<GribMetaData*>(data_);
    Q_ASSERT(gd);

    foreach (QString ns, gribNameSpace) {
        QString pname = "Namespace: " + ns;
        auto* prof = new MvKeyProfile(pname.toStdString());
        gd->getKeyList(1, ns.toStdString().c_str(), prof);
        allKeys_ << prof;
    }
}

void GribExaminer::slotLoadFile(QString f)
{
    if (data_->fileName() == f.toStdString())
        return;

    data_->setFileName(f.toStdString());
    currentMessageNo_ = -1;

    // Reload
    mainPanel_->reloadData();

    updateFileInfoLabel();

    initAllKeys();
}

void GribExaminer::loadKeyProfile(MvKeyProfile* prof)
{
    Q_ASSERT(mainPanel_);
    mainPanel_->loadKeyProfile(prof);
}

MvKeyProfile* GribExaminer::loadedKeyProfile() const
{
    Q_ASSERT(mainPanel_);
    return mainPanel_->keyProfile();
}

void GribExaminer::slotShowAboutBox()
{
#ifdef ECCODES_UI
    MvQAbout about("codes_ui", "", MvQAbout::GribApiVersion);
#else
    MvQAbout about("GribExaminer", "", MvQAbout::GribApiVersion | MvQAbout::MetviewVersion);
#endif
    about.exec();
}

void GribExaminer::updateFileInfoLabel()
{
    auto* grib = dynamic_cast<GribMetaData*>(data_);

    fileInfoLabel_->setGribTextLabel(QString(data_->fileName().c_str()), data_->totalMessageNum(),
                                     data_->isFilterEnabled(), data_->messageNum(), grib->hasMultiMessage());

    updateWinTitle(QString::fromStdString(data_->fileName()));
}

void GribExaminer::writeSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, settingsName_);

    settings.clear();

    MvQAbstractMessageExaminer::writeSettings(settings);

    mainPanel_->writeSettings(settings);
}

void GribExaminer::readSettings()
{
    QSettings settings(MVQ_QSETTINGS_DEFAULT_ARGS, settingsName_);

    MvQAbstractMessageExaminer::readSettings(settings);

    mainPanel_->readSettings(settings);
}
