/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QMessageBox>
#include <QDebug>
#include <QSettings>
#include <QDir>
#include <QFileDialog>

/////////////////////// Local includes
#include "config.h"
#include "gui/ConfigSettingsDlg.hpp"
#include "nongui/ConfigSetting.hpp"


namespace MsXpS
{

namespace massxpert
{


  ConfigSettingsDlg::ConfigSettingsDlg(const QString &applicationName,
                                       ConfigSettings *configSettings,
                                       const QString &configSettingsFilePath,
                                       bool failedConfig)
    : m_applicationName{applicationName},
      mp_configSettings{configSettings},
      m_configSettingsFilePath{configSettingsFilePath}
  {
    if(!mp_configSettings)
      qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

    if(!initialize(failedConfig))
      {
        qDebug() << "Failed to initialize the "
                    "config settings dialog window";
      }
  }


  void
  ConfigSettingsDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
  {
    QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
    settings.beginGroup("ConfigSettingsDlg");
    settings.setValue("geometry", saveGeometry());
    settings.endGroup();
  }


  ConfigSettingsDlg::~ConfigSettingsDlg()
  {
  }


  bool
  ConfigSettingsDlg::initialize(bool failedConfig)
  {
    m_ui.setupUi(this);

    // This dialog window might be brought up either by the user proactively
    // willing to set some configuration ; or it might be brought up
    // automatically upon running of <applicationName> because some configuration
    // setting failed. Depending on the situation, the explanatory text
    // changes.

    QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
    settings.beginGroup("configSettingsDlg");
    restoreGeometry(settings.value("geometry").toByteArray());
    settings.endGroup();

    // At this point set the General concepts text depending on the
    // platform.


    QString text =
      QString(
        "The msXpertSuite software program's %1 module's components "
        "have been built and should be located in "
        "the following system places:\n\n"
        "- the binary program in %2\n"
        "- the data in %3\n"
        "- the user documentation in %4\n\n\n")
        .arg(m_applicationName)
        .arg(QString("%1").arg(BIN_DIR))
        .arg(QString("%1").arg(DATA_DIR))
        .arg(QString("%1").arg(DOC_DIR));

    if(failedConfig)
      text += QString(
                "However, it appears that the config on this system "
                "is not typical. The software package might have been "
                "relocated.\n\n"
                "You are given the opportunity to locate the msXpertSuite"
                "software program's %1 module's components' main directories.")
                .arg(m_applicationName);
    else
      text += QString(
                "You are however entitled to modify any configuration setting "
                "if the directories do not match the default locations.\n\n"
                "You are given the opportunity to locate the msXpertSuite"
                "software program's %1 module's components' main directories.")
                .arg(m_applicationName);

    m_ui.generalConceptsTextEdit->setText(text);

    // At this point we need to iterate in the ConfigSettings QMap and for
    // each item make sure that we have a proper widget showing all the
    // required sub-widgets.

    // We will need a splitter later on.
    mp_splitter = new QSplitter(Qt::Vertical, this);

    // Setup the scroll area where we'll pack all the widgets that we'll
    // create for each configuration setting.

    // Passing the widget as parent to the layout constructor is the same as
    // calling widget->setLayout(layout).
    mp_groupBoxGridLayout = new QGridLayout(m_ui.configSettingsGroupBox);
    mp_groupBoxGridLayout->setObjectName(
      QStringLiteral("mp_groupBoxGridLayout"));

    // Now that the main layout is set to the main group box widget, let's pack
    // the
    // scroll area in it. Upon adding the area to the layout, that area is
    // reparented to the layout that is responsible for its destruction.
    mp_scrollArea = new QScrollArea(m_ui.configSettingsGroupBox);
    mp_scrollArea->setObjectName(QStringLiteral("mp_scrollArea"));
    mp_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    mp_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    mp_scrollArea->setWidgetResizable(true);
    mp_scrollArea->setAlignment(Qt::AlignCenter);

    mp_scrollAreaWidgetContents = new QWidget();
    mp_scrollAreaWidgetContents->setObjectName(
      QStringLiteral("mp_scrollAreaWidgetContents"));
    // mp_scrollAreaWidgetContents->setGeometry(QRect(0, 0, 766, 525));

    mp_scrollAreaWidgetContentsGridLayout =
      new QGridLayout(mp_scrollAreaWidgetContents);
    mp_scrollAreaWidgetContentsGridLayout->setObjectName(
      QStringLiteral("mp_scrollAreaWidgetContentsGridLayout"));

    mp_scrollAreaVBoxLayout = new QVBoxLayout();
    mp_scrollAreaVBoxLayout->setObjectName(
      QStringLiteral("mp_scrollAreaVBoxLayout"));

    mp_scrollAreaWidgetContentsGridLayout->addLayout(
      mp_scrollAreaVBoxLayout, 0, 0, 1, 1);

    mp_scrollArea->setWidget(mp_scrollAreaWidgetContents);

    mp_groupBoxGridLayout->addWidget(mp_splitter, 0, 0, 1, 1);

    // At the moment we only add the scroll area where all the new widgets
    // will be stacked as configsettings are required.
    mp_splitter->addWidget(mp_scrollArea);


    const QList<ConfigSetting *> *valueList = mp_configSettings->values();

    for(int iter = 0; iter < valueList->size(); ++iter)
      {
        ConfigSetting *iterConfigSetting = valueList->at(iter);

        QGroupBox *widget =
          static_cast<QGroupBox *>(setupNewWidgetry(iterConfigSetting));

        mp_scrollAreaVBoxLayout->addWidget(widget);
      }


    connect(m_ui.saveSettingsPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(saveSettingsPushButtonClicked()));

    connect(m_ui.eraseSettingsPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(eraseSettingsPushButtonClicked()));

    connect(m_ui.cancelPushButton,
            SIGNAL(clicked()),
            this,
            SLOT(cancelPushButtonClicked()));

    return true;
  }


