/***************************** 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 <QApplication>
#include <QAction>
#include <QActionGroup>
#include <QCheckBox>
#include <QCloseEvent>
#include <QComboBox>
#include <QCursor>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QFrame>
#include <QInputDialog>
#include <QLineEdit>
#include <QListView>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QProgressBar>
#include <QProgressDialog>
#include <QPushButton>
#include <QSettings>
#include <QSortFilterProxyModel>
#include <QSpinBox>
#include <QSplitter>
#include <QStackedLayout>
#include <QStackedWidget>
#include <QStatusBar>
#include <QString>
#include <QStringList>
#include <QStringListModel>
#include <QTextBrowser>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdio>
#include <cstdlib>

#include "MvQAbstractMessageExaminer.h"

#include "MvKeyProfile.h"
#include "MvMiscellaneous.h"

#ifdef METVIEW
#include "MvVersionInfo.h"
#endif

#include "LogHandler.h"

#include "MessageControlPanel.h"
#include "MvQArrowSpinWidget.h"
#include "MvQFileDialog.h"
#include "MvQKeyDialog.h"
#include "MvQFileInfoLabel.h"
#include "MvQFileList.h"
#include "MvQFileListWidget.h"
#include "MvQKeyManager.h"
#include "MvQKeyProfileModel.h"
#include "MvQKeyProfileTree.h"
#include "MvQLogPanel.h"
#include "MvQMainWindow.h"
//#include "MvQTreeViewSearchLine.h"
#include "MvQTreeView.h"
#include "MvQTheme.h"
#include "StatusMsgHandler.h"
#include "StatusProgBarHandler.h"

#ifdef ECCODES_UI
#include "MvQLocalSocketServer.h"
#endif

#include "MvQBufrMessageScanner.h"
#include "MvQGribMessageScanner.h"
#include "MvLog.h"

#define _UI_MVQABSTRACTMESSAGEEXAMINER_DEBUG

MvQMessagePanel::MvQMessagePanel(QWidget* parent) :
    QWidget(parent),
    data_(nullptr),
    loader_(nullptr)
{
    //--------------------------------
    // Message tree (key profile)
    //--------------------------------

    auto* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(1);

    // Message tree
    model_ = new MvQKeyProfileModel(this);
    model_->setShadeFirstColumn(true);

    sortModel_ = new MvQKeyProfileSortFilterModel;
    sortModel_->setSourceModel(model_);
    sortModel_->setDynamicSortFilter(true);
    sortModel_->setFilterRole(Qt::UserRole);
    sortModel_->setFilterFixedString("1");
    sortModel_->setFilterKeyColumn(0);

    // progress bar
    progWidget_ = new QWidget(this);
    auto* progHb = new QHBoxLayout(progWidget_);
    progHb->setContentsMargins(0, 1, 0, 0);

    progLabel_ = new QLabel(this);
    progLabel_->setText(tr("Scanning messages: "));
    progHb->addWidget(progLabel_);

    progBar_ = new QProgressBar(this);
    progBar_->setRange(0, 100);
    QFont f;
    QFontMetrics fm(f);
    progBar_->setMaximumHeight(fm.height());

    progHb->addWidget(progBar_);
    layout->addWidget(progWidget_);
    progWidget_->hide();

    // tree
    tree_ = new MvQKeyProfileTree(this);
    tree_->setModels(model_, sortModel_);
    tree_->setUniformRowHeights(true);

    layout->addWidget(tree_);

    // Message list selection
    // connect(tree_,SIGNAL(clicked(QModelIndex)),
    //     this,SLOT(selectMessage(QModelIndex)));

    // connect(tree_,SIGNAL(activated(QModelIndex)),
    //     this,SLOT(selectMessage(QModelIndex)));

    connect(tree_, SIGNAL(selectionChanged(QModelIndex)),
            this, SLOT(selectMessage(QModelIndex)));

    // The profile was altered via the treeview headings
    connect(tree_, SIGNAL(profileChanged(bool, int)),
            this, SLOT(adjustProfile(bool, int)));

    // The profile was altered via drag and drop
    //  connect(model_,SIGNAL(keyInserted()),
    //      this,SLOT(reloadProfileFromModel()));
}

void MvQMessagePanel::init(MvMessageMetaData* data)
{
    Q_ASSERT(!data_);
    data_ = data;
    data_->registerObserver(this);
}

MvKeyProfile* MvQMessagePanel::keyProfile() const
{
    return model_->keyProfile();
}

void MvQMessagePanel::loadKeyProfile(MvKeyProfile* profDef)
{
    // Clone the profile
    MvKeyProfile* prof = profDef->clone();

    // Load data into the profile
    loadKeyProfileCore(prof);  // can be threaded!!!

    // Will be finished in slotKeyProfileReady()!!!
}

void MvQMessagePanel::loadKeyProfileCore(MvKeyProfile* prof)
{
    Q_ASSERT(data_);

    // int num;
    // num=data_->messageNum();
    // if(num == 0)
    //{
    //     num=data_->getEstimatedMessageNum();
    // }

    if (!loader_) {
        if (data_->type() == MvMessageMetaData::BufrType)
            loader_ = new BufrMessageScanner(data_, this);
        else if (data_->type() == MvMessageMetaData::GribType)
            loader_ = new GribMessageScanner(data_, this);
        else {
            Q_ASSERT(0);
            exit(1);
        }

        connect(loader_, SIGNAL(profileReady(MvKeyProfile*)),
                this, SLOT(slotKeyProfileReady(MvKeyProfile*)));

        //        connect(loader_, SIGNAL(started()),
        //                this, SLOT(slotKeyProfileLoadStarted()));

        connect(loader_, SIGNAL(loadStarted()),
                this, SLOT(slotKeyProfileLoadStarted()));

        //        connect(loader_, SIGNAL(finished()),
        //                this, SLOT(slotKeyProfileLoadFinished()));

        connect(loader_, SIGNAL(loadFinished()),
                this, SLOT(slotKeyProfileLoadFinished()));

        connect(loader_, SIGNAL(profileProgress(int)),
                this, SLOT(slotKeyProfileLoadProgress(int)));
    }

    // This can be run in a separate thread!
    loader_->startScan(prof);
}

void MvQMessagePanel::slotKeyProfileLoadStarted()
{
    progWidget_->show();
    progLabel_->setText(tr("Determining number of messages ..."));
    progBar_->setRange(0, 0);
}

void MvQMessagePanel::slotKeyProfileLoadFinished()
{
    progWidget_->hide();
    tree_->viewport()->update();
    emit keyProfileLoadFinished();
}

void MvQMessagePanel::slotKeyProfileLoadProgress(int loadPercent)
{
    progBar_->setValue(loadPercent);
}

void MvQMessagePanel::slotKeyProfileReady(MvKeyProfile* prof)
{
    Q_ASSERT(loader_);

    int selectedRow = -1;
    std::string val = prof->metaData("selectedRow");
    if (!val.empty()) {
        selectedRow = metview::fromString<int>(val);
    }

    if (selectedRow == -1) {
        int oriRow = sortModel_->mapToSource(tree_->currentIndex()).row();
        bool firstLoad = (model_->keyProfile() == nullptr);

        // Update message tree
        model_->setKeyProfile(prof);

        tree_->setCurrentIndex(QModelIndex());

        // Restore selection
        if (oriRow >= 0 && oriRow < sortModel_->rowCount()) {
            tree_->setCurrentIndex(sortModel_->mapFromSource(model_->index(oriRow, 0)));
        }
        else {
            tree_->setCurrentIndex(sortModel_->index(0, 0));
        }

        tree_->setEditable(!prof->systemProfile());

        if (firstLoad) {
            for (int i = 0; i < sortModel_->columnCount() - 1; i++)
                tree_->resizeColumnToContents(i);
        }
    }
    else {
        model_->setKeyProfile(prof);

        // model_->keyProfileChangeFinished();

        if (selectedRow >= model_->rowCount() || selectedRow == -1)
            selectedRow = 0;

        tree_->setCurrentIndex(sortModel_->mapFromSource(model_->index(selectedRow, 0)));

        emit keyProfileChanged();
    }

    Q_EMIT messageNumDetermined();

    if (progWidget_->isVisible()) {
        progLabel_->setText(tr("Scanning messages: "));
        progBar_->setRange(0, 100);
        progBar_->reset();
    }

    emit keyProfileReady();
}

void MvQMessagePanel::selectMessage(int msgCnt)
{
    QModelIndex srcIndex = model_->index(msgCnt, 0);
    tree_->setCurrentIndex(sortModel_->mapFromSource(srcIndex));
}

void MvQMessagePanel::selectMessage(QModelIndex idx)
{
    int cnt = sortModel_->mapToSource(idx).row();
    emit messageSelected(cnt);
}

void MvQMessagePanel::messageScanStepChanged(int step)
{
    emit messageScanProgess(step);
}

void MvQMessagePanel::clearProfile()
{
    if (loader_) {
        loader_->stopLoad();
    }

    MvKeyProfile* profDef = model_->keyProfile();
    Q_ASSERT(profDef);
    MvKeyProfile* prof = profDef->clone();  // this does not copy the data!!!
    model_->setKeyProfile(prof);
}

// The profile was modified via the treeview headings
void MvQMessagePanel::adjustProfile(bool reload, int selectedRow)
{
    // emit statusMessage(tr("Reload key profile ..."));

    if (reload) {
        if (loader_) {
            loader_->stopLoad();
        }

        if (selectedRow == -1)
            selectedRow = sortModel_->mapToSource(tree_->currentIndex()).row();

        // model_->keyProfileIsAboutToChange();

        // Get the current profile from the model.
        MvKeyProfile* profDef = model_->keyProfile();
        Q_ASSERT(profDef);
        // Clear the data
        MvKeyProfile* prof = profDef->clone();
        prof->setMetaData("selectedRow", std::to_string(selectedRow));

        // Update grib metadata with the new key profile
        loadKeyProfileCore(prof);  // can be threaded!!!!

        // Will be finished in slotKeyProfileReady()!!!

#if 0
        model_->setKeyProfile(prof);

        //model_->keyProfileChangeFinished();

        if(selectedRow >= model_->rowCount() || selectedRow == -1)
           selectedRow=0;

        tree_->setCurrentIndex(sortModel_->mapFromSource(model_->index(selectedRow,0)));
#endif
    }
    else {
        emit keyProfileChanged();
    }

    // for(int i=0;i < model_->columnCount()-1; i++)
    //{
    //      tree_->resizeColumnToContents(i);
    // }

    // tree_->setCurrentIndex(sortModel_->index(0,0));
    // selectMessage(tree_->currentIndex());
}

void MvQMessagePanel::slotMessageDataInvalid(int msg)
{
    MvKeyProfile* prof = model_->keyProfile();
    if (msg >= 0 && msg < static_cast<int>(prof->valueNum())) {
        if (!prof->isErrorRow(msg)) {
            prof->markErrorRow(msg);
            // We update the row in the tree
            QModelIndex idx1 = sortModel_->mapFromSource(model_->index(msg, 0));
            QModelIndex idx2 = sortModel_->mapFromSource(model_->index(msg, model_->columnCount() - 1));
            tree_->dataChanged(idx1, idx2);
        }
    }
}

MvQAbstractMessageExaminer::MvQAbstractMessageExaminer(QString appName, Type type, QWidget* parent) :
    MvQMainWindow(parent),
    appName_(appName),
    type_(type)
{
    // File info label
    fileInfoLabel_ = new MvQFileInfoLabel(this);

    // Set up the panels
    logPanel_ = new MvQLogPanel(this);

#ifdef ECCODES_UI
    filePanel_ = new MvQFileListWidget(this);

    connect(filePanel_, SIGNAL(fileSelected(QString)),
            this, SLOT(slotLoadFile(QString)));
#endif

    //----------------------------------------------
    // Defines the main layouts the and holders of
    // the panels
    //----------------------------------------------

    QWidget* w;

    //----------------------
    // Main splitter
    //----------------------

    mainSplitter_ = new QSplitter(this);
    mainSplitter_->setOrientation(Qt::Vertical);

    mainSplitter_->setOpaqueResize(false);
    setCentralWidget(mainSplitter_);

    //-----------------------------------------------------
    // The main layout (the upper part of mainSplitter)
    //-----------------------------------------------------

    auto* mainLayout = new QVBoxLayout;
    mainLayout->setObjectName(QString::fromUtf8("vboxLayout"));
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);
    w = new QWidget(this);
    w->setLayout(mainLayout);
    mainSplitter_->addWidget(w);

    //------------------
    // File info label
    //------------------

    mainLayout->addWidget(fileInfoLabel_);

    //---------------------
    // Central splitter
    //---------------------

    centralSplitter_ = new QSplitter(this);
    centralSplitter_->setOpaqueResize(false);
    mainLayout->addWidget(centralSplitter_, 1);

    //-------------------------
    // Populate central splitter
    //-------------------------

#ifdef ECCODES_UI
    centralSplitter_->addWidget(filePanel_);
    centralSplitter_->setCollapsible(0, false);
#endif

    //--------------------------
    // Log Area
    //--------------------------

    mainSplitter_->addWidget(logPanel_);
    mainSplitter_->setCollapsible(1, false);

    //--------------------------
    // Status bar
    //--------------------------

    statusMessageLabel_ = new QLabel(this);
    statusMessageLabel_->setFrameShape(QFrame::NoFrame);
    statusBar()->addPermanentWidget(statusMessageLabel_, 1);  // '1' means 'please stretch me when resized'

    StatusMsgHandler::instance()->init(statusMessageLabel_);

    //----------------------------
    // Menus
    //----------------------------

    setupFileMenu();
    setupEditMenu();
    setupViewMenu();
    setupSettingsMenu();
    setupProfilesMenu();
    setupHelpMenu();

    //----------------------------
    // Signals and slots
    //----------------------------

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

#ifdef ECCODES_UI
    // Creat a socket based server to listen to incoming commands/messages
    QString typeString;
    if (type_ == GribType)
        typeString = "grib";
    else if (type_ == BufrType)
        typeString = "bufr";

    if (!typeString.isEmpty()) {
        MvQLocalSocketServer* socketServer = new MvQLocalSocketServer("codes_ui_" + typeString, this);
        connect(socketServer, SIGNAL(messageReceived(QString)),
                this, SLOT(slotSocketMessage(QString)));
    }
#endif
}

MvQAbstractMessageExaminer::~MvQAbstractMessageExaminer()
{
    // Cannot call saveKeyProfiles() here beacuse it depends on a pure
    // virtual method. So it must be called in the subclass destructors.
}

void MvQAbstractMessageExaminer::closeEvent(QCloseEvent* event)
{
    // saveKeyProfiles(true);
    event->accept();
}

#if 0
void MvQAbstractMessageExaminer::setupMessagePanel()
{
	//--------------------------------
	// Message tree (key profile)
	//--------------------------------

    messagePanel_=new QWidget(this);
	messageLayout_ = new QVBoxLayout;
	messageLayout_->setContentsMargins(0,0,0,0);
	messagePanel_->setLayout(messageLayout_);
	//messageSplitter_->addWidget(messageWidget);	

	//Label
	//label=new QLabel(tr("Messages"));
    //label->setFrameShape(QFrame::StyledPanel);
	//messageLayout_->addWidget(label);

	//Message tree
	messageModel_= new MvQKeyProfileModel;
	messageSortModel_= new MvQKeyProfileSortFilterModel;
    messageSortModel_->setSourceModel(messageModel_);
	messageSortModel_->setDynamicSortFilter(true);
	messageSortModel_->setFilterRole(Qt::UserRole);
	//messageSortModel_->setFilterRegExp(QRegExp("[1]"));
	messageSortModel_->setFilterFixedString("1");
	messageSortModel_->setFilterKeyColumn(0);

	messageTree_=new MvQKeyProfileTree(messageModel_,messageSortModel_);
	messageTree_->setUniformRowHeights(true);

	messageLayout_->addWidget(messageTree_);	
}
#endif

void MvQAbstractMessageExaminer::setupFileMenu()
{
#ifdef ECCODES_UI
    actionAddFile_ = new QAction(this);
    actionAddFile_->setObjectName(QString::fromUtf8("actionAddFile"));
    actionAddFile_->setText(tr("&Add file ..."));
    actionAddFile_->setToolTip(tr("Add file"));
    actionAddFile_->setShortcut(tr("Ctrl+N"));
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/examiner/addFile.svg")), QIcon::Normal, QIcon::Off);
    actionAddFile_->setIcon(icon);

    connect(actionAddFile_, SIGNAL(triggered(bool)),
            this, SLOT(slotAddFile(bool)));

    MvQMainWindow::MenuType menuType = MvQMainWindow::FileMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(actionAddFile_, MvQMenuItem::MenuTarget));
#endif
}


void MvQAbstractMessageExaminer::setupViewMenu()
{
    //
    actionFileInfo_ = new QAction(this);
    actionFileInfo_->setObjectName(QString::fromUtf8("actionFileInfo"));
    actionFileInfo_->setText(tr("&File info"));
    actionFileInfo_->setShortcut(tr("Ctrl+I"));
    actionFileInfo_->setCheckable(true);
    actionFileInfo_->setChecked(true);
    actionFileInfo_->setToolTip(tr("View file info"));
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/examiner/info_light.svg")), QIcon::Normal, QIcon::Off);
    actionFileInfo_->setIcon(icon);

    // Define routines
    connect(actionFileInfo_, SIGNAL(toggled(bool)),
            fileInfoLabel_, SLOT(setVisible(bool)));

    //
    actionLog_ = new QAction(this);
    actionLog_->setObjectName(QString::fromUtf8("actionLog"));
    actionLog_->setText(tr("&Log"));
    actionLog_->setShortcut(tr("Ctrl+L"));
    actionLog_->setCheckable(true);
    actionLog_->setChecked(true);
    actionLog_->setToolTip(tr("View log"));
    QIcon icon1;
    icon1.addPixmap(QPixmap(QString::fromUtf8(":/examiner/log.svg")), QIcon::Normal, QIcon::Off);
    actionLog_->setIcon(icon1);

    // logPanel connects up logAction  and manages it
    logPanel_->setViewAction(actionLog_);

    // Log is hidden by default!!!
    actionLog_->setChecked(false);

#ifdef ECCODES_UI
    // File panel is hidden by default
    actionFilePanel_ = new QAction(this);
    actionFilePanel_->setObjectName(QString::fromUtf8("actionFilePanel"));
    actionFilePanel_->setText(tr("&File list"));
    actionFilePanel_->setCheckable(true);
    actionFilePanel_->setChecked(true);
    actionFilePanel_->setToolTip(tr("View file list"));
    actionFilePanel_->setIcon(QPixmap(":/examiner/fileList.svg"));

    // actionFilePanel_->setEnabled(false);

    // Define routines
    connect(actionFilePanel_, SIGNAL(toggled(bool)),
            filePanel_, SLOT(setVisible(bool)));

    connect(filePanel_, SIGNAL(clearPanel(bool)),
            this, SLOT(slotClearFilePanel(bool)));

    connect(filePanel_, SIGNAL(closePanel(bool)),
            this, SLOT(slotCloseFilePanel(bool)));

    connect(filePanel_, SIGNAL(fileSelected(QString)),
            this, SLOT(slotLoadFile(QString)));

#endif

    MvQMainWindow::MenuType menuType = MvQMainWindow::ViewMenu;

#ifdef ECCODES_UI
    menuItems_[menuType].push_back(new MvQMenuItem(actionFilePanel_));
#endif
    menuItems_[menuType].push_back(new MvQMenuItem(actionFileInfo_));
    menuItems_[menuType].push_back(new MvQMenuItem(actionLog_));
}

void MvQAbstractMessageExaminer::setupEditMenu()
{
// TODO: reimplement find
#if 0
    actionFind_=MvQMainWindow::createAction(MvQMainWindow::FindAction,this);
	connect(actionFind_,SIGNAL(triggered(bool)),
		findPanel_,SLOT(setHidden(bool)));
				
	QAction *actionFindNext=MvQMainWindow::createAction(MvQMainWindow::FindNextAction,this);
	connect(actionFindNext,SIGNAL(triggered(bool)),
		findPanel_,SLOT(slotFindNext(bool)));	
		
	QAction *actionFindPrev=MvQMainWindow::createAction(MvQMainWindow::FindPreviousAction,this);
	connect(actionFindPrev,SIGNAL(triggered(bool)),
		findPanel_,SLOT(slotFindPrev(bool)));
		
	MvQMainWindow::MenuType menuType=MvQMainWindow::EditMenu;
	
	menuItems_[menuType].push_back(new MvQMenuItem(actionFind_,MvQMenuItem::MenuTarget));
	menuItems_[menuType].push_back(new MvQMenuItem(actionFindNext,MvQMenuItem::MenuTarget));
	menuItems_[menuType].push_back(new MvQMenuItem(actionFindPrev,MvQMenuItem::MenuTarget));
#endif
}

void MvQAbstractMessageExaminer::setupSettingsMenu()
{
#ifdef ECCODES_UI
    QMenu* styleMenu = new QMenu(tr("UI Theme"), this);

    styleAg_ = new QActionGroup(this);
    QAction* actionLightStyle_ = new QAction(this);
    actionLightStyle_->setText(tr("Light"));
    actionLightStyle_->setData("light");
    actionLightStyle_->setCheckable(true);
    QAction* actionDarkStyle_ = new QAction(this);
    actionDarkStyle_->setText(tr("Dark"));
    actionDarkStyle_->setData("dark");
    actionDarkStyle_->setCheckable(true);
    styleAg_->addAction(actionLightStyle_);
    styleAg_->addAction(actionDarkStyle_);
    actionLightStyle_->setChecked(true);

    connect(styleAg_, SIGNAL(triggered(QAction*)),
            this, SLOT(slotUiStyleSelect(QAction*)));

    MvQMainWindow::MenuType menuType = MvQMainWindow::SettingsMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(styleMenu));
    menuItems_[menuType].push_back(new MvQMenuItem(actionLightStyle_, MvQMenuItem::SubMenuTarget));
    menuItems_[menuType].push_back(new MvQMenuItem(actionDarkStyle_, MvQMenuItem::SubMenuTarget));
#endif
}

void MvQAbstractMessageExaminer::setupProfilesMenu()
{
    //
    auto* actionNew = new QAction(this);
    actionNew->setObjectName(QString::fromUtf8("actionProfileNew"));
    actionNew->setText(tr("&New profile"));
    actionNew->setToolTip(tr("Create new key profile"));
    QIcon icon;
    icon.addPixmap(QPixmap(QString::fromUtf8(":/keyDialog/profile_add.svg")), QIcon::Normal, QIcon::Off);
    actionNew->setIcon(icon);

    auto* actionSaveAs = new QAction(this);
    actionSaveAs->setObjectName(QString::fromUtf8("actionProfileSaveAs"));
    actionSaveAs->setText(tr("&Duplicate profile ..."));
    actionSaveAs->setToolTip(tr("Duplicate key profile"));
    QIcon icon2;
    icon2.addPixmap(QPixmap(QString::fromUtf8(":/keyDialog/profile_duplicate.svg")), QIcon::Normal, QIcon::Off);
    actionSaveAs->setIcon(icon2);

    auto* actionProfileManager = new QAction(this);
    actionProfileManager->setObjectName(QString::fromUtf8("actionKeyProfileManager"));
    actionProfileManager->setText(tr("&Manage profiles"));
    actionProfileManager->setToolTip(tr("Manage key profiles"));
    QIcon icon3;
    icon3.addPixmap(QPixmap(QString::fromUtf8(":/keyDialog/profile_manage.svg")), QIcon::Normal, QIcon::Off);
    actionProfileManager->setIcon(icon3);
    actionProfileManager->setToolTip(tr("Key profile manager"));

    /*QAction* actionExport = new QAction(this);
        actionExport->setObjectName(QString::fromUtf8("actionProfileExport"));
    actionExport->setText(tr("&Export"));
    actionExport->setToolTip(tr("Export key profiles"));

    QAction* actionImport = new QAction(this);
        actionImport->setObjectName(QString::fromUtf8("actionProfileImport"));
    actionImport->setText(tr("&Import"));
    actionImport->setToolTip(tr("Import key profiles"));*/


    // Key profile management

    connect(actionNew, SIGNAL(triggered()),
            this, SLOT(slotCreateNewProfile()));

    connect(actionSaveAs, SIGNAL(triggered()),
            this, SLOT(slotSaveAsCurrentProfile()));

    connect(actionProfileManager, SIGNAL(triggered()),
            this, SLOT(slotManageKeyProfiles()));


    auto* wh = new QWidget(this);
    auto* hb = new QHBoxLayout(wh);
    hb->setContentsMargins(1, 1, 1, 1);

    // Combo box
    // We will add items + signal/slots later in "init"!
    // Combo box for key profile selection
    auto* keyLabel = new QLabel(tr("Key profile:"), wh);
    keyCombo_ = new QComboBox(wh);
    keyLabel->setBuddy(keyCombo_);

    hb->addWidget(keyLabel);
    hb->addWidget(keyCombo_);

    // MvQMenuItem::MenuTarget

    MvQMainWindow::MenuType menuType = MvQMainWindow::ProfilesMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(actionNew, MvQMenuItem::MenuTarget));
    menuItems_[menuType].push_back(new MvQMenuItem(actionSaveAs, MvQMenuItem::MenuTarget));
    // menuItems_["profiles"].push_back(new MvQMenuItem(actionExport,MvQMenuItem::MenuTarget));
    // menuItems_["profiles"].push_back(new MvQMenuItem(actionImport,MvQMenuItem::MenuTarget));
    menuItems_[menuType].push_back(new MvQMenuItem(actionProfileManager, MvQMenuItem::MenuTarget));
    // menuItems_["profiles"].push_back(new MvQMenuItem(actionKeyManager,MvQMenuItem::MenuTarget));
    menuItems_[menuType].push_back(new MvQMenuItem(wh));
}

