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


/////////////////////// Qt includes
#include <QMessageBox>
#include <QSettings>


/////////////////////// pappsomspp includes


/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassCore/PolChemDef.hpp>
#include <MsXpS/libXpertMassCore/CleavageAgent.hpp>


/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "CleavageAgentDefDlg.hpp"
#include "PolChemDefWnd.hpp"


namespace MsXpS
{

namespace MassXpert
{


CleavageAgentDefDlg::CleavageAgentDefDlg(
  libXpertMassCore::PolChemDefSPtr pol_chem_def_csp,
  PolChemDefWnd *pol_chem_def_wnd,
  const QString &config_settings_file_path,
  const QString &application_name,
  const QString &description)
  : AbstractPolChemDefDependentDlg(pol_chem_def_csp,
                                   pol_chem_def_wnd,
                                   config_settings_file_path,
                                   "CleavageAgentDefDlg",
                                   application_name,
                                   description),
    mref_cleavageAgents(pol_chem_def_csp->getCleavageAgentsRef())
{
  if(!initialize())
    qFatal()
      << "Programming error. Failed to initialize the dialog window.";
}


CleavageAgentDefDlg::~CleavageAgentDefDlg()
{
}


void
CleavageAgentDefDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  // No real close, because we did not ask that
  // close==destruction. Thus we only hide the dialog remembering its
  // position and size.


  mp_pol_chem_def_wnd_p->m_ui.cleavagePushButton->setChecked(false);

  writeSettings();
}


void
CleavageAgentDefDlg::readSettings()
{
  QSettings settings(m_settings_file_path, QSettings::IniFormat);

  settings.beginGroup(m_wndTypeName);
  restoreGeometry(settings.value("geometry").toByteArray());
  m_ui.splitter->restoreState(settings.value("splitter").toByteArray());
  settings.endGroup();
}


void
CleavageAgentDefDlg::writeSettings()
{

  QSettings settings(m_settings_file_path, QSettings::IniFormat);

  settings.beginGroup(m_wndTypeName);
  restoreGeometry(settings.value("geometry").toByteArray());
  settings.setValue("splitter", m_ui.splitter->saveState());
  settings.endGroup();
}


bool
CleavageAgentDefDlg::initialize()
{
  m_ui.setupUi(this);

  setWindowIcon(qApp->windowIcon());

  // Now we need to actually show the window title (that element is empty in
  // m_ui)
  displayWindowTitle();

  // Set all the cleavage_agent_sps to the list widget.

  for(const libXpertMassCore::CleavageAgentCstSPtr cleavage_agent_csp :
      mref_cleavageAgents)
    m_ui.cleavageAgentListWidget->addItem(cleavage_agent_csp->getName());

  readSettings();

  // Make the connections.

  connect(m_ui.addCleavageAgentPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addCleavageAgentPushButtonClicked()));

  connect(m_ui.removeCleavageAgentPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeCleavageAgentPushButtonClicked()));

  connect(m_ui.moveUpCleavageAgentPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpCleavageAgentPushButtonClicked()));

  connect(m_ui.moveDownCleavageAgentPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownCleavageAgentPushButtonClicked()));

  connect(m_ui.addCleavageRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addCleavageRulePushButtonClicked()));

  connect(m_ui.removeCleavageRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeCleavageRulePushButtonClicked()));

  connect(m_ui.moveUpCleavageRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpCleavageRulePushButtonClicked()));

  connect(m_ui.moveDownCleavageRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownCleavageRulePushButtonClicked()));

  connect(m_ui.applyCleavageAgentPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyCleavageAgentPushButtonClicked()));

  connect(m_ui.applyCleavageRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyCleavageRulePushButtonClicked()));

  connect(m_ui.validatePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(validatePushButtonClicked()));

  connect(m_ui.cleavageAgentListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(cleavageAgentListWidgetItemSelectionChanged()));

  connect(m_ui.cleavageRuleListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(cleavageRuleListWidgetItemSelectionChanged()));

  return true;
}


