/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2024 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 <vector>

/////////////////////// Qt includes
#include <QString>
#include <QStringList>
#include <QDebug>
#include <QRegularExpression>


/////////////////////// Local includes
#include "MsXpS/libXpertMassCore/globals.hpp"
#include "MsXpS/libXpertMassCore/Isotope.hpp"
#include "MsXpS/libXpertMassCore/Utils.hpp"


int isotopeMetaTypeId = qRegisterMetaType<MsXpS::libXpertMassCore::Isotope>(
  "MsXpS::libXpertMassCore::Isotope");

int isotopePtrMetaTypeId =
  qRegisterMetaType<MsXpS::libXpertMassCore::Isotope *>(
    "MsXpS::libXpertMassCore::IsotopePtr");

int isotopeSPtrMetaTypeId =
  qRegisterMetaType<QSharedPointer<MsXpS::libXpertMassCore::Isotope>>(
    "QSharedPointer<MsXpS::libXpertMassCore::Isotope>");

int isotopeCstSPtrMetaTypeId =
  qRegisterMetaType<QSharedPointer<MsXpS::libXpertMassCore::Isotope>>(
    "QSharedPointer<const MsXpS::libXpertMassCore::Isotope>");

namespace MsXpS
{
namespace libXpertMassCore
{

// #include <libisospec++/isoSpec++.h>
//

// extern const char* elem_table_element[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES];

// extern const char* elem_table_symbol[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES];

// extern const double elem_table_mass[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES];

// extern const double
// elem_table_probability[ISOSPEC_NUMBER_OF_ISOTOPIC_ENTRIES];


// This is the order of the columns in the gui TableView
// ELEMENT,
// SYMBOL,
// MASS,
// PROBABILITY

/*!
\enum MsXpS::libXpertMassCore::IsotopeFields

\brief This enum type documents the various member data in \l{Isotope}.

The values assigned to the various enum members are used to specify the
columsn in the GUI table view. They are also used to access substrings in
the proper order in the overloaded initialize() function.

\value NAME       Indicates Isotope::m_name.
\value SYMBOL       Indicates the Isotope::m_symbol.
\value MASS       Indicates the Isotope::m_mass.
\value PROBABILITY       Indicates the Isotope::m_probability.
\omitvalue LAST
*/

/*!
\class MsXpS::libXpertMassCore::Isotope
\inmodule libXpertMassCore
\ingroup PolChemDefBuildingdBlocks
\inheaderfile Isotope.hpp

\brief The Isotope class models an isotope.

The Isotope class models an Isotope by featuring all the methods and
member data required to fully characterize an isotope. The member data in this
class have been inspired by the element tables from the IsoSpec library.
Please, see \l{https://github.com/MatteoLacki/IsoSpec/}.
*/

/*!
\variable MsXpS::libXpertMassCore::Isotope::m_name

\brief The element name, like "carbon" or "nitrogen" (lowercase).
*/

/*!
\variable MsXpS::libXpertMassCore::Isotope::m_symbol
\brief The element symbol, like "C" or "N".
*/

/*!
\variable MsXpS::libXpertMassCore::Isotope::m_mass
\brief The mass of this isotope.

Cannot be negative.
*/

/*!
\variable MsXpS::libXpertMassCore::Isotope::m_probability
\brief The probability of this isotope, that is, its abundance.

Cannot be negative nor superior to 1.
*/

/*!
 \variable MsXpS::libXpertMassCore::Isotope::m_isValid
 \brief The validity status of this isotope.

\sa validate()
 */

/*!
\typedef MsXpS::libXpertMassCore::IsotopeQSPtr
\relates Isotope

Synonym for std::shared_ptr<Isotope>.
*/

/*!
\typedef MsXpS::libXpertMassCore::IsotopeCstQSPtr
\relates Isotope

Synonym for std::shared_ptr<const Isotope>.
*/

/*!
\brief Constructs an absolutely empty \l{Isotope}.

The Isotope instance is invalid. It can be later intialized
with the setter functions or the initialization functions.

\sa initialize(const QString &), initialize(const QString &name,
const QString &symbol,
double mass,
double probability)
*/
Isotope::Isotope(QObject *parent): QObject(parent)
{
}

/*!
 \overload
 \brief Constructs the \l{Isotope} with all the required arguments.

The isotope is created as a fully documented instance if all the following
parameters are correctly set:

\list
\li \a name (\l{m_name}): element's name
\li \a symbol (\l{m_symbol}): element's symbol
\li \a mass (\l{m_mass}): isotope's mass
\li \a probability (\l{m_probability}): isotope's probability, that is abundance
\endlist

\sa initialize(const QString &name, const QString &symbol, double mass, double
probability)
*/
Isotope::Isotope(const QString &name,
                 const QString &symbol,
                 double mass,
                 double probability,
                 QObject *parent)
  : QObject(parent),
    m_name(name),
    m_symbol(symbol),
    m_mass(mass),
    m_probability(probability)
{
  ErrorList error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      QString errors = Utils::joinErrorList(error_list, "\n");

      qDebug().noquote()
        << "The constructed isotope is not valid, with errors:\n"
        << errors;
    }
}

/*!
 \overload
 \brief Constructs the \l{Isotope} as a copy of \a other setting parent to \a
parent.
*/
Isotope::Isotope(const Isotope &other, QObject *parent): QObject(parent)
{
  // qDebug() << "Constructing using reference.";

  m_name        = other.m_name;
  m_symbol      = other.m_symbol;
  m_mass        = other.m_mass;
  m_probability = other.m_probability;

  QVector<QString> error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      QString errors = Utils::joinErrorList(error_list, "\n");

      qDebug().noquote()
        << "The copy-constructed isotope is not valid, with errors:\n"
        << errors;
    }
}