void MvQAbstractMessageExaminer::setupHelpMenu()
{
    // About
    auto* actionAbout = new QAction(this);
    actionAbout->setObjectName(QString::fromUtf8("actionAbout"));
    actionAbout->setText(tr("&About ") + appName_);

    connect(actionAbout, SIGNAL(triggered()), this, SLOT(slotShowAboutBox()));

    MvQMainWindow::MenuType menuType = MvQMainWindow::HelpMenu;

    menuItems_[menuType].push_back(new MvQMenuItem(actionAbout, MvQMenuItem::MenuTarget));
}

void MvQAbstractMessageExaminer::init(MvMessageMetaData* data, MvQKeyManager* manager, MvQFileList* fileList)
{
    data_ = data;
    keyManager_ = manager;

#ifdef ECCODES_UI
    Q_ASSERT(fileList);
#endif

    // Files are specified
    if (fileList) {
        // The specified list is not empty
        if (fileList->count() > 0) {
            // We delete the file list loaded from the settings
            if (fileList_) {
                delete fileList_;
            }
            // and use the specified list instead
            fileList_ = fileList;
        }
        // otherwise if nothing was read from the settings we
        // initialise the filelist with the empty one
        else if (!fileList_) {
            fileList_ = fileList;
        }
        Q_ASSERT(fileList_);
    }

    Q_ASSERT(data_);
    Q_ASSERT(keyManager_);

#ifdef ECCODES_UI
    // Init file list
    Q_ASSERT(fileList_);

    // The filename is set in data - it must have been set externally
    if (!data_->fileName().empty()) {
        QString err;
        // This is the only file in the list
        if (fileList_->count() == 1) {
            sourceFileName_ = QString::fromStdString(data_->fileName());
            if (!fileList_->isPresent(sourceFileName_))
                fileList_->add(sourceFileName_, err, false);
        }

        // There are other files and there is a saved filename
        else if (!sourceFileName_.isEmpty()) {
            // The saved filename is valid, we set it in the data
            if (fileList_->isPresent(sourceFileName_)) {
                data_->setFileName(sourceFileName_.toStdString());
            }
            else
                sourceFileName_ = QString::fromStdString(data_->fileName());
        }
    }
    // No filename was set externally - we must use now the saved filelist
    else {
        QString err;
        // There is a saved filename
        if (!sourceFileName_.isEmpty()) {
            bool valid = fileList_->isPresent(sourceFileName_);
            if (!valid) {
                valid = fileList_->add(sourceFileName_, err, false);
            }

            // The saved filename is valid, we set it in the data
            if (valid) {
                data_->setFileName(sourceFileName_.toStdString());
            }
            // otherwise the take first from the list
            else if (fileList_->count() > 0) {
                sourceFileName_ = fileList_->path(0);
                data_->setFileName(sourceFileName_.toStdString());
            }
        }
        // otherwise the take first from the list
        else if (fileList_->count() > 0) {
            sourceFileName_ = fileList_->path(0);
            data_->setFileName(sourceFileName_.toStdString());
        }
    }

    filePanel_->setFileList(fileList_);
    filePanel_->selectFile(sourceFileName_, false);

#else
    sourceFileName_ = QString::fromStdString(data_->fileName());
#endif

    // Init dump panel
    initMain(data_);

    //----------------------------------
    // Initilaize the key profile list
    //---------------------------------

    // Init key combo
    int currentIndex = -1;
    for (unsigned int i = 0; i < keyManager_->data().size(); i++) {
        QString str(keyManager_->data().at(i)->name().c_str());
        keyCombo_->addItem(str);
        if (savedKeyProfileName_ == str) {
            currentIndex = i;
        }
        if (keyManager_->data().at(i)->systemProfile()) {
            keyCombo_->setItemIcon(i, MvQTheme::logo());
        }
    }

    // If we do not find a matching a item will select the last one!
    if (currentIndex == -1)
        currentIndex = keyCombo_->count() - 1;

    // Set the keyCombo_'s current index to -1 and define the signal-slot for the keyCombo_
    keyCombo_->setCurrentIndex(-1);

    connect(keyCombo_, SIGNAL(currentIndexChanged(int)),
            this, SLOT(slotLoadKeyProfile(int)));

    // Now the currentIndex in keyCombo_ is set to -1, but the currentIndex local variable stores
    // a non-negativ value. So by setting the current index in the keyCombo_ the slot
    // above will be called and the right key profile will be loaded.
    keyCombo_->setCurrentIndex(currentIndex);

#if 0
	messageTree_->setPredefinedKeysOnly(keyManager_->predefinedKeysOnly());
#endif

    // Fileinfo label
    updateFileInfoLabel();

    // Get all keys
    initAllKeys();
}

