/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2020 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 <QObject>
#include <QDebug>
#include <QWidget>
#include <QMessageBox>
#include <QSettings>
#include <QLineEdit>
#include <QCloseEvent>


/////////////////////// Local includes
#include "MzIntegrationParamsDlg.hpp"
#include "Application.hpp"


namespace msxps
{
namespace minexpert
{


MzIntegrationParamsDlg::MzIntegrationParamsDlg(QWidget *parent)
  : QDialog(parent)
{
  if(parent == nullptr)
    qFatal("Programming error.");

  m_ui.setupUi(this);

  setupWidget();
}


MzIntegrationParamsDlg::MzIntegrationParamsDlg(
  QWidget *parent,
  const pappso::MzIntegrationParams &mz_integration_params,
  const QColor &color)
  : QDialog(parent),
    m_mzIntegrationParams(mz_integration_params),
    m_color(color)
{
  if(parent == nullptr)
    qFatal("Programming error.");

  // qDebug().noquote() << "The mz integration params at dialog construction:"
  //<< m_mzIntegrationParams.toString();

  m_ui.setupUi(this);

  setupWidget();
}


MzIntegrationParamsDlg::~MzIntegrationParamsDlg()
{
  // qDebug();
  writeSettings();
}


void
MzIntegrationParamsDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  // qDebug();

  writeSettings();

  emit mzIntegrationParamsDlgShouldBeDestroyedSignal();
}


//! Write the settings to as to restore the window geometry later.
void
MzIntegrationParamsDlg::writeSettings()
{
  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MzIntegrationParamsDlg");

  settings.setValue("geometry", saveGeometry());

  settings.endGroup();
}


//! Read the settings to as to restore the window geometry.
void
MzIntegrationParamsDlg::readSettings()
{
  QSettings settings(static_cast<Application *>(QCoreApplication::instance())
                       ->getUserConfigSettingsFilePath(),
                     QSettings::IniFormat);
  settings.beginGroup("MzIntegrationParamsDlg");

  restoreGeometry(settings.value("geometry").toByteArray());

  settings.endGroup();
}


void
MzIntegrationParamsDlg::setupWidget()
{

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

  QPalette pal = palette();
  pal.setColor(QPalette::Window, m_color);
  m_ui.coloredLine->setAutoFillBackground(true);
  m_ui.coloredLine->setPalette(pal);

  // Now update all the values in the different controls.

  m_ui.decimalPlacesSpinBox->setValue(m_mzIntegrationParams.getDecimalPlaces());

  // The precision widget required to specify the precursor m/z values
  // search tolerance.
  mp_precisionWidget = new pappso::PrecisionWidget(this);
  m_ui.precisionWidgetHorizontalLayout->addWidget(mp_precisionWidget);
  mp_precisionWidget->setToolTip("Set the size of the m/z bins");
  mp_precisionWidget->setPrecision(m_mzIntegrationParams.getPrecision());

  connect(mp_precisionWidget,
          &pappso::PrecisionWidget::precisionChanged,
          [this](pappso::PrecisionPtr precision_p) {
            mp_precision = precision_p;
            m_ui.arbitraryBinningRadioButton->setChecked(true);
          });

  m_ui.noBinningRadioButton->setChecked(m_mzIntegrationParams.getBinningType() ==
                                        pappso::BinningType::NONE);
  m_ui.arbitraryBinningRadioButton->setChecked(
    m_mzIntegrationParams.getBinningType() == pappso::BinningType::ARBITRARY);

  connect(m_ui.arbitraryBinningRadioButton,
          &QRadioButton::toggled,
          [this](bool checked) { mp_precisionWidget->setEnabled(checked); });

  m_ui.removeZeroValueCheckBox->setCheckState(
    m_mzIntegrationParams.isRemoveZeroValDataPoints() ? Qt::Checked
                                                    : Qt::Unchecked);

  /********* Savitzky-Golay parameters **********/

  m_ui.savitzkyGolayGroupBox->setChecked(
    m_mzIntegrationParams.isApplySavGolFilter());

  m_ui.nlSpinBox->setValue(m_mzIntegrationParams.getSavGolParams().nL);
  m_ui.nrSpinBox->setValue(m_mzIntegrationParams.getSavGolParams().nR);
  m_ui.mSpinBox->setValue(m_mzIntegrationParams.getSavGolParams().m);
  m_ui.ldSpinBox->setValue(m_mzIntegrationParams.getSavGolParams().lD);

  connect(m_ui.applyPushButton,
          &QPushButton::clicked,
          this,
          &MzIntegrationParamsDlg::applyPushButtonClicked);

  readSettings();
}


void
MzIntegrationParamsDlg::binningRadioButtonToggled(bool checked)
{
  QRadioButton *radioButton = static_cast<QRadioButton *>(QObject::sender());

  if(radioButton == m_ui.noBinningRadioButton)
    {
      if(checked == true)
        m_mzIntegrationParams.setBinningType(pappso::BinningType::NONE);
    }
  else if(radioButton == m_ui.arbitraryBinningRadioButton)
    {
      if(checked == true)
        m_mzIntegrationParams.setBinningType(pappso::BinningType::ARBITRARY);
    }
  else
    qFatal("Programming error.");
}


QString
MzIntegrationParamsDlg::toString() const
{
  return m_mzIntegrationParams.toString();
}


void
MzIntegrationParamsDlg::setBinningType(pappso::BinningType binningType)
{
  if(binningType == pappso::BinningType::NONE)
    m_ui.noBinningRadioButton->setChecked(true);
  else if(binningType == pappso::BinningType::ARBITRARY)
    m_ui.arbitraryBinningRadioButton->setChecked(true);
  else
    qFatal("Programming error.");
}


void
MzIntegrationParamsDlg::setBinSizePrecisionPtr(
  pappso::PrecisionPtr bin_size_precsion_p)
{
  mp_precisionWidget->setPrecision(bin_size_precsion_p);
}


void
MzIntegrationParamsDlg::setRemoveZeroValDataPoints(bool apply)
{
  m_ui.removeZeroValueCheckBox->setChecked(apply);
}


void
MzIntegrationParamsDlg::setDecimalPlaces(int decimal_places)
{
  m_ui.decimalPlacesSpinBox->setValue(decimal_places);
}


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

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

