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


/////////////////////// StdLib includes
#include <cmath>


/////////////////////// Qt includes
#include <QPushButton>
#include <QTimer>
#include <QSettings>


/////////////////////// pappsomspp includes


/////////////////////// Local includes
#include "MsXpS/libXpertMassGui/ToleranceWidget.hpp"

#include "ui_ToleranceWidget.h"

namespace MsXpS
{

namespace libXpertMassGui
{


/*!
\class MsXpS::libXpertMassGui::ToleranceWidget
\inmodule libXpertMassGui
\ingroup XpertMassGuiMassCalculations
\inheaderfile ToleranceWidget.hpp

\brief The ToleranceWidget class provides a widget for the
configuration of the mass tolerance.

This composite widget contains all the widgets and all the logic required to
fully configure the mass peak tolerance.
*/

/*!
\variable MsXpS::libXpertMassGui::ToleranceWidget::mp_ui

\brief The graphical user interface definition.
*/

/*!
\brief Constructs a ToleranceWidget instance.

\list
\li \a parent_p: the parent widget.
\li \a config_settings_file_path: the QSettings configuration file path
\li \a nominal: the nominal libXpertMassCore::Tolerance value
\li \a type: the libXpertMassCore::Tolerance::Type type
\endlist
*/
ToleranceWidget::ToleranceWidget(QWidget *parent,
                                 const QString &config_settings_file_path,
                                 double nominal,
                                 libXpertMassCore::Tolerance::Type type)
  : QWidget(parent),
    m_configSettingsFilePath(config_settings_file_path),
    mp_tolerance(new libXpertMassCore::Tolerance(this)),
    mp_ui(new Ui::ToleranceWidget)
{
  qDebug() << "Creating ToleranceWidget";

  if(!parent)
    qFatal("Programming error. Program aborted.");

  mp_ui->setupUi(this);
  mp_ui->nominalDoubleSpinBox->setRange(0, 2000000);

  setupWidget(nominal, type);
}

ToleranceWidget::ToleranceWidget(QWidget *parent,
                                 const QString &config_settings_file_path,
                                 const libXpertMassCore::Tolerance &tolerance)
  : QWidget(parent),
    m_configSettingsFilePath(config_settings_file_path),
    mp_tolerance(new libXpertMassCore::Tolerance(this)),
    mp_ui(new Ui::ToleranceWidget)
{
  qDebug() << "Creating ToleranceWidget";

  if(!parent)
    qFatal("Programming error. Program aborted.");

  mp_ui->setupUi(this);
  mp_ui->nominalDoubleSpinBox->setRange(0, 2000000);

  setupWidget(tolerance);
}

/*!
\brief Constructs a ToleranceWidget instance.

\list
\li \a parent_p: the parent widget.
\li \a config_settings_file_path: the path to the configuration settings file
\endlist
*/
ToleranceWidget::ToleranceWidget(QWidget *parent_p,
                                 const QString &config_settings_file_path)
  : QWidget(parent_p),
    m_configSettingsFilePath(config_settings_file_path),
    mp_tolerance(new libXpertMassCore::Tolerance(this)),
    mp_ui(new Ui::ToleranceWidget)
{
  qDebug() << "Creating ToleranceWidget";

  if(!parent_p)
    qFatal("Programming error. Program aborted.");

  mp_ui->setupUi(this);
  mp_ui->nominalDoubleSpinBox->setRange(0, 2000000);

  setupWidget();
}

/*!
\brief Destructs this ToleranceWidget instance.
*/
ToleranceWidget::~ToleranceWidget()
{
  writeSettings(m_configSettingsFilePath);

  delete mp_ui;
}

/*!
\brief Writes the settings of this widget to \a config_settings_file_path for
later restoration.
*/
void
ToleranceWidget::writeSettings(const QString &config_settings_file_path)
{
  QSettings settings(config_settings_file_path, QSettings::IniFormat);

  settings.beginGroup("ToleranceWidget");

  settings.setValue("Tolerance", mp_tolerance->toString());

  settings.endGroup();
}

/*!
\brief Reads the settings of this widget from \a config_settings_file_path.

Tolerance settings are tried from the settings. If not found, the default values
for nominal and tolerance type are set.
*/
void
ToleranceWidget::readSettings(const QString &config_settings_file_path)
{
  QSettings settings(config_settings_file_path, QSettings::IniFormat);

  settings.beginGroup("ToleranceWidget");

  libXpertMassCore::Tolerance tolerance;
  QString default_tolerance_values = tolerance.toString();

  mp_tolerance->initialize(
    settings.value("Tolerance", default_tolerance_values).toString());

  emit toleranceChangedSignal(
    libXpertMassCore::Tolerance(*mp_tolerance, nullptr));

  settings.endGroup();
}

const libXpertMassCore::Tolerance *
ToleranceWidget::getTolerance() const
{
  return mp_tolerance;
}

const libXpertMassCore::Tolerance &
ToleranceWidget::getToleranceCstRef() const
{
  return *mp_tolerance;
}

/*!
\brief Sets the Tolerance nominal value to \a nominal.
*/
void
ToleranceWidget::setNominal(double nominal)
{
  if(nominal != mp_tolerance->getNominal())
    mp_tolerance->setNominal(nominal);
  toleranceChangedSignal(libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}

/*!
\brief Returns the Tolerance nominal value.
*/
double
ToleranceWidget::getNominal() const
{
  return mp_tolerance->getNominal();
}

/*!
\brief Sets the Tolerance type to \a type.
*/
void
ToleranceWidget::setType(libXpertMassCore::Tolerance::Type type)
{
  if(type != mp_tolerance->getType())
    mp_tolerance->setType(type);
  toleranceChangedSignal(libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}

/*!
\brief Returns the Tolerance type .
*/
libXpertMassCore::Tolerance::Type
ToleranceWidget::getType() const
{
  return mp_tolerance->getType();
}

/*!
\brief Initializes the libXpertMassCore::Tolerance member using \a nominal and
\a type.
*/
void
ToleranceWidget::initializeTolerance(double nominal,
                                     libXpertMassCore::Tolerance::Type type)
{
  mp_tolerance->setNominal(nominal);
  mp_tolerance->setType(type);
  toleranceChangedSignal(libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}

/*!
\brief Initializes the libXpertMassCore::Tolerance member using \a nominal and
\a type.
*/
void
ToleranceWidget::initializeTolerance(
  const libXpertMassCore::Tolerance &tolerance)
{
  mp_tolerance->initialize(tolerance);
  toleranceChangedSignal(libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}

/*!
\brief Initializes the libXpertMassCore::Tolerance member using values read from
the QSettings settings.

If the settings are not found, the libXpertMassCore::Tolerance member is left
untouched and has thus default values set in the class declaration.

Returns true if the settings were found, false otherwise.
*/
bool
ToleranceWidget::initializeToleranceFromSettings()
{
  readSettings(m_configSettingsFilePath);

  return true;
}

/*!
\brief Sets this widget up for the common data.
*/
void
ToleranceWidget::setupWidgetCommon()
{
  mp_ui->typeComboBox->clear();
  qDebug() << "The type combo box should be empty:"
           << mp_ui->typeComboBox->count();

  // First enum value is NOT_SET = 0x0.
  for(int iter = 1;
      iter < static_cast<int>(libXpertMassCore::Tolerance::Type::LAST);
      ++iter)
    mp_ui->typeComboBox->addItem(mp_tolerance->getTypeAsString(
      static_cast<libXpertMassCore::Tolerance::Type>(iter)));

  mp_ui->nominalDoubleSpinBox->setValue(mp_tolerance->getNominal());
  mp_ui->typeComboBox->setCurrentText(mp_tolerance->getTypeAsString());

  connect(mp_ui->nominalDoubleSpinBox,
          &QDoubleSpinBox::valueChanged,
          this,
          &ToleranceWidget::nominalDoubleSpinBoxValueChanged);

  connect(mp_ui->typeComboBox,
          &QComboBox::currentTextChanged,
          this,
          &ToleranceWidget::typeComboBoxValueChanged);
}

/*!
\brief Sets this widget up.

First the libXpertMassCore::Tolerance member is initialized from QSettings
settings, if possible. Then the values are set to the widgets.
*/
void
ToleranceWidget::setupWidget()
{
  initializeToleranceFromSettings();
  setupWidgetCommon();
}

/*!
\brief Sets this widget up using \a nominal and \a type.

The libXpertMassCore::Tolerance member is initialized using the arguments
and the values are set to the widgets.
*/
void
ToleranceWidget::setupWidget(double nominal,
                             libXpertMassCore::Tolerance::Type type)
{
  initializeTolerance(nominal, type);
  setupWidgetCommon();
}

/*!
\brief Sets this widget up using \a tolerance.

The libXpertMassCore::Tolerance member is initialized using the argument
and the values are set to the widgets.
*/
void
ToleranceWidget::setupWidget(const libXpertMassCore::Tolerance &tolerance)
{
  initializeTolerance(tolerance);
  setupWidgetCommon();
}

/*!
\brief Signals that the nominal value is now \a value.
*/
void
ToleranceWidget::nominalDoubleSpinBoxValueChanged(double value)
{
  mp_tolerance->setNominal(value);
  qDebug() << "Updated nominal value of Tolerance:"
           << mp_tolerance->getNominal();

  emit toleranceChangedSignal(
    libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}

/*!
\brief Signals that the Tolerance type is now described as \a text.
*/
void
ToleranceWidget::typeComboBoxValueChanged(const QString &text)
{
  mp_tolerance->setType(text);
  qDebug() << "Updated type of Tolerance:" << mp_tolerance->getTypeAsString();

  emit toleranceChangedSignal(
    libXpertMassCore::Tolerance(*mp_tolerance, nullptr));
}


} // namespace libXpertMassGui

} // namespace MsXpS