void MvQAbstractMessageExaminer::setCurrentKeyProfileName(QString name)
{
    if (keyManager_) {
        for (unsigned int i = 0; i < keyManager_->data().size(); i++) {
            QString str(keyManager_->data().at(i)->name().c_str());
            if (str == name) {
                keyCombo_->setCurrentIndex(i);
                break;
            }
        }
    }
}

void MvQAbstractMessageExaminer::slotLoadKeyProfile(int index)
{
    saveKeyProfiles(false);

    if (index != -1) {
        loadKeyProfile(keyManager_->data().at(index));
    }
}

void MvQAbstractMessageExaminer::slotKeyProfileChanged()
{
    saveKeyProfiles(false);
}

void MvQAbstractMessageExaminer::slotCreateNewProfile()
{
    bool ok = false;
    QString text = QInputDialog::getText(nullptr, tr("New profile"),
                                         tr("New profile name:"), QLineEdit::Normal,
                                         "", &ok);

    if (ok && !text.isEmpty()) {
        saveKeyProfiles(false);
        keyManager_->addProfile(text.toStdString());
        keyCombo_->addItem(text);
        keyCombo_->setCurrentIndex(keyCombo_->count() - 1);
        keyManager_->saveProfiles();  // save it to disk
    }
}

void MvQAbstractMessageExaminer::slotSaveAsCurrentProfile()
{
    MvKeyProfile* prof = loadedKeyProfile();

    if (!prof)
        return;

    bool ok = false;
    QString text = QInputDialog::getText(nullptr, tr("Duplicate profile"),
                                         tr("New profile name:"), QLineEdit::Normal,
                                         "", &ok);
    if (ok && !text.isEmpty()) {
        saveKeyProfiles(false);
        keyManager_->addProfile(text.toStdString(), prof);
        keyCombo_->addItem(text);
        keyCombo_->setCurrentIndex(keyCombo_->count() - 1);
        keyManager_->saveProfiles();
    }
}


