/* 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 msXpSmassXpert
{

ConfigSettingsDlg::ConfigSettingsDlg(const QString &moduleName,
                                     ConfigSettings *configSettings,
                                     const QString &configSettingsFilePath,
                                     bool failedConfig)
  : m_moduleName{moduleName},
    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(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 <moduleName> 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_moduleName)
                   .arg(QString("%1").arg(MASSXPERT_BIN_DIR))
                   .arg(QString("%1").arg(MASSXPERT_DATA_DIR))
                   .arg(QString("%1").arg(MASSXPERT_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_moduleName);
  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_moduleName);

  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(MASSXPERT_DATA_DIR).arg(m_moduleName),
    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 == msXpS::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 == msXpS::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 msXpSmassXpert