/*!
 \overload
 \brief Constructs the \l{Isotope} as a copy of \a other_p, setting parent to \a
parent.
*/
Isotope::Isotope(const Isotope *other_p, QObject *parent): QObject(parent)
{
  Q_ASSERT(other_p != nullptr);
  // qDebug() << "Constructing using pointer.";

  m_name        = other_p->m_name;
  m_symbol      = other_p->m_symbol;
  m_mass        = other_p->m_mass;
  m_probability = other_p->m_probability;

  QVector<QString> error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      QString errors = Utils::joinErrorList(error_list, "\n");

      qDebug().noquote()
        << "The copy-constructed isotope is not valid, with errors:\n"
        << errors;
    }
}

/*!
 \overload
\brief Isotope::Isotope(const QString &text, QObject *parent)

Constructs this \l{Isotope} using all the data in the \a text string,
setting parent to \a parent.

The string contains all the Isotope data, separated by a comma ',' exactly
with the same format as that implemented by Isotope::toString().

\sa initialize(const QString &), initialize(const QString &name,
const QString &symbol,
double mass,
double probability), toString()
*/
Isotope::Isotope(const QString &text, QObject *parent): QObject(parent)
{
  // This is somehow the reverse of toString().

  if(!initialize(text))
    qDebug() << "The initialization failed using text:" << text;
}

/*!
\brief Destructs this Isotope.
*/
Isotope::~Isotope()
{
}

/*!
\brief Returns a heap-allocated Isotope as a copy of \c this Isotope but
reparented with \a parent.

Each member datum in \c this Isotope is copied the returned Isotope,
effectivaly making a copy of \c this Isotope and returning it after
reparenting to \a parent.
*/
Isotope *
Isotope::clone(QObject *parent) const
{
  Isotope *copy_p = new Isotope(parent);
  copy_p->initialize(m_name, m_symbol, m_mass, m_probability);

  return copy_p;
}

/*!
\brief Returns a heap-allocated Isotope as a copy of \a other but reparented
with \a parent.

Each member datum in \a other is copied the returned Isotope,
effectivaly making a copy of \a other and returning it after
reparenting to \a parent.
*/
Isotope *
Isotope::clone(const Isotope &other, QObject *parent)
{
  Isotope *copy_p = new Isotope(parent);
  copy_p->initialize(
    other.m_name, other.m_symbol, other.m_mass, other.m_probability);

  return copy_p;
}

/*!
\brief Resets all the member data to default invalid values.

The m_isValid member datum is set to false.
*/
void
Isotope::clear()
{
  m_name        = "";
  m_symbol      = "";
  m_mass        = -1.0;
  m_probability = -1.0;

  m_isValid = false;
}

