/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright 2009--2026 by 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 <QDebug>


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


/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "MsXpS/libXpertMassCore/Tolerance.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

Tolerance::Tolerance(QObject *parent): QObject(parent)
{
}

Tolerance::Tolerance(const QString &text, QObject *parent): QObject(parent)
{
  initialize(text, parent);
}

Tolerance::Tolerance(Tolerance::Type type, double nominal, QObject *parent)
  : QObject(parent), m_nominal(nominal), m_type(type)
{
}

Tolerance::Tolerance(const Tolerance &other, QObject *parent)
  : QObject(parent), m_nominal(other.m_nominal), m_type(other.m_type)
{
}

void
Tolerance::initialize(double nominal, Type type, QObject *parent)
{
  QObject::setParent(parent);

  if(m_typeToStringMap.value(type).isEmpty())
    qFatal() << "Programming error. This tolerance type is not known.";

  m_nominal = nominal;
  m_type    = type;

  emit toleranceChangedSignal(
    std::pair<double, Tolerance::Type>(m_nominal, m_type));
}

void
Tolerance::initialize(const Tolerance &other, QObject *parent)
{
  initialize(other.m_nominal, other.m_type, parent);
}

void
Tolerance::initialize(const QString &text, QObject *parent)
{
  QObject::setParent(parent);

  QStringList string_list = text.split(" ");
  bool ok;
  m_nominal = string_list[0].toInt(&ok);
  m_type    = getType(string_list[1]);
}

void
Tolerance::setNominal(double nominal)
{
  if(m_nominal == nominal)
    return;

  m_nominal = nominal;

  emit nominalChangedSignal();
  emit toleranceChangedSignal(
    std::pair<double, Tolerance::Type>(m_nominal, m_type));
}

double
Tolerance::getNominal() const
{
  return m_nominal;
}

void
Tolerance::setType(Type type)
{
  if(m_type == type)
    return;

  QString type_string = m_typeToStringMap.value(type);
  if(type_string.isEmpty())
    qFatal() << "Programming error. This tolerance type value is not known.";

  m_type = type;

  emit typeChangedSignal();
  emit toleranceChangedSignal(
    std::pair<double, Tolerance::Type>(m_nominal, m_type));
}

void
Tolerance::setType(const QString &text)
{
  QMap<Type, QString>::const_iterator the_iterator_cst =
    std::find_if(m_typeToStringMap.cbegin(),
                 m_typeToStringMap.cend(),
                 [text](const QString &value) {
                   return value == text;
                 });

  if(the_iterator_cst == m_typeToStringMap.cend())
    qFatal() << "Programming error. This tolerance type string is not known:"
             << text;

  m_type = the_iterator_cst.key();

  emit typeChangedSignal();
  emit toleranceChangedSignal(
    std::pair<double, Tolerance::Type>(m_nominal, m_type));
}

Tolerance::Type
Tolerance::getType() const
{
  return m_type;
}

Tolerance::Type
Tolerance::getType(const QString &text) const
{
  QMap<Type, QString>::const_iterator the_iterator_cst =
    std::find_if(m_typeToStringMap.cbegin(),
                 m_typeToStringMap.cend(),
                 [text](const QString &value) {
                   return value == text;
                 });

  if(the_iterator_cst == m_typeToStringMap.cend())
    {
      qDebug() << "Programming error. This tolerance string is not known.";
      return Tolerance::Type::NOT_SET;
    }

  return the_iterator_cst.key();
}

QString
Tolerance::getTypeAsString(Tolerance::Type type) const
{
  return m_typeToStringMap.value(type);
}

QString
Tolerance::getTypeAsString() const
{
  return getTypeAsString(m_type);
}

Tolerance *
Tolerance::clone(QObject *parent)
{
  Tolerance *copy = new Tolerance(m_type, m_nominal, parent);
  return copy;
}

double
Tolerance::calculateWidth(double reason) const
{
  double width = 0;

  if(m_type == Type::DALTON)
    width = m_nominal;
  else if(m_type == Type::RES)
    width = reason / m_nominal;
  else if(m_type == Type::PPM)
    width = (m_nominal * reason) / 1000000;
  else
    qFatal() << "Programming error. The Type is not valid.";

  return width;
}

double
Tolerance::calculateHalfWidth(double reason) const
{
  return calculateWidth(reason) / 2;
}

double
Tolerance::calculateNominal(double width, double mass_or_mz) const
{
  double nominal = 0;

  if(m_type == Type::DALTON)
    nominal = width;
  else if(m_type == Type::RES)
    nominal = mass_or_mz / width;
  else if(m_type == Type::PPM)
    nominal = width * 1000000 / mass_or_mz;
  else
    qFatal() << "Programming error. The Type is not valid.";

  return nominal;
}

QString
Tolerance::toString() const
{
  QString type_as_text = getTypeAsString();

  return QString("%1 %2").arg(m_nominal).arg(type_as_text);
}


} // namespace libXpertMassCore
} // namespace MsXpS