  QWidget *
  ConfigSettingsDlg::setupNewWidgetry(ConfigSetting *configSetting)
  {
    // We want to create a QGroupBox widget with a title and that should
    // contain all the necessary to configure the required bit.

    QGroupBox *groupBox = new QGroupBox;
    groupBox->setTitle(configSetting->m_title);
    groupBox->setCheckable(true);
    groupBox->setChecked(false);

    // Set this layout as the layout for the group box;
    QGridLayout *gridLayout = new QGridLayout(groupBox);

    QLineEdit *lineEdit = new QLineEdit;
    lineEdit->setText(configSetting->m_value.toString());
    gridLayout->addWidget(lineEdit);

    QPushButton *pushButton = new QPushButton;
    pushButton->setText("Browse to dir...");
    gridLayout->addWidget(pushButton);

    // Make sure we will be able to connect a given push button with the
    // corresponding line edit.
    m_lepbMap.insert(lineEdit, pushButton);

    // Make sure we'll know for what ConfigSetting instance pointer this line
    // edit widget was setup.
    m_lecsMap.insert(lineEdit, configSetting);

    // Make the connections.

    connect(pushButton,
            &QPushButton::clicked,
            this,
            &ConfigSettingsDlg::browseDirPushButtonClicked);

    return groupBox;
  }


  void
  ConfigSettingsDlg::browseDirPushButtonClicked()
  {
    QDir dir(QFileDialog::getExistingDirectory(
      this,
      tr("Locate the directory"),
      QString("%1/%2").arg(DATA_DIR).arg(m_applicationName),
      QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks));

#if 0
			if (!checkDataDir(dir))
			{
				QMessageBox::warning(this,
						tr("massXpert - Config Settings"),
						tr("%1@%2\n"
							"Failed to verify the consistency "
							"of the data directory.\n"
							"Please ensure that the package "
							"is installed correctly\n")
						.arg(__FILE__)
						.arg(__LINE__),
						QMessageBox::Ok);
				return;
			}
#endif

    // Get the mapped line edit:
    QLineEdit *le =
      m_lepbMap.key(static_cast<QPushButton *>(QObject::sender()));

    // Write to the line edit the dir name.
    le->setText(dir.absolutePath());

    // But now, also refresh the ConfigSetting item that corrsponds to this
    // line edit widget:

    ConfigSetting *cs = m_lecsMap.value(le);
    cs->m_value       = dir.absolutePath();
  }


