/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2026 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 "ProgramWindow.hpp"
#include "config.h"
#include "Application.hpp"
#include "FileSystemHierarchyPreferencesWidget.hpp"
#include "nongui/ConfigSetting.hpp"
#include "ApplicationPreferencesWnd.hpp"


namespace MsXpS
{

namespace MassXpert
{


FileSystemHierarchyPreferencesWidget::FileSystemHierarchyPreferencesWidget(
  const QString &application_name, QWidget *parent)
  : QWidget(parent),
    m_applicationName(application_name),
    mp_ui(new Ui::FileSystemHierarchyPreferencesWidget)
{
  mp_ui->setupUi(this);
  setWindowIcon(qApp->windowIcon());

  msp_configSettings = static_cast<Application *>(QCoreApplication::instance())
                         ->getProgramWindow()
                         ->getConfigSettings();

  Q_ASSERT(msp_configSettings != nullptr);

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

FileSystemHierarchyPreferencesWidget::~FileSystemHierarchyPreferencesWidget()
{
  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  settings.beginGroup("FileSystemHierarchyPreferencesWidget");

  settings.setValue("splitter", mp_ui->splitter->saveState());

  settings.endGroup();
}

bool
FileSystemHierarchyPreferencesWidget::initializeGeneralConcepts(
  bool because_init_failed)
{
  // 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 <application_name> because some configuration
  // setting failed. Depending on the situation, the explanatory text
  // changes.

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

  QString text = QString(
                   "The %1 software 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(PROJECT_INSTALL_BIN_DIR))
                   .arg(QString("%1").arg(PROJECT_INSTALL_CHEMDATA_DIR))
                   .arg(QString("%1").arg(PROJECT_INSTALL_DOC_DIR));

  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  if(because_init_failed)
    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 %1 software "
              "components' directories.\n\n The software configuration will be "
              "stored in config file %2.\n")
              .arg(m_applicationName)
              .arg(settings_file_path);
  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 the %1 software "
              "components' main directories.\n\n The software configuration "
              "will be stored in config file %2.\n")
              .arg(m_applicationName)
              .arg(settings_file_path);

  mp_ui->generalConceptsTextEdit->setText(text);
  return true;
}

bool
FileSystemHierarchyPreferencesWidget::initialize(bool because_init_failed)
{
  // 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 <application_name> because some configuration
  // setting failed. Depending on the situation, the explanatory text
  // changes.
  initializeGeneralConcepts(because_init_failed);

  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  // 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.

  // What we get from the ui file is two group boxes:

  // mp_ui->generalConceptsGroupBox, where the textedit box displays the
  // general concepts about the file system hierarchy preferences

  // mp_ui->preferencesGroupBox, where all the different sub-widgets are going
  // to be packed dynamically.

  // There is mp_ui->splitter that splits the two group boxes above.

  // 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).

  // QGroupBox *generalConceptsGroupBox = mp_ui->generalConceptsGroupBox;

  // This is the group box where all the widgetry work will happen.
  QGroupBox *preferencesGroupBox = mp_ui->preferencesGroupBox;

  // Create vertical layout for the preferences group box
  mp_groupBoxVertLayout = new QVBoxLayout(preferencesGroupBox);

  // Now pack the scroll area into it.
  mp_scrollArea = new QScrollArea(preferencesGroupBox);
  mp_scrollArea->setHorizontalScrollBarPolicy(
    Qt::ScrollBarAsNeeded); // Changed from AlwaysOn
  mp_scrollArea->setVerticalScrollBarPolicy(
    Qt::ScrollBarAsNeeded); // Changed from AlwaysOn
  mp_scrollArea->setWidgetResizable(true);
  mp_scrollArea->setAlignment(Qt::AlignCenter);

  // Create the contents widget with proper parent
  mp_scrollAreaContentsWidget = new QWidget(mp_scrollArea);
  mp_scrollArea->setWidget(mp_scrollAreaContentsWidget);

  // Set the scroll area to the group box layout
  mp_groupBoxVertLayout->addWidget(mp_scrollArea);

  // Create layout for the contents widget
  mp_scrollAreaContentsWidgetVertLayout =
    new QVBoxLayout(mp_scrollAreaContentsWidget);

  QSettings settings(settings_file_path, QSettings::IniFormat);
  settings.beginGroup("FileSystemHierarchyPreferencesWidget");
  mp_ui->splitter->restoreState(settings.value("splitter").toByteArray());
  settings.endGroup();

  Q_ASSERT(msp_configSettings != nullptr);

  qDebug() << "Now setting up the widgetry for"
           << msp_configSettings->getConfigSettingsCstRef().size()
           << "settings.";

  fillInAllTheWidgetry();

  if(because_init_failed)
    {
      const_cast<ApplicationPreferencesWnd *>(
        static_cast<Application *>(QCoreApplication::instance())
          ->getProgramWindow()
          ->getApplicationPreferencesWnd())
        ->showSectionListItem(
          ApplicationPreferencesWnd::Pages::FILESYSTEM_HIERARCHY);
    }

  connect(mp_ui->eraseSettingsPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(eraseSettingsPushButtonClicked()));

  return true;
}

QWidget *
FileSystemHierarchyPreferencesWidget::setupNewWidgetry(
  ConfigSettingSPtr config_setting_sp)
{
  // 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(config_setting_sp->m_title);
  groupBox->setCheckable(true);
  groupBox->setChecked(false);

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

  QLineEdit *lineEdit = new QLineEdit;
  lineEdit->setText(config_setting_sp->m_value.toString());
  vBoxLayout->addWidget(lineEdit);

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

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

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

  // Make the connections.

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

  return groupBox;
}

void
FileSystemHierarchyPreferencesWidget::fillInAllTheWidgetry()
{
  if(msp_configSettings != nullptr)
    {
      qDebug() << "Filling in all the widgetry with"
               << msp_configSettings->getConfigSettingsCstRef().size()
               << "configuration settings.";

      for(const ConfigSettingSPtr &config_setting_sp :
          msp_configSettings->getConfigSettingsRef())
        {
          QGroupBox *widget =
            static_cast<QGroupBox *>(setupNewWidgetry(config_setting_sp));

          mp_scrollAreaContentsWidgetVertLayout->addWidget(widget);
        }
    }
}

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

#if 0
			if (!checkDataDir(dir))
			{
				QMessageBox::warning(this,
						tr("MassXpert3 - 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_lineEditPushButtonMap.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:

  ConfigSettingSPtr cs_sp = m_lineEditConfigSettingMap.value(le);
  cs_sp->m_value          = dir.absolutePath();
}


bool
FileSystemHierarchyPreferencesWidget::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("%1-polChemDefsCat").arg(m_applicationName));

  return QFile::exists(filePath);
}


QScrollArea *
FileSystemHierarchyPreferencesWidget::getMainScrollArea()
{
  return mp_ui->scrollArea;
}

void
FileSystemHierarchyPreferencesWidget::saveSettings()
{
  qDebug() << "Now saving the file system hierarchy preferences.";

  // We have to check that all the data are correct.

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

  QMapIterator<QLineEdit *, QPushButton *> line_edit_push_button_map_iterator(
    m_lineEditPushButtonMap);

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

      QLineEdit *line_edit_p = line_edit_push_button_map_iterator.key();

      QString line_edit_text = line_edit_p->text();
      if(line_edit_text.isEmpty())
        continue;

      QDir dir(line_edit_text);

      if(!dir.exists())
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - Filesystem hierarchy preferences"),
            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.

  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  qDebug() << "settings_file_path:" << settings_file_path;

  // Iterate in the other map:
  QMapIterator<QLineEdit *, ConfigSettingSPtr>
    line_edit_config_setting_map_iterator(m_lineEditConfigSettingMap);

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

      ConfigSettingSPtr config_setting_sp =
        line_edit_config_setting_map_iterator.value();

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

      // Now update the QSettings:
      if(config_setting_sp->m_userType == UserType::SYSTEM)
        settings.beginGroup("SystemFileSystemHierarchyPreferences");
      else
        settings.beginGroup("UserFileSystemHierarchyPreferences");

      // Get the text from the line edit widget
      QString line_edit_text = line_edit_p->text();

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

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

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


void
FileSystemHierarchyPreferencesWidget::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 *> group_box_list =
    mp_scrollArea->findChildren<QGroupBox *>();

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

  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  qDebug() << "settings_file_path:" << settings_file_path;

  QGroupBox *group_box_p;

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

          QLineEdit *line_edit_widget_p = group_box_p->findChild<QLineEdit *>();

          ConfigSettingSPtr config_setting_sp =
            m_lineEditConfigSettingMap.value(line_edit_widget_p);

          qDebug() << "config_setting_sp: " << config_setting_sp->m_key;

          if(config_setting_sp->m_userType == UserType::SYSTEM)
            settings.beginGroup("FileSystemHierarchyPreferences");
          else
            settings.beginGroup("UserFileSystemHierarchyPreferences");

          settings.remove(config_setting_sp->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.
          line_edit_widget_p->clear();
        }
    }
}

} // namespace MassXpert

} // namespace MsXpS
