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


/////////////////////// StdLib includes


/////////////////////// Qt includes
#include <QSettings>
#include <QMenuBar>
#include <QMenu>
#include <QDebug>
#include <QVector>
#include <QCloseEvent>


/////////////////////// pappsomspp includes
#include <pappsomspp/core/utils.h>


/////////////////////// libXpertMassCore includes


/////////////////////// libXpertMassGui includes
#include <MsXpS/libXpertMassGui/WindowGeometryManager.hpp>


/////////////////////// Local includes
#include "../nongui/MsFragmentationSpec.hpp"
#include "Application.hpp"
#include "BaseTracePlotCompositeWidget.hpp"
#include "MsFragmentationSpecDlg.hpp"
#include "ui_MsFragmentationSpecDlg.h"

namespace MsXpS
{
namespace MineXpert
{


//! Construct an MsFragmentationSpecDlg instance.
MsFragmentationSpecDlg::MsFragmentationSpecDlg(QWidget *parent)
  : QDialog(parent), mp_ui(new ::Ui::MsFragmentationSpecDlg)
{
  if(parent == Q_NULLPTR)
    qFatal() << "Programming error.";

  mp_ui->setupUi(this);
}

MsFragmentationSpecDlg::MsFragmentationSpecDlg(
  QWidget *parent,
  const MsFragmentationSpec &ms_fragmentation_spec,
  const QColor &color)
  : QDialog(parent),
    mp_ui(new ::Ui::MsFragmentationSpecDlg),
    m_msFragmentationSpec(ms_fragmentation_spec),
    m_color(color)
{
  if(parent == nullptr)
    qFatal() << "Programming error.";

  // qDebug().noquote() << "The ms fragmentation spec at dialog construction:"
  //<< m_msFragmentationSpec.toString();

  mp_ui->setupUi(this);

  setupWidget();
}

//! Destruct \c this MsFragmentationSpecDlg instance.
MsFragmentationSpecDlg::~MsFragmentationSpecDlg()
{
  writeSettings();
}

//! Handle the close event.
void
MsFragmentationSpecDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  writeSettings();