//-----------------------------------------------------
// Key manager
//-----------------------------------------------------

void MvQAbstractMessageExaminer::slotManageKeyProfiles()
{
    saveKeyProfiles(false);

    QString dialogTitle = "Metview";

    MvQKeyManager::KeyType type;
    if (messageType_ == "GRIB") {
        type = MvQKeyManager::GribType;
#ifdef ECCODES_UI
        dialogTitle = "Grib Examiner";
#endif
    }
    else if (messageType_ == "BUFR") {
        type = MvQKeyManager::BufrType;
#ifdef ECCODES_UI
        dialogTitle = "Bufr Examiner";
#endif
    }
    else {
        return;
    }

    // Create a tmp copy of the whole manager (no data
    // values, just key descriptions)
    MvQKeyManager* tmpManager = keyManager_->clone();

    MvQKeyDialog profDialog(dialogTitle + " - " + messageType_ + tr(" Key Profile Manager"), type, tmpManager, keyCombo_->currentIndex(), allKeys_);

    if (profDialog.exec() == QDialog::Accepted) {
        // Save the current profile name
        std::string currentProfName = keyCombo_->currentText().toStdString();
#if 0
		messageModel_->keyProfileIsAboutToChange();
		messageModel_->setKeyProfile(0);
#endif
        keyManager_->update(tmpManager);
        keyManager_->saveProfiles();

        disconnect(keyCombo_, SIGNAL(currentIndexChanged(int)), nullptr, nullptr);

        // Clear the profile list
        keyCombo_->clear();

        // resetMessageHeader();

        int i = 0;
        int index = -1;
        for (auto it = keyManager_->data().begin(); it != keyManager_->data().end(); it++) {
            keyCombo_->addItem(QString((*it)->name().c_str()));
            if ((*it)->name() == currentProfName) {
                index = i;
            }
            if (keyManager_->data().at(i)->systemProfile()) {
                keyCombo_->setItemIcon(i, MvQTheme::logo());
            }

            i++;
        }

        if (index != -1) {
            keyCombo_->setCurrentIndex(index);
        }
        else {
            keyCombo_->setCurrentIndex(0);
        }

        Q_ASSERT(keyCombo_->currentIndex() >= 0 &&
                 keyCombo_->currentIndex() < static_cast<int>(keyManager_->data().size()));

        // We load the profile directly
        loadKeyProfile(keyManager_->data().at(keyCombo_->currentIndex()));

        connect(keyCombo_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(slotLoadKeyProfile(int)));
    }

    delete tmpManager;
}