/*!
\overload MsXpS::libXpertMassCore::initialize(const QString &name,
                    const QString &symbol,
                    double mass,
                    double probability)

\brief Initializes this Isotope instance using \a name, \a symbol, \a mass and
\a probability.

Returns true if initilization was without error, false otherwise.

\sa initialize(const QString &text)
*/
bool
Isotope::initialize(const QString &name,
                    const QString &symbol,
                    double mass,
                    double probability)
{
  // Start by resetting this Isotope instance (sets m_isValid to false)
  clear();

  QString local_text;
  QRegularExpression regexp;

  //////// The element's name
  local_text = name.simplified();
  regexp.setPattern("^[a-z]+$");

  if(!regexp.match(local_text).hasMatch())
    {
      qDebug() << "Failed to initialize the element's name.";
    }
  else
    m_name = local_text;

  //////// The element's symbol
  local_text = symbol.simplified();
  regexp.setPattern("^[A-Z][a-z]*$");

  if(!regexp.match(local_text).hasMatch())
    qDebug() << "Failed to initialize the element's symbol.";
  else
    m_symbol = local_text;

  //////// The isotope's mass
  if(mass < 0)
    qDebug() << "Failed to initialize the isotope's mass.";
  else
    m_mass = mass;

  //////// The isotope's probability
  if(probability < 0 || probability > 1)
    qDebug() << "Failed to initialize the isotope's probability.";
  else
    m_probability = probability;

  //  At this point, it looks like the Isotope is syntactically valid,
  //  but is it from a chemical standpoint?

  QVector<QString> error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      QString errors;

      for(const QString &error : error_list)
        errors.append(QString("%1\n").arg(error));

      qDebug().noquote() << "The initialized isotope is not valid, with errors:"
                         << errors;
    }

  return m_isValid;
}

/*!
\brief Initializes the \l{Isotope} using all the data in the \a text string.

The string passed as argument is QString::simplified() and
QString::split() with ',' as the delimiter.

The obtained strings are converted to the corresponding numerical or textual
values to initalize all the member data.

Returns true if the string contained valid substrings that successfully
initialized the \l{Isotope}, false otherwise.

\sa Isotope(const QString &text, QObject *parent), initialize(const QString
&name, const QString &symbol, double mass, double probability)
*/
bool
Isotope::initialize(const QString &text)
{
  // qDebug() << "Initializing Isotope with text:" << text;

  // Start by resetting this Isotope instance (sets m_isValid to false)
  clear();

  if(text.isEmpty())
    {
      qDebug() << "The text used to initialize the Isotope instance is empty.";
      m_isValid = false;
      return m_isValid;
    }

  // At this point deconstruct the line. Make sure we remove all spaces from
  // beginning and end AND reduce all multiple-space substrings in the text to
  // only single-space substrings.

  QString local_text = text.simplified();
  // qDebug() << "After simplification, the text becomes:" << local_text;

  QStringList string_list = local_text.split(',');

  // qDebug() << "After splitting text at commas, count of the subtext strings:"
  //          << string_list.size();

  // We may be reading data from a line that comes from an old version of
  // isotopic data files that contain much more information than from newer
  // version of such files. Check this.

  if(string_list.size() == 10)
    {
      // We may be reading from an older version file.
      return initializeVersion1(text);
    }
  else if(string_list.size() == static_cast<int>(IsotopeFields::LAST))
    {
      // We may be reading from a more recent version file.
      return initializeVersion2(text);
    }
  else
    {
      return false;
    }

  return false;
}

