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


/////////////////////// Local includes
#include "MsXpS/libXpertMassCore/FragmentationConfig.hpp"
#include "MsXpS/libXpertMassCore/PolChemDef.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{


/*!
\class MsXpS::libXpertMassCore::FragmentationConfig
\inmodule libXpertMassCore
\ingroup PolChemDefGasPhaseChemicalReactions
\inheaderfile FragmentationConfig.hpp

\brief The FragmentationConfig class derives from FragmentationPathway and adds
functionality to configure the way the fragmentation occurs in the \l{Oligomer}
\l{Sequence}.

Since an Oligomer is actually defined to merely be a monomer range in a given
\l{Polymer} \l{Sequence},  the configuration handles the definition of the
Polymer sequence region that needs to undergo fragmentation.

Because the user might need to apply specific gas phase chemical reactions upon
fragmentation of the Oligomer that are not defined per se in the
FragmentationPathway, a container of formulas enables the application of any
number of such chemical reactions for each fragmentation event. This is useful
in any polymer chemistry definition,  because most often the Oligomer undergoing
fragmentation has loss of neutral ions decompositions like \e{NH\sub{3}} or
\e{H\sub{2}O}.

The user might need to generate fragmentation ions that are of different
ionization levels. This class provides settings for this eventuality.

\sa FragmentationPathway
*/


/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_startIndex

\brief The index that delimits the start position (index) of the Polymer
Sequence that defines the Oligomer undergoing fragmentation.
*/

/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_stopIndex

\brief The index that delimits the stop position (index) of the Polymer Sequence
that defines the Oligomer undergoing fragmentation.
*/

/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_startIonizeLevel

\brief The first value of the ionization level range.
*/

/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_stopIonizeLevel

\brief The last value of the ionization level range.
*/

/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_sequenceEmbedded

\brief Specifies if the sequence of the fragments needs to be stored or not in
the Oligomer fragments.
*/

/*!
\variable MsXpS::libXpertMassCore::FragmentationConfig::m_formulas

\brief Container for Formula instances that describe additional decomposition
reactions like those often observed in protein gas phase chemistry (loss of
ammonia and/or of water).
*/

/*!
\brief Constructs a fragmentation configuration with a number of parameters.

\list
\li \a pol_chem_def_csp Polymer chemistry definition that is passed to the
FragmentationPathway base class. Cannot be nullptr (fatal error if so).

\li \a name Name that is passed to the FragmentationPathway base class. Cannot
be empty.

\li \a formula Formula that is passed to the FragmentationPathway base class.

\li \a frag_end The end of the Oligomer undergoing fragmentation that is found
in the product ions. Used to initialize the FragmentationPathway base class.

\li \a comment Comment that is passed to the FragmentationPathway base class.
Defaults to the null string.

\li \a sequence_embedded Indicates if the product ion sequence needs to be
stored in the fragment Oligomer.
\endlist

\sa FragmentationPathway
*/
FragmentationConfig::FragmentationConfig(PolChemDefCstSPtr pol_chem_def_csp,
                                         const QString &name,
                                         const QString &formula,
                                         Enums::FragEnd frag_end,
                                         const QString &comment,
                                         bool sequence_embedded)
  : FragmentationPathway(pol_chem_def_csp, name, formula, frag_end, comment),
    m_sequenceEmbedded(sequence_embedded)
{
  if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr;";
}

/*!
\brief Constructs a fragmentation configuration with a number of parameters.

\list
\li \a fragmentation_pathway Fragmentation pathway that will become the base
class instance of this instance.

\li \a start_index Start position (index) in the Polymer Sequence that delimits
the Oligomer undergoing fragmentation.

\li \a stop_index Stop position (index) in the Polymer Sequence that delimits
the Oligomer undergoing fragmentation.

\li \a sequence_embedded Indicates if the product ion sequence needs to be
stored in the fragment Oligomer.
\endlist

The PolChemDef member of the FragmentationPathway base class is tested. If it is
nullptr,  that is a fatal error.

\sa FragmentationPathway
*/
FragmentationConfig::FragmentationConfig(
  const FragmentationPathway &fragmentation_pathway,
  int start_index,
  int stop_index,
  bool sequence_embedded)
  : FragmentationPathway(fragmentation_pathway),
    m_startIndex(start_index),
    m_stopIndex(stop_index),
    m_sequenceEmbedded(sequence_embedded)
{
  if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr;";
}

/*!
\brief Constructs a fragmentation configuration as a copy of \a other.

The PolChemDef member of the FragmentationPathway base class is tested. If it is
nullptr,  that is a fatal error.

\sa FragmentationPathway
*/
FragmentationConfig::FragmentationConfig(const FragmentationConfig &other)
  : FragmentationPathway(other),
    m_startIndex(other.m_startIndex),
    m_stopIndex(other.m_stopIndex),
    m_startIonizeLevel(other.m_startIonizeLevel),
    m_stopIonizeLevel(other.m_stopIonizeLevel),
    m_sequenceEmbedded(other.m_sequenceEmbedded),
    m_formulas(other.m_formulas)
{
  if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr;";
}

/*!
\brief Constructs this fragmentation configuration.
*/
FragmentationConfig::~FragmentationConfig()
{
  m_formulas.clear();
}