void MvQAbstractMessageExaminer::saveKeyProfiles(bool saveToDisk)
{
    bool diff = false;
    if (MvKeyProfile* prof = loadedKeyProfile()) {
        if (MvKeyProfile* ap = keyManager_->findProfile(prof->name())) {
            if (*ap != *prof) {
                keyManager_->changeProfile(prof->name(), prof);
                diff = true;
            }
        }
    }

    if (saveToDisk || diff)
        keyManager_->saveProfiles();
}

void MvQAbstractMessageExaminer::clearLog()
{
    logPanel_->clearLog();
}

void MvQAbstractMessageExaminer::slotClearFilePanel(bool)
{
#ifdef ECCODES_UI
    fileList_->clear();
    slotLoadFile("");
#endif
}

void MvQAbstractMessageExaminer::slotCloseFilePanel(bool)
{
#ifdef ECCODES_UI
    // if(ignoreSidebarAction_==true)
    //     return;

    filePanel_->hide();
    actionFilePanel_->setChecked(false);

    // ignoreSidebarAction_=false;
#endif
}

void MvQAbstractMessageExaminer::slotAddFile(bool)
{
    QString startDir;
    if (fileList_->count() > 0) {
        startDir = fileList_->path(0);
        QFileInfo fi(startDir);
        startDir = fi.dir().path();
    }
#ifdef _UI_MVQABSTRACTMESSAGEEXAMINER_DEBUG
    qDebug() << "startDir" << startDir;
#endif
    MvQFileDialog dialog(startDir, QObject::tr("Add file to examiner"), this);
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
    dialog.setFileMode(QFileDialog::ExistingFile);
    dialog.setLabelText(QFileDialog::Accept, "Ok");

    if (dialog.exec() == QDialog::Accepted) {
        QStringList lst = dialog.selectedFiles();
        if (lst.count() > 0) {
            QString f = lst[0];
            QString err;
            if (fileList_->isPresent(f))
                filePanel_->selectFile(f);
            else if (fileList_->add(f, err)) {
                filePanel_->selectFile(f);
            }
            else if (!err.isEmpty()) {
                QMessageBox::critical(this, "Could not add file", err);
            }
        }
    }
}