  emit msFragmentationSpecDlgShouldBeDestroyedSignal();
}

//! Write the settings to as to restore the window geometry later.
void
MsFragmentationSpecDlg::writeSettings()
{
  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  qDebug() << "Saving window geometry.";

  libXpertMassGui::WindowGeometryManager::save(
    dynamic_cast<const QWidget *>(this), "MsFragmentationSpecDlg", settings);

  settings.beginGroup("MsFragmentationSpecDlg");

  // Awaiting settings...

  settings.endGroup();
}

//! Read the settings to as to restore the window geometry.
void
MsFragmentationSpecDlg::readSettings()
{
  QString settings_file_path =
    static_cast<Application *>(QCoreApplication::instance())
      ->getUserConfigSettingsFilePath();

  QSettings settings(settings_file_path, QSettings::IniFormat);

  qDebug() << "Restoring window geometry.";

  libXpertMassGui::WindowGeometryManager::restore(
    this, "MsFragmentationSpecDlg", settings);

  settings.beginGroup("MsFragmentationSpecDlg");

  // Awaiting settings...

  settings.endGroup();
}

void
MsFragmentationSpecDlg::setupWidget()
{

  // Set the right color to the line "tag" to help the user determine for which
  // graph the parameters were initialized.

#if 0
  // This was the gradient version that I find too heavy on the eye

  // This one is the top color in the groupBox and is deemed to be more intense
  QColor hsv_stop1_color = m_color.toHsv();
  hsv_stop1_color.setHsv(hsv_stop1_color.hsvHue(),
                         hsv_stop1_color.hsvSaturation(),
                         hsv_stop1_color.value());

  // This one is the bottom color in the groupBox and is deemed to be more light
  QColor hsv_stop2_color = m_color.toHsv();
  hsv_stop2_color.setHsv(hsv_stop2_color.hsvHue(),
                         hsv_stop2_color.hsvSaturation() * 0.1,
                         hsv_stop2_color.value());

  QString color_stop1_hexrgb_name = hsv_stop1_color.name();
  // qDebug() << color_stop1_hexrgb_name;
  QString color_stop2_hexrgb_name = hsv_stop2_color.name();
  // qDebug() << color_stop2_hexrgb_name;

  QString style = QString(
                    "QGroupBox {"
                    "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, "
                    "y2: 1, stop: 0 %1, stop: 1 %2);"
                    "border: 2px solid gray;"
                    "}")
                    .arg(color_stop1_hexrgb_name)
                    .arg(color_stop2_hexrgb_name);
#endif

  QString style =
    QString("QGroupBox {border: 2px solid %1;}").arg(m_color.name());
  mp_ui->msLevelGroupBox->setStyleSheet(style);


  // The precision widget required to specify the precursor m/z values
  // search tolerance.
  mp_precisionWidget = new pappso::PrecisionWidget(this);
  mp_ui->precisionWidgetHorizontalLayout->addWidget(mp_precisionWidget);
  mp_precisionWidget->setToolTip(
    "Set the tolerance with which the m/z value match is performed.");

  connect(mp_precisionWidget,
          &pappso::PrecisionWidget::precisionChanged,
          [this](pappso::PrecisionPtr precision_p) {
            mp_precision = precision_p;
          });

  // When the user clicks Apply, the member specification is updated with the
  // data from the widgets.
  connect(mp_ui->applyPushButton, &QPushButton::clicked, [this]() {
    applyPushButtonClicked();
  });

  // Set the ms fragmentation spec's data in the widget.

  mp_ui->msLevelSpinBox->setValue(m_msFragmentationSpec.getMsLevel());

  mp_precisionWidget->setPrecision(m_msFragmentationSpec.getPrecisionPtr());

  mp_ui->precursorMzPlainTextEdit->setPlainText(
    m_msFragmentationSpec.mzPrecursorsToString());
  mp_ui->precursorSpectrumIndexPlainTextEdit->setPlainText(
    m_msFragmentationSpec.precursorSpectrumIndicesToString());

  readSettings();
}

QString
MsFragmentationSpecDlg::toString() const
{
  return m_msFragmentationSpec.toString();
}

void
MsFragmentationSpecDlg::keyPressEvent(QKeyEvent *event)
{
  // If Ctrl return, apply and close.

  if(event->key() == Qt::Key_Return && event->modifiers() & Qt::ControlModifier)
    {
      if(applyPushButtonClicked())
        close();
    }
}

void
MsFragmentationSpecDlg::show()
{
  readSettings();

  QDialog::show();
}

void
MsFragmentationSpecDlg::clearAllPushButtonClicked(void)
{
  mp_ui->msLevelSpinBox->setValue(0);
  mp_ui->precursorMzPlainTextEdit->setPlainText("");
  mp_ui->precursorSpectrumIndexPlainTextEdit->setPlainText("");
}

bool
MsFragmentationSpecDlg::validateInputData()
{
  // qDebug();

  MsFragmentationSpec ms_fragmentation_spec;

  // Let's dig into all the data and fill-in the fields...

  std::size_t ms_level = mp_ui->msLevelSpinBox->value();

  ms_fragmentation_spec.m_msLevel = ms_level;

  QString debug_string;

#if 0
  QString debug_string = QString("The ms levels set by the user - size: %1 - ")
                           .arg(ms_fragmentation_spec.m_msLevels.size());
  debug_string += QString("vector members: ");

  for(auto &&item : ms_fragmentation_spec.m_msLevels)
    debug_string += QString("%1, ").arg(item);

	qDebug() << debug_string;

  debug_string.clear();

#endif


  // We want to store the number of errors that might be encountered when
  // parsing the various text widgets' contents.
  std::size_t error_count = 0;

  if(mp_ui->precursorMzValuesGroupBox->isChecked())
    {
      // qDebug();

      std::vector<double> mz_vector =
        pappso::Utils::splitMzStringToDoubleVectorWithSpaces(
          mp_ui->precursorMzPlainTextEdit->toPlainText(), error_count);

      if(error_count)
        {
          mp_ui->statusLineEdit->setText(
            "Please check the precursors list: error(s) encountered");

          // Leave the status message for 2 seconds and then erase it.
          QTimer::singleShot(2000, [this]() {
            mp_ui->statusLineEdit->setText("");
          });

          return false;
        }

      ms_fragmentation_spec.m_precursorMzValues = mz_vector;

      // At this point, make sure we have the tolerance set to the proper
      // value.

      ms_fragmentation_spec.setPrecisionPtr(mp_precisionWidget->getPrecision());
    }
  else
    {
      ms_fragmentation_spec.m_precursorMzValues.clear();
    }

#if 0

  debug_string = QString("The m/z precursors set by the user - size: %1 - ")
                   .arg(ms_fragmentation_spec.m_precursorMzValues.size());
  debug_string += QString("vector members: ");

  for(auto &&item : ms_fragmentation_spec.m_precursorMzValues)
    debug_string += QString("%1, ").arg(item, 0, 'f', 6);

  qDebug() << debug_string;

  debug_string.clear();

#endif

  if(mp_ui->precursorSpectraIndicesGroupBox->isChecked())
    {
      error_count = 0;

      std::vector<std::size_t> precursor_spectrum_indices =
        pappso::Utils::splitSizetStringToSizetVectorWithSpaces(
          mp_ui->precursorSpectrumIndexPlainTextEdit->toPlainText(),
          error_count);

      if(error_count)
        {
          mp_ui->statusLineEdit->setText(
            "Please check the precursor spectrum index list: error(s) "
            "encountered");

          // Leave the status message for 2 seconds and then erase it.
          QTimer::singleShot(2000, [this]() {
            mp_ui->statusLineEdit->setText("");
          });

          return false;
        }

      ms_fragmentation_spec.m_precursorSpectrumIndices =
        precursor_spectrum_indices;
    }
  else
    {
      ms_fragmentation_spec.m_precursorSpectrumIndices = {};
    }

#if 0

  debug_string =
    QString("The precursor spectrum indices set by the user - size: %1 - ")
      .arg(ms_fragmentation_spec.m_precursorSpectrumIndices.size());
  debug_string += QString("vector members: ");

  for(auto &&item : ms_fragmentation_spec.m_precursorSpectrumIndices)
    debug_string += QString("%1, ").arg(item);

  qDebug() << debug_string;

#endif

  // At this point, set the frag spec to the member datum.

  m_msFragmentationSpec = ms_fragmentation_spec;

  return true;
}

bool
MsFragmentationSpecDlg::applyPushButtonClicked()
{
  if(!validateInputData())
    return false;

  static_cast<BaseTracePlotCompositeWidget *>(parent())
    ->msFragmentationSpecReady(m_msFragmentationSpec);

  mp_ui->statusLineEdit->setText("Validation successful.");

  // Leave the status message for 2 seconds and then erase it.
  QTimer::singleShot(2000, [this]() {
    mp_ui->statusLineEdit->setText("");
  });

  // qDebug().noquote() << "The newly set parameters:" << toString();

  emit msFragmentationSpecChangedSignal(m_msFragmentationSpec);

  return true;
}


} // namespace MineXpert

} // namespace MsXpS