      close();
    }
}

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

  QDialog::show();
}


void
MzIntegrationParamsDlg::applyPushButtonClicked()
{
  // qDebug();

  // We need to check all the widgets, extract their information and then craft
  // a pappso::MzIntegrationParams instance that we'll pass to any widget (the trace
  // plot composite widget) that might be listening. We make a new instance by
  // copying the member instance because that member instance has data that are
  // not configured in this dialog. We would loose those data if we created a
  // default mz integration params instance.

  pappso::MzIntegrationParams mz_integration_params(m_mzIntegrationParams);

  // qDebug().noquote() << "Before reading all the widgets' values::"
  //<< mz_integration_params.toString();

  if(m_ui.noBinningRadioButton->isChecked())
    mz_integration_params.setBinningType(pappso::BinningType::NONE);
  else if(m_ui.arbitraryBinningRadioButton->isChecked())
    mz_integration_params.setBinningType(pappso::BinningType::ARBITRARY);

  mz_integration_params.setPrecision(mp_precision);

  mz_integration_params.setDecimalPlaces(m_ui.decimalPlacesSpinBox->value());

  mz_integration_params.setRemoveZeroValDataPoints(m_ui.removeZeroValueCheckBox->isChecked());

  if(m_ui.savitzkyGolayGroupBox->isChecked())
    {
      // qDebug();
      mz_integration_params.setApplySavGolFilter(true);
      mz_integration_params.setSavGolParams(m_ui.nlSpinBox->value(),
                                            m_ui.nrSpinBox->value(),
                                            m_ui.mSpinBox->value(),
                                            m_ui.ldSpinBox->value());
    }

  m_mzIntegrationParams = mz_integration_params;

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

  emit mzIntegrationParamsChangedSignal(m_mzIntegrationParams);
}


} // namespace minexpert

} // namespace msxps
