/* 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/ChemicalGroupRule.hpp"

namespace MsXpS
{

namespace libXpertMassCore
{


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

\brief The ChemicalGroupRule class provides a model for refining the
acido-basic behaviour of a chemical group of either a \l Monomer object or of a
\l Modif object.

In an pkaphpidata definition file, the following xml structure
is encountered:

\code
<pkaphpidata>
<monomers>
<monomer>
<code>A</code>
<mnmchemgroup>
<name>N-term NH2</name>
<pka>9.6</pka>
<acidcharged>TRUE</acidcharged>
<polrule>left_trapped</polrule>
<chemgrouprule>
<entity>LE_PLM_MODIF</entity>
<name>Acetylation</name>
<outcome>LOST</outcome>
</chemgrouprule>
</mnmchemgroup>
<mnmchemgroup>
<name>C-term COOH</name>
<pka>2.35</pka>
<acidcharged>FALSE</acidcharged>
<polrule>right_trapped</polrule>
</mnmchemgroup>
</monomer>
<monomer>
<code>C</code>
<mnmchemgroup>
<name>N-term NH2</name>
<pka>9.6</pka>
<acidcharged>TRUE</acidcharged>
<polrule>left_trapped</polrule>
<chemgrouprule>
<entity>LE_PLM_MODIF</entity>
<name>Acetylation</name>
<outcome>LOST</outcome>
</chemgrouprule>
</mnmchemgroup>
<mnmchemgroup>
<name>C-term COOH</name>
<pka>2.35</pka>
<acidcharged>FALSE</acidcharged>
<polrule>right_trapped</polrule>
</mnmchemgroup>
<mnmchemgroup>
<name>Lateral SH2</name>
<pka>8.3</pka>
<acidcharged>FALSE</acidcharged>
<polrule>never_trapped</polrule>
</mnmchemgroup>
</monomer>
.....
<modifs>
<modif>
<name>Phosphorylation</name>
<mdfchemgroup>
<name>none_set</name>
<pka>1.2</pka>
<acidcharged>FALSE</acidcharged>
</mdfchemgroup>
<mdfchemgroup>
<name>none_set</name>
<pka>6.5</pka>
<acidcharged>FALSE</acidcharged>
</mdfchemgroup>
</modif>
</modifs>
</pkaphpidata>
\endcode

\sa ChemicalGroup,
*/


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

\brief The name of the ChemicalGroupRule instance.
*/

/*!
\variable MsXpS::libXpertMassCore::ChemicalGroupRule::m_entity

\brief The entity of the ChemicalGroupRule instance, like LE_PLM_MODIF for
\e{left end polymer modification}.
*/

/*!
\variable MsXpS::libXpertMassCore::ChemicalGroupRule::m_chemicalGroupFate

\brief The fate of the ChemicalGroupRule instance.

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

/*!
\brief Constructs a ChemicalGroupRule instance.

\list
\li \a name: The name of this ChemicalGroupRule instance.
\li \a entity: The entity of this ChemicalGroupRule instance.
\li \a fate: The fate of this ChemicalGroupRule instance.
\endlist
*/
ChemicalGroupRule::ChemicalGroupRule(const QString &name,
                                     const QString &entity,
                                     Enums::ChemicalGroupFate fate)
  : m_name(name), m_entity(entity), m_chemicalGroupFate(fate)
{
  Q_ASSERT(m_chemicalGroupFate == Enums::ChemicalGroupFate::LOST ||
           m_chemicalGroupFate == Enums::ChemicalGroupFate::PRESERVED);
}

/*!
\brief Constructs a ChemicalGroupRule instance as a copy of \a other.
*/
ChemicalGroupRule::ChemicalGroupRule(const ChemicalGroupRule &other)
  : m_name(other.m_name),
    m_entity(other.m_entity),
    m_chemicalGroupFate(other.m_chemicalGroupFate),
    m_isValid(other.m_isValid)
{
}

/*!
 \ brief Constructs this ChemicalGroupRule
*/
ChemicalGroupRule::~ChemicalGroupRule()
{
}

/*!
\brief Sets the \a name.
*/
void
ChemicalGroupRule::setName(const QString &name)
{
  m_name = name;

  ErrorList error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      qDebug()
        << "After setting name, the object did not validate successfully.";
    }
}

/*!
\brief Returns the name.
*/
QString
ChemicalGroupRule::getName()
{
  return m_name;
}

/*!
\brief Sets the \a entity.
*/
void
ChemicalGroupRule::setEntity(const QString &entity)
{
  m_entity = entity;

  ErrorList error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      qDebug()
        << "After setting entity, the object did not validate successfully.";
    }
}