/*!
\brief Initializes the \l{Isotope} using all the data in the \a text string.

The string passed as argument is QString::simplified() and
QString::split() with ',' as the delimiter.

The index of the substring in the string list obtained by splitting the initial
text at ',' delimiter defines what data element it corresponds to.

The obtained strings are converted to the corresponding numerical or textual
values to initalize all the member data.

Returns true if the string contained valid substrings that successfully
initialized the \l{Isotope}, false otherwise.

\sa Isotope(const QString &text, QObject *parent), initialize(const QString
&name, const QString &symbol, double mass, double probability)
*/
bool
Isotope::initializeVersion1(const QString &text)
{
  // qDebug() << "Initializing Isotope with text:" << text;

  // This version has a set of data matching this structure:

  //    enum class IsotopeFields
  //    {
  //      ID              = 0,
  //      ELEMENT         = 1,
  //      SYMBOL          = 2,
  //      ATOMIC_NUMBER   = 3,
  //      MASS            = 4,
  //      MASS_NUMBER     = 5,
  //      EXTRA_NEUTRONS  = 6,
  //      PROBABILITY     = 7,
  //      LN_PROBABILITY = 8,
  //      RADIOACTIVE     = 9,
  //      LAST            = 10,
  //    };

  // Start by resetting this Isotope instance (sets m_isValid to false)
  clear();

  if(text.isEmpty())
    {
      qDebug() << "The text used to initialize the Isotope instance is empty.";
      m_isValid = false;
      return m_isValid;
    }

  double temp_value_double;
  QString temp_value_string;
  QRegularExpression regexp;

  // At this point deconstruct the line. Make sure we remove all spaces from
  // beginning and end AND reduce all multiple-space substrings in the text to
  // only single-space substrings.

  QString local_text = text.simplified();
  // qDebug() << "After simplification, the text becomes:" << local_text;

  QStringList string_list = local_text.split(',');

  // qDebug() << "After splitting text at commas, count of the subtext strings:"
  //          << string_list.size();

  if(string_list.size() != 10)
    {
      qDebug() << "The text does not match an Isotope definition.";
      return false;
    }

  bool ok = false;

  // The element name
  temp_value_string =
    string_list[static_cast<int>(Version1IsotopeFields::ELEMENT)].simplified();
  //  The element name can be checked using a regexp
  regexp.setPattern("^[a-z]+$");
  if(!regexp.match(temp_value_string).hasMatch())
    {
      qDebug() << "Failed to extract the element name.";
      return false;
    }
  m_name = temp_value_string;

  temp_value_string =
    string_list[static_cast<int>(Version1IsotopeFields::SYMBOL)].simplified();
  //  The symbol can be checked using a regexp
  regexp.setPattern("^[A-Z][a-z]*$");
  if(!regexp.match(temp_value_string).hasMatch())
    {
      qDebug() << "Failed to extract the element symbol.";
      return false;
    }
  m_symbol = temp_value_string;

  temp_value_double = string_list[static_cast<int>(Version1IsotopeFields::MASS)]
                        .simplified()
                        .toDouble(&ok);
  if(!ok)
    {
      qDebug() << "Failed to extract the isotope mass.";
      return false;
    }
  m_mass = temp_value_double;

  temp_value_double =
    string_list[static_cast<int>(Version1IsotopeFields::PROBABILITY)]
      .simplified()
      .toDouble(&ok);
  if(!ok)
    {
      qDebug() << "Failed to extract the isotope probability.";
      return false;
    }
  m_probability = temp_value_double;

  // qDebug() << toString();

  return true;
}