void
CleavageAgentDefDlg::addCleavageAgentPushButtonClicked()
{
  // We are asked to add a new cleavage_agent_sp. We'll add it right after the
  // current item.

  //  Start with the creation of the CleavageAgent.

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    std::make_shared<libXpertMassCore::CleavageAgent>(
      msp_polChemDef, tr("Type Spec Name"), tr("Type Pattern"));

  // Returns -1 if the list is empty.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  if(index == -1)
    {
      mref_cleavageAgents.push_back(cleavage_agent_sp);
    }
  else
    {
      mref_cleavageAgents.insert(mref_cleavageAgents.begin() + index,
                                 cleavage_agent_sp);
    }

  m_ui.cleavageAgentListWidget->insertItem(index, cleavage_agent_sp->getName());

  setModified();

  // Needed so that the setCurrentRow() call below actually set the
  // current row!
  if(index <= 0)
    index = 0;

  m_ui.cleavageAgentListWidget->setCurrentRow(index);

  // Erase cleavageRule data that might be left over by precedent current
  // cleavage_agent_sp.
  updateCleavageRuleDetails(nullptr);

  // Set the focus to the lineEdit that holds the name of the cleavage_agent_sp.
  m_ui.nameLineEdit->setFocus();
  m_ui.nameLineEdit->selectAll();
}


void
CleavageAgentDefDlg::removeCleavageAgentPushButtonClicked()
{
  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the current cleavage_agent_sp.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  QListWidgetItem *item = m_ui.cleavageAgentListWidget->takeItem(index);
  delete item;

  mref_cleavageAgents.erase(mref_cleavageAgents.begin() + index);

  setModified();

  // If there are remaining items, we want to set the next item the
  // currentItem. If not, then, the currentItem should be the one
  // preceding the cleavage_agent_sp that we removed.

  if(m_ui.cleavageAgentListWidget->count() >= index + 1)
    {
      m_ui.cleavageAgentListWidget->setCurrentRow(index);
      cleavageAgentListWidgetItemSelectionChanged();
    }

  // If there are no more items in the cleavage_agent_sp list, remove all the
  // items from the cleavageRuleList.

  if(!m_ui.cleavageAgentListWidget->count())
    {
      m_ui.cleavageRuleListWidget->clear();
      clearAllDetails();
    }
}


void
CleavageAgentDefDlg::moveUpCleavageAgentPushButtonClicked()
{
  // Move the current row to one index less.

  // If no cleavage_agent_sp is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the cleavage_agent_sp and the cleavage_agent_sp itself.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  // If the item is already at top of list, do nothing.
  if(!index)
    return;

  std::swap(mref_cleavageAgents[index], mref_cleavageAgents[index - 1]);

  QListWidgetItem *item = m_ui.cleavageAgentListWidget->takeItem(index);

  m_ui.cleavageAgentListWidget->insertItem(index - 1, item);
  m_ui.cleavageAgentListWidget->setCurrentRow(index - 1);
  cleavageAgentListWidgetItemSelectionChanged();

  setModified();
}


void
CleavageAgentDefDlg::moveDownCleavageAgentPushButtonClicked()
{
  // Move the current row to one index less.

  // If no cleavage_agent_sp is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the cleavage_agent_sp and the cleavage_agent_sp itself.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  // If the item is already at bottom of list, do nothing.
  if(index == m_ui.cleavageAgentListWidget->count() - 1)
    return;

  std::swap(mref_cleavageAgents[index], mref_cleavageAgents[index + 1]);

  QListWidgetItem *item = m_ui.cleavageAgentListWidget->takeItem(index);
  m_ui.cleavageAgentListWidget->insertItem(index + 1, item);
  m_ui.cleavageAgentListWidget->setCurrentRow(index + 1);
  cleavageAgentListWidgetItemSelectionChanged();

  setModified();
}