/*!
\brief Returns the entity.
*/
QString
ChemicalGroupRule::getEntity()
{
  return m_entity;
}

/*!
\brief Sets the \a fate.
*/
void
ChemicalGroupRule::setFate(Enums::ChemicalGroupFate fate)
{
  m_chemicalGroupFate = fate;

  ErrorList error_list;
  m_isValid = validate(&error_list);

  if(!m_isValid)
    {
      qDebug()
        << "After setting fate, the object did not validate successfully.";
    }
}

/*!
\brief Returns the fate.
*/
Enums::ChemicalGroupFate
ChemicalGroupRule::getFate()
{
  return m_chemicalGroupFate;
}

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

  m_name              = other.m_name;
  m_entity            = other.m_entity;
  m_chemicalGroupFate = other.m_chemicalGroupFate;
  m_isValid           = other.m_isValid;

  return *this;
}

/*!
\brief Validates this instance, setting eventual error messages to \a
error_list_p.

\note \a error_list_p is not cleared.

Following a successful validation, m_isValid is set to true, to false
otherwise and that result is returned.
*/
bool
ChemicalGroupRule::validate(ErrorList *error_list_p) const
{
  qsizetype error_count = error_list_p->size();

  if(m_name.isEmpty())
    {
      qDebug() << "The ChemicalGroupRule cannot validate with an empty name.";
      error_list_p->push_back(
        "The ChemicalGroupRule cannot validate with an empty name");
    }

  if(m_entity.isEmpty())
    {
      qDebug() << "The ChemicalGroupRule cannot validate with an empty entity.";
      error_list_p->push_back(
        "The ChemicalGroupRule cannot validate with an empty entity");
    }

  m_isValid = error_list_p->size() > error_count ? false : true;

  return m_isValid;
}

/*!
\brief Returns the validity status of this instance.
*/
bool
ChemicalGroupRule::isValid() const
{
  return m_isValid;
}

/*!
\brief Parses the ChemicalGroupRule XML \a element.

Upon parsing of the \a element, its data are validated and set to this
ChemicalGroupRule instance, thus essentially initializing it.

Returns true if parsing and validation were successful, false otherwise.
*/
bool
ChemicalGroupRule::renderXmlElement(const QDomElement &element)
{
  QDomElement child;

  // In an acidobasic definition file, the following xml structure
  // is encountered:

  // <monomer>
  //  <code>C</code>
  //  <mnmchemgroup>
  //   <name>N-term NH2</name>
  //   <pka>9.6</pka>
  //   <acidcharged>TRUE</acidcharged>
  //   <polrule>left_trapped</polrule>
  //   <chemgrouprule>
  //    <entity>LE_PLM_MODIF</entity>
  //    <name>Acetylation</name>
  //    <outcome>LOST</outcome>
  //   </chemgrouprule>
  //  </mnmchemgroup>

  // The relevant DTD line is:
  // <!ELEMENT chemgrouprule(entity,name,outcome)>

  // And the element the parameter points to is:

  // <chemgrouprule>

  // Which means that element.tagName() == "chemgrouprule" and that we'll
  // have to go one step down to the first child of the current node
  // in order to get to the <entity> element.

  if(element.tagName() != "chemgrouprule")
    {
      qDebug() << "Failed to render <chemgrouprule> element: "
                  "<chemgrouprule> element not found.";
      return false;
    }

  child = element.firstChildElement("entity");

  if(child.isNull())
    {
      qDebug() << "Failed to render <chemgrouprule> element: <entity> "
                  "element not found.";
      return false;
    }

  m_entity = child.text();

  child = child.nextSiblingElement();

  if(child.isNull() || child.tagName() != "name")
    {
      qDebug() << "Failed to render <chemgrouprule> element: <name> element "
                  "not found.";
      return false;
    }

  m_name = child.text();

  child = child.nextSiblingElement();

  if(child.isNull() || child.tagName() != "outcome")
    {
      qDebug() << "Failed to render <chemgrouprule> element: <outcome> "
                  "element not found.";
      return false;
    }

  if(child.text() == "LOST")
    m_chemicalGroupFate = Enums::ChemicalGroupFate::LOST;
  else if(child.text() == "PRESERVED")
    m_chemicalGroupFate = Enums::ChemicalGroupFate::PRESERVED;
  else
    {
      qDebug() << "Failed to render <chemgrouprule> element: <outcome> "
                  "element had bad value.";
      return false;
    }

  ErrorList error_list;

  m_isValid = validate(&error_list);

  return m_isValid;
}


} // namespace libXpertMassCore
} // namespace MsXpS