void MvQAbstractMessageExaminer::slotSocketMessage(QString msg)
{
#ifdef ECCODES_UI
    qDebug() << "MvQAbstractMessageExaminer::slotSocketMessage -->";
    qDebug() << "  message" << msg;
    msg.remove(QChar('\n'));
    if (msg.startsWith("add ")) {
        QString last;
        foreach (QString f, msg.mid(4).split(" ")) {
            if (!f.isEmpty()) {
                QString err;
                if (fileList_->add(f, err)) {
                    last = f;
                    qDebug() << "  Added file" << f;
                }
            }
        }

        if (!last.isEmpty()) {
            filePanel_->selectFile(last);
            raise();
        }
    }
#endif
}

QString MvQAbstractMessageExaminer::currentKeyProfileName()
{
    return keyCombo_->currentText();
}

void MvQAbstractMessageExaminer::updateWinTitle(QString fname)
{
    QFileInfo info(fname);
    if (!info.fileName().isEmpty())
        setWindowTitle(info.fileName() + " - " + winTitleBase_);
    else
        setWindowTitle(winTitleBase_);
}

#ifdef ECCODES_UI
void MvQAbstractMessageExaminer::slotUiStyleSelect(QAction* ac)
{
    if (ac) {
        MvQTheme::writeCodesUiStyleName(ac->data().toString());
        QMessageBox::information(this, "codes_ui", tr("Please restart codes_ui to apply the Ui Theme change."));
    }
}
#endif