  bool
  ConfigSettingsDlg::checkDataDir(const QDir &dir)
  {
    // At this point we must ensure that the
    // polChemDefs/massxpert-polChemDefsCat catalogue exists.

    QString filePath(dir.absolutePath() + QDir::separator() +
                     QString("polChemDefs") + QDir::separator() +
                     QString("massxpert-polChemDefsCat"));

    return QFile::exists(filePath);
  }


  void
  ConfigSettingsDlg::saveSettingsPushButtonClicked()
  {
    // We have to check that all the data are correct and we then set
    // the values to the m_configSettings passed as parameter.

    // Iterate in the map, and check that each line edit widget contains a
    // valid directory/file.

    QMapIterator<QLineEdit *, QPushButton *> lepbIterator(m_lepbMap);

    while(lepbIterator.hasNext())
      {
        lepbIterator.next();

        QLineEdit *le = lepbIterator.key();

        QString leText = le->text();
        if(leText.isEmpty())
          continue;

        QDir dir(leText);

        if(!dir.exists())
          {
            QMessageBox::warning(
              this,
              tr("massXpert - Config settings"),
              tr("%1@%2\n"
                 "The selected directory \n"
                 "(%3)\n"
                 "was not found.\n"
                 "Please ensure that the package is installed correctly\n")
                .arg(__FILE__)
                .arg(__LINE__)
                .arg(dir.absolutePath()),
              QMessageBox::Ok);
            return;
          }
      }

    // OK, at this point we can store all these data in the
    // config settings for the current user using the
    // QSettings system and also store all the new values in the various
    // ConfigSetting * instances of the ConfigSetting object of which we got a
    // pointer upon construction of this dialog.

    QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

    // Iterate in the other map:
    QMapIterator<QLineEdit *, ConfigSetting *> lecsIterator(m_lecsMap);

    while(lecsIterator.hasNext())
      {
        lecsIterator.next();

        ConfigSetting *cs = lecsIterator.value();

        // Store the new dir name to the ConfigSetting:
        QLineEdit *le = lecsIterator.key();

        // Now update the QSettings:
        if(cs->m_userType == UserType::USER_TYPE_SYSTEM)
          settings.beginGroup("system_data_config_settings");
        else
          settings.beginGroup("user_data_config_settings");

        // Get the text from the line edit widget
        QString leText = le->text();

        if(leText.isEmpty())
          {
            // The user does not want that config setting.
            settings.remove(cs->m_key);
          }
        else
          {
            // Yes, we can update the value of the config setting.
            cs->m_value = le->text();

            // Same for the QSetting.
            settings.setValue(cs->m_key, cs->m_value);
          }

        // Now close the group, because otherwise we do concatenate the keys of
        // the various iterations...
        settings.endGroup();
      }
  }


  void
  ConfigSettingsDlg::eraseSettingsPushButtonClicked()
  {
    // Get a list of all the group boxes that have been packed into the scroll
    // area. At the moment group boxes packed in there are all for
    // configuration of diretories, so we are safe.
    QList<QGroupBox *> gbList = mp_scrollArea->findChildren<QGroupBox *>();

    if(gbList.size() == 0)
      return;

    QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);

    QGroupBox *gb;

    foreach(gb, gbList)
      {
        if(gb->isChecked())
          {
            // We need to remove the corresponding config setting from the
            // QSettings file.

            QLineEdit *le = gb->findChild<QLineEdit *>();

            ConfigSetting *cs = m_lecsMap.value(le);

            qDebug() << __FILE__ << __LINE__ << "cs: " << cs->m_key;

            if(cs->m_userType == UserType::USER_TYPE_SYSTEM)
              settings.beginGroup("system_data_config_settings");
            else
              settings.beginGroup("user_data_config_settings");

            settings.remove(cs->m_key);

            // Now close the group, because otherwise we do concatenate the keys
            // of
            // the various iterations...
            settings.endGroup();

            // Now, clear the corresponding line edit widget.
            le->clear();
          }
      }
  }


  void
  ConfigSettingsDlg::cancelPushButtonClicked()
  {
    // This will let the caller know that nothing should be done.
    reject();
  }

} // namespace massxpert

} // namespace MsXpS