/*!
\brief Initializes the \l{Isotope} using all the data in the \a text string.

The string passed as argument is QString::simplified() and
QString::split() with ',' as the delimiter.

The index of the substring in the string list obtained by splitting the initial
text at ',' delimiter defines what data element it corresponds to.

Returns true if the string contained valid substrings that successfully
initialized the \l{Isotope}, false otherwise.

\sa Isotope(const QString &text, QObject *parent), initialize(const QString
&name, const QString &symbol, double mass, double probability)
*/
bool
Isotope::initializeVersion2(const QString &text)
{
  // qDebug() << "Initializing Isotope with text:" << text;

  // Start by resetting this Isotope instance (sets m_isValid to false)
  clear();

  if(text.isEmpty())
    {
      qDebug() << "The text used to initialize the Isotope instance is empty.";
      m_isValid = false;
      return m_isValid;
    }

  double temp_value_double;
  QString temp_value_string;
  QRegularExpression regexp;

  // At this point deconstruct the line. Make sure we remove all spaces from
  // beginning and end AND reduce all multiple-space substrings in the text to
  // only single-space substrings.

  QString local_text = text.simplified();
  // qDebug() << "After simplification, the text becomes:" << local_text;

  QStringList string_list = local_text.split(',');

  // qDebug() << "After splitting text at commas, count of the subtext strings:"
  //          << string_list.size();

  // qDebug() << "Expecting " << static_cast<int>(IsotopeFields::LAST)
  //          << "comma-separated fields for isotope-describing text line."
  //          << "QStringList is:" << string_list;

  if(string_list.size() != static_cast<int>(IsotopeFields::LAST))
    {
      qDebug() << "The text does not match an Isotope definition.";
      m_isValid = false;
      return m_isValid;
    }

  bool ok = false;

  temp_value_string =
    string_list[static_cast<int>(IsotopeFields::NAME)].simplified();
  regexp.setPattern("^[a-z]+$");
  if(!regexp.match(temp_value_string).hasMatch())
    {
      qDebug() << "Failed to extract the element name.";
      m_isValid = false;
      return m_isValid;
    }
  else
    m_name = temp_value_string;

  temp_value_string =
    string_list[static_cast<int>(IsotopeFields::SYMBOL)].simplified();
  regexp.setPattern("^[A-Z][a-z]*$");
  if(!regexp.match(temp_value_string).hasMatch())
    {
      qDebug() << "Failed to extract the element symbol.";
      m_isValid = false;
      return m_isValid;
    }
  else
    m_symbol = temp_value_string;

  temp_value_double =
    string_list[static_cast<int>(IsotopeFields::MASS)].simplified().toDouble(
      &ok);
  if(!ok)
    {
      qDebug() << "Failed to extract the isotope mass.";
      m_isValid = false;
      return m_isValid;
    }
  else
    m_mass = temp_value_double;


  temp_value_double = string_list[static_cast<int>(IsotopeFields::PROBABILITY)]
                        .simplified()
                        .toDouble(&ok);
  if(!ok || temp_value_double > 1)
    {
      qDebug() << "Failed to extract the isotope probability.";
      m_isValid = false;
      return m_isValid;
    }
  else
    m_probability = temp_value_double;

  //  At this point, it looks like the Isotope is syntactically valid,
  //  but is it from a chemical standpoint?

  QVector<QString> error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      QString errors;

      for(const QString &error : error_list)
        errors.append(QString("%1\n").arg(error));

      qDebug().noquote() << "The initialized isotope is not valid, with errors:"
                         << errors;
    }

  return m_isValid;
}

/*!
\brief Sets the name of the isotope to \a name.

\sa getName()
*/
void
Isotope::setName(const QString &name)
{
  QRegularExpression regexp("^[a-z]+$");
  if(!regexp.match(name).hasMatch())
    {
      qDebug() << "Failed to set the element name. The name should be all "
                  "lower-case. Isotope left unchanged.";
      m_isValid = false;
    }

  m_name = name;
}

/*!
\brief Returns the name of the isotope.

\sa setName()
*/
QString
Isotope::getName() const
{
  return m_name;
}

/*!
\brief Sets the symbol of the isotope to \a symbol.

\sa getSymbol()
*/
void
Isotope::setSymbol(const QString &symbol)
{
  QRegularExpression regexp("^[A-Z][a-z]*$");
  if(!regexp.match(symbol).hasMatch())
    {
      qDebug() << "Failed to set the element symbol. Isotope left unchanged.";
      m_isValid = false;
    }

  m_symbol = symbol;
}

/*!
\brief Returns the symbol of the isotope.

\sa setSymbol()
*/
QString
Isotope::getSymbol() const
{
  return m_symbol;
}

/*!
\brief Sets the the mass of the isotope to \a mass.

\sa getMass()
*/
void
Isotope::setMass(double mass)
{
  if(mass < 0)
    {
      qDebug() << "Failed to set the mass. Isotope left unchanged.";
      m_isValid = false;
    }

  m_mass = mass;
}

/*!
\brief Returns the mass of the isotope.

\sa setMass()
*/
double
Isotope::getMass() const
{
  return m_mass;
}

/*!
\brief Sets the probability (the abundance) of the isotope to \a probability.

\sa getProbability()
*/
void
Isotope::setProbability(double probability)
{
  if(probability < 0 || probability > 1)
    {
      qDebug() << "Failed to set the probability. Isotope left unchanged.";
      m_isValid = false;
    }

  m_probability = probability;
}

/*!
\brief Returns the probability (the abundance) of the isotope.

\sa setProbability()
*/
double
Isotope::getProbability() const
{
  return m_probability;
}