void
CleavageAgentDefDlg::addCleavageRulePushButtonClicked()
{
  // We are asked to add a new cleavageRule. We'll add it right after the
  // current item. Note however, that one cleavage_agent_sp has to be selected.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    {
      QMessageBox::information(this,
                               tr("MassXpert3 - Cleavage agent definition"),
                               tr("Please, select a cleavage first."),
                               QMessageBox::Ok);
      return;
    }

  // Get the index of the current cleavage_agent_sp so that we know to which
  // cleavage_agent_sp we'll add the cleavageRule.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  // What's the actual cleavage_agent_sp?
  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // Allocate the new cleavageRule.
  libXpertMassCore::CleavageRuleSPtr cleavage_rule_sp =
    std::make_shared<libXpertMassCore::CleavageRule>(
      msp_polChemDef, tr("Type Rule Name"), "", "", "", "");

  // Get the row index of the current cleavageRule item. Returns -1 if the
  // list is empty.
  index = m_ui.cleavageRuleListWidget->currentRow();

  m_ui.cleavageRuleListWidget->insertItem(index, cleavage_rule_sp->getName());

  // Needed so that the setCurrentRow() call below actually set the
  // current row!
  if(index <= 0)
    index = 0;

  cleavage_agent_sp->getRulesRef().insert(
    cleavage_agent_sp->getRulesRef().begin() + index, cleavage_rule_sp);

  m_ui.cleavageRuleListWidget->setCurrentRow(index);

  setModified();

  // Set the focus to the lineEdit that holds the mass of the cleavageRule.
  m_ui.cleavageRuleNameLineEdit->setFocus();
  m_ui.cleavageRuleNameLineEdit->selectAll();
}


void
CleavageAgentDefDlg::removeCleavageRulePushButtonClicked()
{
  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageRuleListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the current cleavage_agent_sp so that we know from
  // which cleavage_agent_sp we'll remove the cleavageRule.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // Get the index of the current cleavageRule.
  index = m_ui.cleavageRuleListWidget->currentRow();

  // First remove the item from the listwidget because that will have
  // cleavageRuleListWidgetItemSelectionChanged() triggered and we have to
  // have the item in the cleavageRule list in the cleavage_agent_sp! Otherwise
  // a crash occurs.
  QListWidgetItem *item = m_ui.cleavageRuleListWidget->takeItem(index);
  delete item;

  // Remove the cleavageRule from the cleavage_agent_sp proper.
  std::vector<libXpertMassCore::CleavageRuleSPtr> &cleavage_rules =
    cleavage_agent_sp->getRulesRef();
  cleavage_rules.erase(cleavage_rules.begin() + index);

  // If there are remaining items, we want to set the next item the
  // currentItem. If not, then, the currentItem should be the one
  // preceding the cleavage_agent_sp that we removed.

  if(m_ui.cleavageRuleListWidget->count() >= index + 1)
    {
      m_ui.cleavageRuleListWidget->setCurrentRow(index);
      cleavageRuleListWidgetItemSelectionChanged();
    }

  // If there are no more items in the cleavageRule list, remove all the
  // details.

  if(!m_ui.cleavageRuleListWidget->count())
    {
      updateCleavageRuleDetails(0);
    }
  else
    {
    }

  setModified();
}


void
CleavageAgentDefDlg::moveUpCleavageRulePushButtonClicked()
{
  // Move the current row to one index less.

  // If no cleavageRule is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageRuleListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the cleavage_agent_sp to which the cleavageRule belongs.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // Get the index of the current cleavageRule item.
  index = m_ui.cleavageRuleListWidget->currentRow();

  // If the item is already at top of list, do nothing.
  if(!index)
    return;

  // Get the cleavage rules from the cleavage_agent_sp.
  std::vector<libXpertMassCore::CleavageRuleSPtr> &cleavage_rules =
    cleavage_agent_sp->getRulesRef();

  std::swap(cleavage_rules[index], cleavage_rules[index - 1]);

  QListWidgetItem *item = m_ui.cleavageRuleListWidget->takeItem(index);
  m_ui.cleavageRuleListWidget->insertItem(index - 1, item);
  m_ui.cleavageRuleListWidget->setCurrentRow(index - 1);
  cleavageRuleListWidgetItemSelectionChanged();

  setModified();
}


void
CleavageAgentDefDlg::moveDownCleavageRulePushButtonClicked()
{
  // Move the current row to one index less.

  // If no cleavageRule is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageRuleListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the cleavage_agent_sp to which the cleavageRule belongs.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // Get the index of the current cleavageRule item.
  index = m_ui.cleavageRuleListWidget->currentRow();

  // If the item is already at bottom of list, do nothing.
  if(index == m_ui.cleavageRuleListWidget->count() - 1)
    return;

  // Get the cleavage rules from the cleavage_agent_sp.
  std::vector<libXpertMassCore::CleavageRuleSPtr> &cleavage_rules =
    cleavage_agent_sp->getRulesRef();

  std::swap(cleavage_rules[index], cleavage_rules[index + 1]);

  QListWidgetItem *item = m_ui.cleavageRuleListWidget->takeItem(index);
  m_ui.cleavageRuleListWidget->insertItem(index + 1, item);
  m_ui.cleavageRuleListWidget->setCurrentRow(index + 1);
  cleavageRuleListWidgetItemSelectionChanged();

  setModified();
}


