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


/////////////////////// Qt includes
#include <QChar>
#include <QDebug>
#include <QString>


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


int calcOptionsMetaTypeId =
  qRegisterMetaType<MsXpS::libXpertMassCore::CalcOptions>(
    "MsXpS::libXpertMassCore::CalcOptions");

namespace MsXpS
{
namespace libXpertMassCore
{


/*!
\class MsXpS::libXpertMassCore::CalcOptions
\inmodule libXpertMassCore
\ingroup XpertMassMassCoreCalculations
\inheaderfile MsXpS/libXpertMassCore/CalcOptions.hpp

\brief The CalcOptions class provides the specifications that
configure the way masses are calculated for \l{Oligomer}s, \l{Polymer}s and
product ions.
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_deepCalculation

\brief Tells if the calculations must involve the recalculation of all
the masses of the \l{Monomer}s in the sequence. This is typically needed when
calculating masses involving modified Monomer instances, whereby each Monomer
needs to recompute its mass taking into account the Modif instances attached to
it.

By default initializes to false.
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::isDeepCalculation

\brief Tells if the calculations must involve the recalculation of all
the masses of the \l{Monomer}s in the sequence. This is typically needed when
calculating masses involving modified Monomer instances, whereby each Monomer
needs to recompute its mass taking into account the Modif instances attached to
it.
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_massType

\brief The mass type to compute, monoisotopic or average.

By default, initializes to Enums::MassType::BOTH.

\sa Enums::MassType
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::massType

\brief The mass type to compute, monoisotopic or average.

\sa Enums::MassType
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_capType

\brief The cap type, left or right (or both), to account for in the
calculations.

By default, initializes to Enums::CapType::BOTH.

\sa Enums::CapType
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::capType

\brief The cap type, left or right (or both), to account for in the
calculations.

\sa Enums::CapType
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_monomerEntities

\brief The \l Monomer entities to be accounted for in the calculations.

By default, initializes to Enums::ChemicalEntity::NONE.

In the protein realm, the left cap is the proton while the end cap is the
hydroxyl.

\sa Enums::ChemicalEntity
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::monomerEntities

\brief The \l Monomer entities to be accounted for in the calculations.

\sa Enums::ChemicalEntity
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_polymerEntities

\brief The \l Polymer entities to be accounted for in the calculations.

By default, initializes to Enums::ChemicalEntity::NONE.

\sa Enums::ChemicalEntity
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::polymerEntities

\brief The \l Polymer entities to be accounted for in the calculations.

\sa Enums::ChemicalEntity
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_selectionType

\brief The manner the \l{Monomer}s need to be accounted for: as residual chains
or as finished-polymerization state sequences.

The calculations might consider only the residual chain. In that case, only
the mass of the monomers is considered and then the polymer is not in its
finished polymerization state. If that latter state is required, then, the
residual chain must be capped with both the left and the right end caps.

By default, initializes to Enums::SelectionType::OLIGOMERS.

\sa MsXpS::libXpertMassCore::Enums::SelectionType
*/

/*!
\property MsXpS::libXpertMassCore::CalcOptions::selectionType

\brief The manner the monomers need to be accounted for: as residual chains
or as finished-polymerization state sequences.

The calculations might consider only the residual chain. In that case, only
the mass of the monomers is considered and then the polymer is not in its
finished polymerization state. If that latter state is required, then, the
residual chain must be capped with both the left and the right end caps.

\sa MsXpS::libXpertMassCore::Enums::SelectionType
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::mpa_indexRangeCollection

\brief The IndexRangeCollection container holds all the IndexRange
instances that describe what regions of the \l Polymer need to be accounted for
in the calculation. Most generally, there will be only one IndexRange, but there
might be situations in which multiple regions might be selected.
*/


/*!
\brief Constructs a CalcOptions instance with default initialization.
*/
CalcOptions::CalcOptions(QObject *parent)
  : QObject(parent), mpa_indexRangeCollection(new IndexRangeCollection(this))
{
}

/*!
\brief Constructs a CalcOptions instance using the parameters, including
parentship establishment with \a parent.

The following members are initialized using the parameters:

\list

\li \a deepCalculation: initializes m_deepCalculation.

\li \a mass_type: initializes m_massType

\li \a capping: initializes m_capType

\li \a monomer_entities: initializes m_monomerEntities.

\li \a polymer_entities: initializes m_polymerEntities

\endlist

The selection type is set by default to Enums::SelectionType::OLIGOMERS (not
residual chain).
*/
CalcOptions::CalcOptions(bool deepCalculation,
                         Enums::MassType mass_type,
                         Enums::CapType capping,
                         Enums::ChemicalEntity monomer_entities,
                         Enums::ChemicalEntity polymer_entities,
                         QObject *parent)
  : QObject(parent),
    m_deepCalculation(deepCalculation),
    m_massType(mass_type),
    m_capType(capping),
    m_monomerEntities(monomer_entities),
    m_polymerEntities(polymer_entities),
    mpa_indexRangeCollection(new IndexRangeCollection(this))
{
}

/*!
\brief Construct a CalcOptions instance as a copy of \a other setting parentship
to \a parent.
*/
CalcOptions::CalcOptions(const CalcOptions &other, QObject *parent)
  : QObject(parent),
    m_deepCalculation(other.m_deepCalculation),
    m_massType(other.m_massType),
    m_capType(other.m_capType),
    m_monomerEntities(other.m_monomerEntities),
    m_polymerEntities(other.m_polymerEntities),
    m_selectionType(other.m_selectionType),
    mpa_indexRangeCollection(
      new IndexRangeCollection(*other.mpa_indexRangeCollection, this))
{
  qDebug()
    << "In pseudo copy constructor, other IndexRangeCollection indices as text:"
    << other.getIndexRangeCollectionCstRef().indicesAsText();

  // mp_indexRangeCollection = new IndexRangeCollection(this);
  //
  // foreach(const IndexRange *item,
  //         other.getIndexRangeCollectionCstRef().getRangesCstRef())
  //   mp_indexRangeCollection->appendIndexRange(*item);

  qDebug().noquote()
    << "After pseudo copy constructor, with other calc options:"
    << other.toString() << "\n this calc options:" << this->toString();
}

/*!
\brief Assigns \a other to this instance and returns a reference to this
instance.

If parent is not nullptr, parentship is set using \a parent.
*/
CalcOptions &
CalcOptions::initialize(const CalcOptions &other, QObject *parent)
{
  if(this == &other)
    return *this;

  if(parent != nullptr)
    QObject::setParent(parent);

  m_deepCalculation = other.m_deepCalculation;
  m_massType        = other.m_massType;
  m_capType         = other.m_capType;
  m_monomerEntities = other.m_monomerEntities;
  m_polymerEntities = other.m_polymerEntities;
  m_selectionType   = other.m_selectionType;

  mpa_indexRangeCollection->setIndexRanges(
    other.mpa_indexRangeCollection->getRangesCstRef());

  return *this;
}

/*!
\brief Allocates a new instance as a copy of this instance, setting parentship
with \a parent.
*/
CalcOptions *
CalcOptions::clone(QObject *parent)
{
  CalcOptions *copy_p = new CalcOptions(*this, parent);
  return copy_p;
}

/*!
 \brief Allocates a new instance as a copy of \a other, setting parentship
with \a parent.
 */
CalcOptions *
CalcOptions::clone(const CalcOptions &other, QObject *parent)
{
  CalcOptions *copy_p = new CalcOptions(other, parent);
  return copy_p;
}

/*!
\brief Destructs this CalcOptions instance.
*/
CalcOptions::~CalcOptions()
{
  delete mpa_indexRangeCollection;
}

/*!
\brief Sets to \a deep the configuration defining if the mass calculations must
involve the recalculation of the masses of all the \l{Monomer} instances.

The deep calculation is typically requested when computing masses involving
Monomer instances that might be modified with \l{Modif} instances.

\sa m_deepCalculation
*/
void
CalcOptions::setDeepCalculation(bool deep)
{
  m_deepCalculation = deep;
}

/*!
\brief Returns if the calculation should be deep.

\sa m_deepCalculation
*/
bool
CalcOptions::isDeepCalculation() const
{
  return m_deepCalculation;
}

/*!
\brief Copies \a index_range to the member IndexRangeCollection.

\note The member IndexRangeCollection instance is first emptied, making
index_range essentially replacing its contents with a copy of \a index_range.
*/
void
CalcOptions::setIndexRange(const IndexRange &index_range)
{
  mpa_indexRangeCollection->setIndexRange(index_range);
}

/*!
\brief Creates an IndexRange instance with \a index_start and \a index_stop and
sets it as the sole member of the member IndexRangeCollection instance.

\note The member IndexRangeCollection container is first emptied, making the new
IndexRange instance essentially replacing the container contents.
*/
void
CalcOptions::setIndexRange(qsizetype index_start, qsizetype index_stop)
{
  setIndexRange(IndexRange(index_start, index_stop));
}

/*!
\brief Allocates a copy of each IndexRange instance in the \a index_ranges
container and adds it to the member IndexRangeCollection instance.

\note The member IndexRangeCollection container is first emptied, having thus \a
index_ranges essentially replacing its contents.
*/
void
CalcOptions::setIndexRanges(const IndexRangeCollection &index_ranges)
{
  mpa_indexRangeCollection->setIndexRanges(index_ranges);
}

/*!
\brief Returns a const reference to the member IndexRangeCollection instance.
*/
const IndexRangeCollection &
CalcOptions::getIndexRangeCollectionCstRef() const
{
  return *mpa_indexRangeCollection;
}

/*!
\brief Returns a reference to the member IndexRangeCollection instance.
*/
IndexRangeCollection &
CalcOptions::getIndexRangeCollectionRef()
{
  return *mpa_indexRangeCollection;
}

/*!
\brief Returns the member IndexRangeCollection instance.
*/
IndexRangeCollection *
CalcOptions::getIndexRangeCollection()
{
  return mpa_indexRangeCollection;
}

/*!
\brief Sets the mass type to \a mass_type.

\sa m_massType
*/
void
CalcOptions::setMassType(Enums::MassType mass_type)
{
  m_massType = mass_type;
}

/*!
\brief Returns the mass type.

\sa m_massType
*/
Enums::MassType
CalcOptions::getMassType() const
{
  return m_massType;
}

/*!
\brief Set the selection type to \a selection_type.

\sa m_selectionType
*/
void
CalcOptions::setSelectionType(Enums::SelectionType selection_type)
{
  m_selectionType = selection_type;
}

/*!
\brief Returns the selection type.

\sa m_selectionType
*/
Enums::SelectionType
CalcOptions::getSelectionType() const
{
  return m_selectionType;
}

/*!
\brief Sets the cap type to \a cap_type.

\sa m_capType
*/
void
CalcOptions::setCapType(Enums::CapType cap_type)
{
  m_capType = cap_type;
}

/*!
\brief Returns the cap type.

\sa m_capType
*/
Enums::CapType
CalcOptions::getCapType() const
{
  return m_capType;
}

/*!
\brief Sets the monomer entities to \a monomer_chem_ent.

\sa m_monomerEntities
*/
void
CalcOptions::setMonomerEntities(Enums::ChemicalEntity monomer_chem_ent)
{
  m_monomerEntities = monomer_chem_ent;
}

/*!
\brief Returns the monomer entities.

\sa m_monomerEntities
*/
Enums::ChemicalEntity
CalcOptions::getMonomerEntities() const
{
  return m_monomerEntities;
}

/*!
\brief Sets the polymer entities to \a polymer_chem_ent.

\sa m_polymerEntities
*/
void
CalcOptions::setPolymerEntities(Enums::ChemicalEntity polymer_chem_ent)
{
  m_polymerEntities = polymer_chem_ent;
}

/*!
\brief Returns the polymer entities.

\sa m_polymerEntities
*/
Enums::ChemicalEntity
CalcOptions::getPolymerEntities() const
{
  return m_polymerEntities;
}

/*!
\brief Returns true if this instance is identical to \a other, false otherwise.
*/
bool
CalcOptions::operator==(const CalcOptions &other) const
{
  if(m_deepCalculation != other.m_deepCalculation ||
     m_massType != other.m_massType || m_capType != other.m_capType ||
     m_monomerEntities != other.m_monomerEntities ||
     m_polymerEntities != other.m_polymerEntities ||
     m_selectionType != other.m_selectionType)
    return false;

  if(mpa_indexRangeCollection->size() != other.mpa_indexRangeCollection->size())
    return false;

  for(qsizetype iter = 0; iter < mpa_indexRangeCollection->size(); ++iter)
    if(mpa_indexRangeCollection->getRangeCstRefAt(iter) !=
       other.mpa_indexRangeCollection->getRangeCstRefAt(iter))
      return false;

  return true;
}

/*!
\brief Returns true if this instance is different than \a other, false
otherwise.
*/
bool
CalcOptions::operator!=(const CalcOptions &other) const
{
  return !operator==(other);
}

/*!
\brief Returns a string describing this CalcOptions instance.
*/
QString
CalcOptions::toString() const
{
  // qDebug() << "\n~~~~~~~~~~~~~~ CalcOptions instance ~~~~~~~~~~~~~\n"
  //          << this << "\n"
  //          << "m_deepCalculation:" << m_deepCalculation
  //          << "\n m_massType:" << massTypeMap[m_massType]
  //          << "\n m_capType:" << capTypeMap[m_capType] << "\n ";

  QString text;

  text += QString("m_deepCalculation: %1\n").arg(m_deepCalculation);
  text += QString("m_massType: %1\n").arg(massTypeMap[m_massType]);
  text += QString("m_capType: %1\n").arg(capTypeMap[m_capType]);

  if(!mpa_indexRangeCollection->getRangesCstRef().size())
    text += "No IndexRange instances\n";
  else
    text += QString("IndexRange instances (%1 instance(s)):\n")
              .arg(mpa_indexRangeCollection->getRangesCstRef().size());

  foreach(const IndexRange *item, mpa_indexRangeCollection->getRangesCstRef())
    {
      text += QString("[%1 - %2]\n").arg(item->m_start).arg(item->m_stop);
    }

  text += QString("%1: %2\n")
            .arg("Monomer chemical entities")
            .arg(chemicalEntityMap[m_monomerEntities]);
  text += QString("%1: %2\n")
            .arg("Polymer chemical entities")
            .arg(chemicalEntityMap[m_polymerEntities]);
  text += QString("%1: %2\n")
            .arg("Selection type")
            .arg(selectionTypeMap[m_selectionType]);

  return text;
}

void
CalcOptions::registerJsConstructor(QJSEngine *engine)
{
  if(!engine)
    {
      qDebug() << "Cannot register CalcOptions class: engine is null";
      return;
    }

  // Register the meta object as a constructor

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


} // namespace libXpertMassCore
} // namespace MsXpS