void MvQAbstractMessageExaminer::writeSettings(QSettings& settings)
{
    settings.beginGroup("core");
    settings.setValue("geometry", saveGeometry());
    settings.setValue("centralSplitter", centralSplitter_->saveState());
    settings.setValue("mainSplitter", mainSplitter_->saveState());
    settings.setValue("actionFileInfoStatus", actionFileInfo_->isChecked());
    settings.setValue("keyProfileName", keyCombo_->currentText());
    settings.setValue("actionLogStatus", actionLog_->isChecked());
    settings.setValue("sourceFileName", sourceFileName_);

#ifdef ECCODES_UI
    settings.setValue("fileList", fileList_->paths());
    settings.setValue("actionFilePanel", actionFilePanel_->isChecked());
#endif
    settings.endGroup();

    Q_ASSERT(logPanel_);
    logPanel_->writeSettings(settings);
}

void MvQAbstractMessageExaminer::readSettings(QSettings& settings)
{
    settings.beginGroup("core");
    restoreGeometry(settings.value("geometry").toByteArray());
    if (settings.contains("centralSplitter")) {
        centralSplitter_->restoreState(settings.value("centralSplitter").toByteArray());
    }
    else {
#ifdef ECCODES_UI
        // Set a proper initial size for the splitter widgets
        Q_ASSERT(centralSplitter_->count() == 2);
        int len = width() - 20;
        QList<int> sizes;
        sizes << len / 5 << len * 4 / 5;
        qDebug() << sizes;
        centralSplitter_->setSizes(sizes);
#endif
    }

    mainSplitter_->restoreState(settings.value("mainSplitter").toByteArray());
    // settings.endGroup();

    if (settings.value("actionFileInfoStatus").isNull()) {
        actionFileInfo_->setChecked(true);
    }
    else {
        actionFileInfo_->setChecked(settings.value("actionFileInfoStatus").toBool());
    }

    if (settings.value("actionLogStatus").isNull()) {
        // Hidden by default
        actionLog_->setChecked(false);
    }
    else {
        actionLog_->setChecked(settings.value("actionLogStatus").toBool());
    }

#ifdef ECCODES_UI
    if (settings.value("actionFilePanel").isNull()) {
        actionFilePanel_->setChecked(true);
    }
    else {
        actionFilePanel_->setChecked(settings.value("actionFilePanel").toBool());
    }
#endif
    // Init key combo
    // It is empty at this stage!!
    savedKeyProfileName_ = settings.value("keyProfileName").toString();

    // The last loaded file
    sourceFileName_ = settings.value("sourceFileName").toString();

#ifdef ECCODES_UI
    // The last loaded file list
    if (!fileList_) {
        if (type_ == GribType)
            fileList_ = new MvQFileList(MvQFileList::GribFile);
        else if (type_ == BufrType)
            fileList_ = new MvQFileList(MvQFileList::BufrFile);

        Q_ASSERT(fileList_);
        QStringList fLst = settings.value("fileList").toStringList();
        foreach (QString fPath, fLst) {
            QString err;
            fileList_->add(fPath, err, false);
        }
    }

#endif

    settings.endGroup();

    Q_ASSERT(logPanel_);
    logPanel_->readSettings(settings);

#ifdef ECCODES_UI
    auto styleName = MvQTheme::id();
    for (auto ac : styleAg_->actions()) {
        if (ac->data().toString() == styleName) {
            ac->setChecked(true);
            break;
        }
    }
#endif
}