void
CleavageAgentDefDlg::applyCleavageAgentPushButtonClicked()
{
  // We are asked to apply the data for the cleavage_agent_sp.

  // If no cleavage_agent_sp is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the current cleavage_agent_sp item.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // We do not want more than one cleavage_agent_sp by the same name or the same
  // symbol.

  QString editName    = m_ui.nameLineEdit->text();
  QString editPattern = m_ui.patternLineEdit->text();

  // If a cleavage_agent_sp is found in the list with the same name, and that
  // cleavage_agent_sp is not the one that is current in the cleavage_agent_sp
  // list, then we are making a double entry, which is not allowed.

  std::vector<libXpertMassCore::CleavageAgentSPtr>::iterator the_iterator =
    std::find_if(
      mref_cleavageAgents.begin(),
      mref_cleavageAgents.end(),
      [editName](
        const libXpertMassCore::CleavageAgentSPtr iter_cleavage_agent_sp) {
        return iter_cleavage_agent_sp->getName() == editName;
      });

  if(the_iterator != mref_cleavageAgents.end() &&
     std::distance(mref_cleavageAgents.begin(), the_iterator) != index)
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - Cleavage agent definition"),
        tr("A cleavage agent with same name exists already."),
        QMessageBox::Ok);
      return;
    }

  //  Make a temporary object to cleanly test the pattern.
  libXpertMassCore::CleavageAgentSPtr new_cleavage_agent_sp =
    std::make_shared<libXpertMassCore::CleavageAgent>(
      msp_polChemDef, "NOT_SET", editPattern);

  if(!new_cleavage_agent_sp->parse())
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Cleavage agent definition"),
                           tr("The cleavage failed to parse."),
                           QMessageBox::Ok);

      new_cleavage_agent_sp.reset();
      return;
    }

  //  Not useful anymore.
  new_cleavage_agent_sp.reset();

  //  At this point we know the data are correct,  set them to the object
  //  being managed.
  cleavage_agent_sp->setName(editName);
  cleavage_agent_sp->setPattern(editPattern);

  // Update the list widget item.

  QListWidgetItem *item = m_ui.cleavageAgentListWidget->currentItem();
  item->setData(Qt::DisplayRole, cleavage_agent_sp->getName());

  setModified();
}