/*!
\brief Validates the isotope.

The element name, symbol, mass and probability member data are scrutinized and
if errors are detected descriptive error messages are added to \a error_list_p.

For example, for symbol:

\code
if(m_symbol.isEmpty())
{
local_text = m_symbol.simplified();
regexp.setPattern("^[A-Z][a-z]*$");

if(!regexp.match(local_text).hasMatch())
{
error_msg = "Failed to validate the element's symbol.";
error_list_p->push_back(error_msg);
qDebug() << error_msg;
}
}
\endcode

Returns the error count. If no error occurred, the returned value is 0.
*/
bool
Isotope::validate(ErrorList *error_list_p)
{
  qsizetype previous_error_count = error_list_p->size();

  QString error_msg;
  QString local_text;
  QRegularExpression regexp;

  //////// The element name
  local_text = m_name.simplified();
  regexp.setPattern("^[a-z]+$");

  if(!regexp.match(local_text).hasMatch())
    {
      error_msg = "Failed to validate the element's name.";
      error_list_p->push_back(error_msg);
      qDebug() << error_msg;
    }
  m_name = local_text;

  //////// The element symbol
  local_text = m_symbol.simplified();
  regexp.setPattern("^[A-Z][a-z]*$");

  if(!regexp.match(local_text).hasMatch())
    {
      error_msg = "Failed to validate the element's symbol.";
      error_list_p->push_back(error_msg);
      qDebug() << error_msg;
    }
  m_symbol = local_text;

  //////// The element mass
  if(m_mass < 0)
    {
      error_msg = "Failed to validate the isotope's mass.";
      error_list_p->push_back(error_msg);
      qDebug() << error_msg;
    }

  //////// The element probability
  if(m_probability < 0 || m_probability > 1)
    {
      // qDebug() << "The probability:" << m_probability;

      error_msg = "Failed to validate the isotope's probability.";
      error_list_p->push_back(error_msg);
      qDebug() << error_msg;
    }

  if(error_list_p->size() > previous_error_count)
    m_isValid = false;
  else
    m_isValid = true;

  return m_isValid;
}

/*!
\brief Return the member datum \l{m_isValid}.
*/
bool
Isotope::isValid() const
{
  return m_isValid;
}

/*!
\brief Tests the equality between this isotope and \a other.

Each member datum in \a other is compared to this isotope's member datum. If a
difference is detected, a counter is incremented.

Returns true if no difference has been encountered (the counter is 0) and
false otherwise.

\sa operator!=()
*/
bool
Isotope::operator==(const Isotope &other) const
{
  if(&other == this)
    return true;

  return (m_name == other.m_name && m_symbol == other.m_symbol &&
          m_mass == other.m_mass && m_probability == other.m_probability);
}

/*!
\brief Tests the inequality between this isotope and \a other.

Returns the negated result of operator==().

\sa operator==()
*/
bool
Isotope::operator!=(const Isotope &other) const
{
  if(&other == this)
    return false;

  return !operator==(other);
}

/*!
\brief Returns a string containing a comma-separated textual representation
of all the member data values.

All the member data values are separated using commas ','. Only the values
are stored in the string, without naming the variables:

\code
return QString("%1,%2,%3,%4")
.arg(m_name)
.arg(m_symbol)
.arg(m_mass, 0, 'f', 60)
.arg(m_probability, 0, 'f', 60);
\endcode
*/
QString
Isotope::toString() const
{
  // We need to use CSV because there might be spaces in the
  // text in the IsoSpec tables.
  return QString("%1,%2,%3,%4")
    .arg(m_name)
    .arg(m_symbol)
    .arg(m_mass, 0, 'f', 60)
    .arg(m_probability, 0, 'f', 60);
}

void
Isotope::registerJsConstructor(QJSEngine *engine)

{
  if(!engine)
    {
      qDebug() << "Cannot register Isotope class: engine is null";
      return;
    }

  // Register the meta object as a constructor

  QJSValue jsMetaObject = engine->newQMetaObject(&Isotope::staticMetaObject);
  engine->globalObject().setProperty("Isotope", jsMetaObject);
}


} // namespace libXpertMassCore

} // namespace MsXpS
