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

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_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.

\sa Enums::CapType
*/

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

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

\sa Enums::ChemicalEntity
*/

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

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

\sa Enums::ChemicalEntity
*/

/*!
\variable MsXpS::libXpertMassCore::CalcOptions::m_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::mp_indexRangeCollection

\brief The IndexRangeCollection container that contains all the IndexRange
instances that describes 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 an empty CalcOptions instance.
*/
CalcOptions::CalcOptions(QObject *parent)
  : QObject(parent), mp_indexRangeCollection(new IndexRangeCollection(this))
{
}

/*!
\brief Constructs a CalcOptions instance.

All the members are initialized using the parameters:

\list
\li \a deepCalculation: m_deepCalculation (defaults to false)

\li \a mass_type: m_massType (defaults to Enums::MassType::BOTH)

\li \a capping: m_capType (defaults to Enums::CapType::BOTH)

\li \a monomer_entities The monomer entities to be accounted for in the
calculations (defaults to Enums::ChemicalEntity::NONE)

\li \a polymer_entities: The polymer entities to be accounted for in the
calculations (defaults to Enums::ChemicalEntity::NONE)
\endlist

The selection type is set by default to Oligomer (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),
    mp_indexRangeCollection(new IndexRangeCollection(this))
{
}

/*!
\brief Construct a CalcOptions instance as a copy of \a other.
*/
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),
    mp_indexRangeCollection(
      new IndexRangeCollection(*other.mp_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 Returns a reference to this CalcOptions after having initialized it using
\a other.
*/
CalcOptions &
CalcOptions::initialize(const CalcOptions &other)
{
  if(this == &other)
    return *this;

  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;

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

  return *this;
}

/*!
\brief Returns an newly allocated CalcOptions instance with parent set to \a
parent.
*/
CalcOptions *
CalcOptions::clone(QObject *parent)
{
  CalcOptions *copy_p = new CalcOptions(parent);
  copy_p->initialize(*this);
  return copy_p;
}

/*!
 \ b*rief Returns an newly allocated CalcOptions instance initialized using \a
 other and with parent set to \a parent.
 */
CalcOptions *
CalcOptions::clone(const CalcOptions &other, QObject *parent)
{
  CalcOptions *copy_p = new CalcOptions(parent);
  copy_p->initialize(other);
  return copy_p;
}

/*!
\brief Destructs this CalcOptions object.

Clear the member coordinateList.
*/
CalcOptions::~CalcOptions()
{
}

/*!
\brief Sets to \a deep the configuration defining if the mass calculations must
involve the recalculation of the masses of all the monomers.

The deep calculation is typically requested when computing masses involving
Monomer instance that might be modified with 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 instance.

\note The 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)
{
  mp_indexRangeCollection->setIndexRange(index_range);
}

/*!
\brief Creates an IndexRange instance with \a index_start and \a index_end and
sets it as the sole member of the member IndexRangeCollection instance.
*/
void
CalcOptions::setIndexRange(qsizetype index_start, qsizetype index_end)
{
  setIndexRange(IndexRange(index_start, index_end));
}

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

\note The IndexRangeCollection instance is first emptied, essentially replacing
its contents with a copy of those in \a index_ranges.
*/
void
CalcOptions::setIndexRanges(const IndexRangeCollection &index_ranges)
{
  mp_indexRangeCollection->setIndexRanges(index_ranges);
}

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

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

/*!
\brief Returns a reference to the member IndexRangeCollection instance.
*/
IndexRangeCollection *
CalcOptions::getIndexRangeCollection()
{
  return mp_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(mp_indexRangeCollection->size() != other.mp_indexRangeCollection->size())
    return false;

  for(qsizetype iter = 0; iter < mp_indexRangeCollection->size(); ++iter)
    if(mp_indexRangeCollection->getRangeCstRefAt(iter) !=
       other.mp_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(!mp_indexRangeCollection->getRangesCstRef().size())
    text += "No IndexRange instances\n";
  else
    text += QString("IndexRange instances (%1 instance(s)):\n")
              .arg(mp_indexRangeCollection->getRangesCstRef().size());

  foreach(const IndexRange *item, mp_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