/*!
\brief Assigns \a other to this FragmentationConfig instance.
*/
FragmentationConfig &
FragmentationConfig::operator=(const FragmentationConfig &other)
{
  if(&other == this)
    return *this;

  FragmentationPathway::operator=(other);

  m_startIndex = other.m_startIndex;
  m_stopIndex  = other.m_stopIndex;

  m_startIonizeLevel = other.m_startIonizeLevel;
  m_stopIonizeLevel  = other.m_stopIonizeLevel;

  m_sequenceEmbedded = other.m_sequenceEmbedded;

  for(const Formula &formula : other.m_formulas)
    m_formulas.push_back(formula);

  return *this;
}

/*!
\brief Sets the Oligomer start \a index in the Polymer Sequence.
*/
void
FragmentationConfig::setStartIndex(std::size_t index)
{
  m_startIndex = index;
}

/*!
\brief Returns the Oligomer start index in the Polymer Sequence.
*/
std::size_t
FragmentationConfig::getStartIndex() const
{
  return m_startIndex;
}

/*!
\brief Sets the Oligomer stop \a index in the Polymer Sequence.
*/
void
FragmentationConfig::setStopIndex(std::size_t index)
{
  m_stopIndex = index;
}

/*!
\brief Returns the Oligomer stop index in the Polymer Sequence.
*/
std::size_t
FragmentationConfig::getStopIndex() const
{
  return m_stopIndex;
}

/*!
\brief Sets the start \a value of the ionization range.
*/
void
FragmentationConfig::setStartIonizeLevel(std::size_t value)
{
  if(value <= m_stopIonizeLevel)
    {
      m_startIonizeLevel = value;
    }
  else
    {
      m_startIonizeLevel = m_stopIonizeLevel;
      m_stopIonizeLevel  = value;
    }
}

/*!
\brief Returns the start value of the ionization range.
*/
std::size_t
FragmentationConfig::getStartIonizeLevel() const
{
  return m_startIonizeLevel;
}

/*!
\brief Sets the stop \a value of the ionization range.
*/
void
FragmentationConfig::setStopIonizeLevel(std::size_t value)
{
  if(value > m_startIonizeLevel)
    {
      m_stopIonizeLevel = value;
    }
  else
    {
      m_startIonizeLevel = m_stopIonizeLevel;
      m_stopIonizeLevel  = value;
    }
}

/*!
\brief Returns the stop value of the ionization range.
*/
std::size_t
FragmentationConfig::getStopIonizeLevel() const
{
  return m_stopIonizeLevel;
}

/*!
\brief Sets the \a start and \a stop values of the ionization range.
*/
void
FragmentationConfig::setIonizeLevels(std::size_t start, std::size_t stop)
{
  m_startIonizeLevel = start;
  m_stopIonizeLevel  = stop;
}

/*!
\brief Adds a \a formula to the container of Formula instances.

Returns true if the formula validated successfully, false otherwise.
*/
bool
FragmentationConfig::addFormula(const Formula &formula)
{
  if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
    qFatal() << "Programming error. Pointer cannot be nullptr;";

  IsotopicDataCstSPtr isotopic_data_csp =
    mcsp_polChemDef->getIsotopicDataCstSPtr();

  ErrorList error_list;

  if(!formula.validate(isotopic_data_csp, &error_list))
    {
      qDebug() << "Formula" << formula.getActionFormula()
               << "failed to validate with errors:"
               << Utils::joinErrorList(error_list);

      return false;
    }

  // Check that the formula is not already in the list.

  std::vector<Formula>::const_iterator the_iterator_cst =
    std::find_if(m_formulas.cbegin(),
                 m_formulas.cend(),
                 [&formula](const Formula &iter_formula) {
                   return iter_formula == formula;
                 });

  if(the_iterator_cst != m_formulas.cend())
    return false;

  // At this point we can say that the formula is OK and can be
  // added.

  m_formulas.push_back(formula);

  return true;
}

/*!
\brief Adds a Formula to the container of Formula instances using \a
formula_string.
*/
bool
FragmentationConfig::addFormula(const QString &formula_string)
{
  // With the string make a true Formula instance.

  Formula formula = Formula(formula_string);

  return addFormula(formula);
}

/*!
\brief Returns a const reference to the container of Formula instances.
*/
const std::vector<Formula> &
FragmentationConfig::getFormulasCstRef() const
{
  return m_formulas;
}

/*!
\brief Returns a reference to the container of Formula instances.
*/
std::vector<Formula> &
FragmentationConfig::getFormulasRef()
{
  return m_formulas;
}

/*!
\brief Sets if the product ion Oligomer's sequence needs to be stored to \a
value.
*/
void
FragmentationConfig::setSequenceEmbedded(bool value)
{
  m_sequenceEmbedded = value;
}

/*!
\brief Returns if the product ion Oligomer's sequence needs to be stored.
*/
bool
FragmentationConfig::isSequenceEmbedded() const
{
  return m_sequenceEmbedded;
}

/*!
\brief Returns a string describing this FragmentationConfig instance.
*/
QString
FragmentationConfig::toString() const
{
  QString text = "FragmentationConfig: \n";

  text += FragmentationPathway::toString();
  text += "\n";

  text += QString("Indices [%1-%2] -- ionization levels [%3-%4].\n")
            .arg(m_startIndex)
            .arg(m_stopIndex)
            .arg(m_startIonizeLevel)
            .arg(m_stopIonizeLevel);

  if(m_formulas.size())
    {
      text += "Formulas:\n";

      for(const Formula &formula : m_formulas)
        text += QString("%1 - \n").arg(formula.getActionFormula());
    }

  return text;
}


} // namespace libXpertMassCore
} // namespace MsXpS