void
CleavageAgentDefDlg::applyCleavageRulePushButtonClicked()
{
  // We are asked to apply the data for the cleavageRule.

  // If no cleavageRule is selected, just return.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageRuleListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the cleavage_agent_sp to which the cleavageRule belongs.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);

  // Get the index of the current cleavageRule item.
  index = m_ui.cleavageRuleListWidget->currentRow();

  // Get the cleavageRule itself from the cleavage_agent_sp.
  libXpertMassCore::CleavageRuleSPtr cleavage_rule_sp =
    cleavage_agent_sp->getRulesRef().at(index);

  QString editName = m_ui.cleavageRuleNameLineEdit->text();

  QString leftCode    = m_ui.leftCodeLineEdit->text();
  QString leftFormula = m_ui.leftFormulaLineEdit->text();

  QString rightCode    = m_ui.rightCodeLineEdit->text();
  QString rightFormula = m_ui.rightFormulaLineEdit->text();

  // If a cleavageRule is found in the list with the same name, and that
  // cleavageRule is not the one that is current in the cleavageRule list,
  // then we are making a double entry, which is not allowed.

  std::vector<libXpertMassCore::CleavageRuleSPtr>::iterator the_iterator =
    std::find_if(
      cleavage_agent_sp->getRulesRef().begin(),
      cleavage_agent_sp->getRulesRef().end(),
      [editName](
        const libXpertMassCore::CleavageRuleSPtr iter_cleavage_rule_sp) {
        return iter_cleavage_rule_sp->getName() == editName;
      });

  if(the_iterator != cleavage_agent_sp->getRulesRef().end() &&
     std::distance(cleavage_agent_sp->getRulesRef().begin(), the_iterator) !=
       index)
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Cleavage agent definition"),
                           tr("A cleavage rule with same name "
                              "exists already."),
                           QMessageBox::Ok);
      return;
    }

  if(!leftCode.isEmpty())
    {
      if(msp_polChemDef->getMonomerCstSPtrByCode(leftCode) == nullptr)
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Cleavage agent definition"),
                               tr("The left code is not known."),
                               QMessageBox::Ok);
          return;
        }

      libXpertMassCore::Formula formula(leftFormula);

      libXpertMassCore::IsotopicDataSPtr isotopic_data_sp =
        msp_polChemDef->getIsotopicDataSPtr();

      MsXpS::libXpertMassCore::ErrorList error_list;

      if(!formula.validate(isotopic_data_sp, &error_list))
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - libXpertMassCore::Monomer definition"),
            tr("The formula failed to validate."),
            QMessageBox::Ok);
          return;
        }
    }

  if(!rightCode.isEmpty())
    {
      if(msp_polChemDef->getMonomerCstSPtrByCode(rightCode) == nullptr)
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Cleavage agent definition"),
                               tr("The right code is not known."),
                               QMessageBox::Ok);
          return;
        }

      libXpertMassCore::Formula formula(rightFormula);

      libXpertMassCore::IsotopicDataSPtr isotopic_data_sp =
        msp_polChemDef->getIsotopicDataSPtr();

      MsXpS::libXpertMassCore::ErrorList error_list;

      if(!formula.validate(isotopic_data_sp, &error_list))
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - libXpertMassCore::Monomer definition"),
            tr("The formula failed to validate."),
            QMessageBox::Ok);
          return;
        }
    }

  cleavage_rule_sp->setName(editName);

  cleavage_rule_sp->setLeftCode(leftCode);
  cleavage_rule_sp->setLeftFormula(libXpertMassCore::Formula(leftFormula));

  cleavage_rule_sp->setRightCode(rightCode);
  cleavage_rule_sp->setRightFormula(libXpertMassCore::Formula(rightFormula));

  // Update the list widget item.

  QListWidgetItem *item = m_ui.cleavageRuleListWidget->currentItem();
  item->setData(Qt::DisplayRole, cleavage_rule_sp->getName());

  setModified();
}


bool
CleavageAgentDefDlg::validatePushButtonClicked()
{
  libXpertMassCore::ErrorList error_list;

  // All we have to do is validate the cleavage_agent_sp definition. For that
  // we'll go in the listwidget items one after the other and make sure that
  // everything is fine and that colinearity is perfect between the
  // cleavage_agent_sp list and the listwidget.

  std::size_t itemCount = m_ui.cleavageAgentListWidget->count();

  if(itemCount != mref_cleavageAgents.size())
    {
      error_list.push_back(
        QString("\nThe number of cleavage agents in the list widget \n"
                "and in the list of cleavage agents is not identical.\n"));

      QMessageBox::warning(
        this,
        tr("MassXpert3 - Cleavage agent definition"),
        libXpertMassCore::Utils::joinErrorList(error_list, "\n"),
        QMessageBox::Ok);
      return false;
    }

  for(std::size_t iter = 0; iter < mref_cleavageAgents.size(); ++iter)
    {
      QListWidgetItem *item = m_ui.cleavageAgentListWidget->item(iter);

      libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
        mref_cleavageAgents.at(iter);

      if(item->text() != cleavage_agent_sp->getName())
        error_list.push_back(QString("\nCleavage at index %1 has not the same\n"
                                     "name as the list widget item at the\n"
                                     "same index.\n")
                               .arg(iter));

      if(!cleavage_agent_sp->validate(&error_list))
        error_list.push_back(
          QString("\nCleavage at index %1 failed to validate.\n").arg(iter));
    }

  if(error_list.size())
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - Cleavage agent definition"),
        libXpertMassCore::Utils::joinErrorList(error_list, "\n"),
        QMessageBox::Ok);
      return false;
    }
  else
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Cleavage agent definition"),
                           ("Validation: success\n"),
                           QMessageBox::Ok);
    }

  return true;
}


void
CleavageAgentDefDlg::cleavageAgentListWidgetItemSelectionChanged()
{
  // The cleavage_agent_sp item has changed. Empty the cleavageRule list and
  // update its contents. Update the details for the cleavage_agent_sp.

  // The list is a single-item-selection list.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageAgentListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the current cleavage_agent_sp.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);
  Q_ASSERT(cleavage_agent_sp);

  // Set the data of the cleavage_agent_sp to their respective widgets.
  updateCleavageAgentIdentityDetails(cleavage_agent_sp);

  // The list of cleavageRules
  m_ui.cleavageRuleListWidget->clear();

  for(const libXpertMassCore::CleavageRuleSPtr &cleavage_rule_sp :
      cleavage_agent_sp->getRulesCstRef())
    {
      m_ui.cleavageRuleListWidget->addItem(cleavage_rule_sp->getName());
    }

  if(!m_ui.cleavageRuleListWidget->count())
    updateCleavageRuleDetails(0);
  else
    {
      // And now select the first row in the cleavageRule list widget.
      m_ui.cleavageRuleListWidget->setCurrentRow(0);
    }
}


void
CleavageAgentDefDlg::cleavageRuleListWidgetItemSelectionChanged()
{
  // The cleavageRule item has changed. Update the details for the cleavageRule.

  // The list is a single-item-selection list.

  QList<QListWidgetItem *> selectedList =
    m_ui.cleavageRuleListWidget->selectedItems();

  if(selectedList.size() != 1)
    return;

  // Get the index of the current cleavage_agent_sp.
  int index = m_ui.cleavageAgentListWidget->currentRow();

  // Find the cleavageRule object in the list of cleavageRules.
  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp =
    mref_cleavageAgents.at(index);
  Q_ASSERT(cleavage_agent_sp);

  // Get the index of the current cleavageRule.
  index = m_ui.cleavageRuleListWidget->currentRow();

  // Get the cleavageRule that is currently selected from the
  // cleavage_agent_sp's list of cleavageRules.
  libXpertMassCore::CleavageRuleSPtr cleavage_rule_sp =
    cleavage_agent_sp->getRulesCstRef().at(index);
  Q_ASSERT(cleavage_rule_sp != nullptr);

  // Set the data of the cleavageRule to their respective widgets.
  updateCleavageRuleDetails(cleavage_rule_sp);
}


void
CleavageAgentDefDlg::updateCleavageAgentIdentityDetails(
  libXpertMassCore::CleavageAgentSPtr cleavage_agent_sp)
{
  if(cleavage_agent_sp != nullptr)
    {
      m_ui.nameLineEdit->setText(cleavage_agent_sp->getName());
      m_ui.patternLineEdit->setText(cleavage_agent_sp->getPattern());
    }
  else
    {
      m_ui.nameLineEdit->setText("");
      m_ui.patternLineEdit->setText("");
    }
}


void
CleavageAgentDefDlg::updateCleavageRuleDetails(
  libXpertMassCore::CleavageRuleSPtr cleavage_rule_sp)
{
  if(cleavage_rule_sp != nullptr)
    {
      m_ui.cleavageRuleNameLineEdit->setText(cleavage_rule_sp->getName());

      m_ui.leftCodeLineEdit->setText(cleavage_rule_sp->getLeftCode());
      m_ui.leftFormulaLineEdit->setText(
        cleavage_rule_sp->getLeftFormula().getActionFormula());

      m_ui.rightCodeLineEdit->setText(cleavage_rule_sp->getRightCode());
      m_ui.rightFormulaLineEdit->setText(
        cleavage_rule_sp->getRightFormula().getActionFormula());
    }
  else
    {
      m_ui.cleavageRuleNameLineEdit->setText("");

      m_ui.leftCodeLineEdit->setText("");
      m_ui.leftFormulaLineEdit->setText("");

      m_ui.rightCodeLineEdit->setText("");
      m_ui.rightFormulaLineEdit->setText("");
    }
}


void
CleavageAgentDefDlg::clearAllDetails()
{
  m_ui.nameLineEdit->setText("");
  m_ui.patternLineEdit->setText("");

  m_ui.cleavageRuleNameLineEdit->setText("");

  m_ui.leftCodeLineEdit->setText("");
  m_ui.leftFormulaLineEdit->setText("");

  m_ui.rightCodeLineEdit->setText("");
  m_ui.rightFormulaLineEdit->setText("");
}


// VALIDATION
bool
CleavageAgentDefDlg::validate()
{
  return validatePushButtonClicked();
}

} // namespace MassXpert

} // namespace MsXpS
