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


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


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


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


namespace MsXpS
{

namespace MassXpert
{


FragmentationPathwayDefDlg::FragmentationPathwayDefDlg(
  libXpertMassCore::PolChemDefSPtr pol_chem_def_sp,
  PolChemDefWnd *pol_chem_def_wnd_p,
  const QString &settings_file_path,
  const QString &application_name,
  const QString &description)
  : AbstractPolChemDefDependentDlg(pol_chem_def_sp,
                                   pol_chem_def_wnd_p,
                                   settings_file_path,
                                   "FragmentationPathwayDefDlg",
                                   application_name,
                                   description),
    mref_fragmentationPathways(pol_chem_def_sp->getFragmentationPathwaysRef())
{
  if(!initialize())
    qFatal() << "Failed to initialize the dialog window.";
}


FragmentationPathwayDefDlg::~FragmentationPathwayDefDlg()
{
}


void
FragmentationPathwayDefDlg::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.fragmentationPushButton->setChecked(false);

  writeSettings();
}


void
FragmentationPathwayDefDlg::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
FragmentationPathwayDefDlg::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
FragmentationPathwayDefDlg::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 fragSpecs to the list widget.
  for(libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp :
      mref_fragmentationPathways)
    m_ui.fragSpecListWidget->addItem(fragmentation_pathway_sp->getName());

  readSettings();

  // The combobox listing the available ends.

  QStringList endList;
  endList << "N/A"
          << "NE"
          << "LE"
          << "RE";

  m_ui.fragEndComboBox->addItems(endList);

  // Make the connections.

  connect(m_ui.addFragSpecPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addFragSpecPushButtonClicked()));

  connect(m_ui.removeFragSpecPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeFragSpecPushButtonClicked()));

  connect(m_ui.moveUpFragSpecPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpFragSpecPushButtonClicked()));

  connect(m_ui.moveDownFragSpecPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownFragSpecPushButtonClicked()));

  connect(m_ui.addFragRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addFragRulePushButtonClicked()));

  connect(m_ui.removeFragRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeFragRulePushButtonClicked()));

  connect(m_ui.moveUpFragRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpFragRulePushButtonClicked()));

  connect(m_ui.moveDownFragRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownFragRulePushButtonClicked()));

  connect(m_ui.applyFragSpecPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyFragSpecPushButtonClicked()));

  connect(m_ui.applyFragRulePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyFragRulePushButtonClicked()));

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

  connect(m_ui.fragSpecListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(fragSpecListWidgetItemSelectionChanged()));

  connect(m_ui.fragRuleListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(fragRuleListWidgetItemSelectionChanged()));

  return true;
}


void
FragmentationPathwayDefDlg::addFragSpecPushButtonClicked()
{
  // We are asked to add a new fragSpec. We'll add it right after the
  // current item.

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

  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    std::make_shared<libXpertMassCore::FragmentationPathway>(
      msp_polChemDef,
      "Type Spec Name",
      "Type Spec Formula",
      libXpertMassCore::Enums::FragEnd::NE);

  mref_fragmentationPathways.insert(mref_fragmentationPathways.begin() + index,
                                    fragmentation_pathway_sp);
  m_ui.fragSpecListWidget->insertItem(index,
                                      fragmentation_pathway_sp->getName());

  setModified();

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

  m_ui.fragSpecListWidget->setCurrentRow(index);

  // Erase fragRule data that might be left over by precedent current
  // fragSpec.
  updateFragRuleDetails(0);

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


void
FragmentationPathwayDefDlg::removeFragSpecPushButtonClicked()
{
  QList<QListWidgetItem *> selectedList =
    m_ui.fragSpecListWidget->selectedItems();

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

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

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

  mref_fragmentationPathways.erase(mref_fragmentationPathways.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 fragSpec that we removed.

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

  // If there are no more items in the fragSpec list, remove all the items
  // from the fragRuleList.

  if(!m_ui.fragSpecListWidget->count())
    {
      m_ui.fragRuleListWidget->clear();
      clearAllDetails();
    }
}


void
FragmentationPathwayDefDlg::moveUpFragSpecPushButtonClicked()
{
  // Move the current row to one index less.

  // If no fragSpec is selected, just return.

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

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

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

  auto it = mref_fragmentationPathways.begin() + index;
  // If the item is already at top of list, do nothing.
  if(it == mref_fragmentationPathways.begin())
    return;
  std::iter_swap(it, it - 1); // Swap with the previous element

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

  m_ui.fragSpecListWidget->insertItem(index - 1, item);
  m_ui.fragSpecListWidget->setCurrentRow(index - 1);
  fragSpecListWidgetItemSelectionChanged();

  setModified();
}


void
FragmentationPathwayDefDlg::moveDownFragSpecPushButtonClicked()
{
  // Move the current row to one index less.

  // If no fragSpec is selected, just return.

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

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

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

  auto it = mref_fragmentationPathways.begin() + index;
  // If the item is already at bottom of list, do nothing.
  if(it == mref_fragmentationPathways.end())
    return;
  std::iter_swap(it, it + 1); // Swap with the next element

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

  setModified();
}


void
FragmentationPathwayDefDlg::addFragRulePushButtonClicked()
{
  // We are asked to add a new fragRule. We'll add it right after the
  // current item. Note however, that one fragSpec has to be selected.

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

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

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

  // What's the actual fragSpec?
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

  // Allocate the new fragRule.
  libXpertMassCore::FragmentationRuleSPtr fragmentation_rule_sp =
    std::make_shared<libXpertMassCore::FragmentationRule>(msp_polChemDef,
                                                      "Type Rule Name");

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

  m_ui.fragRuleListWidget->insertItem(index, fragmentation_rule_sp->getName());

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

  std::vector<libXpertMassCore::FragmentationRuleSPtr> fragmentation_rules =
    fragmentation_pathway_sp->getRulesRef();
  fragmentation_rules.insert(fragmentation_rules.begin() + index,
                             fragmentation_rule_sp);

  m_ui.fragRuleListWidget->setCurrentRow(index);

  setModified();

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


void
FragmentationPathwayDefDlg::removeFragRulePushButtonClicked()
{
  QList<QListWidgetItem *> selectedList =
    m_ui.fragRuleListWidget->selectedItems();

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

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

  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

  // Get the index of the current fragRule.
  index = m_ui.fragRuleListWidget->currentRow();

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

  // Remove the fragRule from the fragSpec proper.
  std::vector<MsXpS::libXpertMassCore::FragmentationRuleSPtr> fragmentation_rules =
    fragmentation_pathway_sp->getRulesRef();
  fragmentation_rules.erase(fragmentation_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 fragSpec that we removed.

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

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

  if(!m_ui.fragRuleListWidget->count())
    {
      updateFragRuleDetails(0);
    }
  else
    {
    }

  setModified();
}


void
FragmentationPathwayDefDlg::moveUpFragRulePushButtonClicked()
{
  // Move the current row to one index less.

  // If no fragRule is selected, just return.

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

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

  // Get the fragSpec to which the fragRule belongs.
  int index = m_ui.fragSpecListWidget->currentRow();
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

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

  auto it = fragmentation_pathway_sp->getRulesRef().begin() + index;
  // If the item is already at top of list, do nothing.
  if(it == fragmentation_pathway_sp->getRulesRef().begin())
    return;
  std::iter_swap(it, it - 1);

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

  setModified();
}


void
FragmentationPathwayDefDlg::moveDownFragRulePushButtonClicked()
{
  // Move the current row to one index less.

  // If no fragRule is selected, just return.

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

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

  // Get the fragSpec to which the fragRule belongs.
  int index = m_ui.fragSpecListWidget->currentRow();
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

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

  auto it = fragmentation_pathway_sp->getRulesRef().begin() + index;
  // If the item is already at bottom of list, do nothing.
  if(it == fragmentation_pathway_sp->getRulesRef().end())
    return;
  std::iter_swap(it, it + 1);

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

  setModified();
}

void
FragmentationPathwayDefDlg::applyFragSpecPushButtonClicked()
{
  // We are asked to apply the data for the fragSpec.

  // If no fragSpec is selected, just return.

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

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

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

  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

  // We do not want more than one fragSpec by the same name.

  QString editName    = m_ui.fragSpecNameLineEdit->text();
  QString editFormula = m_ui.fragSpecFormulaLineEdit->text();

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

  int frag_index = msp_polChemDef->getFragmentationPathwayIndexByName(editName);

  if(frag_index != -1 && frag_index != index)
    {
      QMessageBox::warning(this,
                           "MassXpert - Fragmentation pathway definition",
                           "A fragmentation pathway with same name "
                           "exists already.",
                           QMessageBox::Ok);
      return;
    }

  QString fragEndString = m_ui.fragEndComboBox->currentText();
  libXpertMassCore::Enums::FragEnd fragEnd;

  if(fragEndString == "NE")
    fragEnd = libXpertMassCore::Enums::FragEnd::NE;
  else if(fragEndString == "BE")
    fragEnd = libXpertMassCore::Enums::FragEnd::BE;
  else if(fragEndString == "LE")
    fragEnd = libXpertMassCore::Enums::FragEnd::LE;
  else if(fragEndString == "RE")
    fragEnd = libXpertMassCore::Enums::FragEnd::RE;
  else
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Fragmentation pathway definition"),
                           tr("The fragmentation pathway end is not correct. "
                              "Choose either 'NE' or 'LE' or 'RE'."),
                           QMessageBox::Ok);
      return;
    }

  // At this point, validate the formula:

  libXpertMassCore::Formula formula(editFormula);

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

  libXpertMassCore::ErrorList error_list;

  if(!formula.validate(isotopic_data_sp, &error_list))
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - Fragmentation pathway definition"),
        QString("The formula failed to validate with errors:\n %1\n.")
          .arg(libXpertMassCore::Utils::joinErrorList(error_list)),
        QMessageBox::Ok);
      return;
    }

  // At this point, check if the fragmented monomer should
  // contribute something to the mass of the fragment (usually one
  // would expect that the contribution be negative, that is that
  // the monomer gets decomposed somehow). This integer value
  // indicates if this monomer's mass should be added (positive
  // integer) or removed (negative integer) and how may times it
  // should thus be. The user is required to state what part of the
  // monomer structure should be readded.

  // The rationale is that we can easily remove the mass of the
  // whole monomer, which we know, of course. However, since full
  // removal of a monomer mass does not make sense, the user should
  // indicate which part of the monomer should be readded. This
  // mechanism is typically used when making definitions of
  // fragmentations which involve lateral chain decomposition (the
  // base in DNA, the side chain for proteins and other chemical
  // groups for sugars).

  // For example, let's imagine, we are dealing with the
  // decomposition of guanine in a DNA oligonucleotide. We want to
  // define the fragmentation pattern 'a' with full decomposition of
  // the base.

  // First off, the monomer of a DNA is constituted of two parts :

  // 1. The skeleton, which does not change when the nucleotide
  // changes (that is which is constant which ever the base is at
  // any given monomer position), this is the sugar plus the
  // phosphate (C5H8O5P);

  // 2. The base, which changes each time a monomer changes (that's
  // the lateral chain of the monomer). Adenine has formula C5H4N5,
  // for example.

  // Now, if we wanted to define the fragmentation a-B (that is a
  // with base decomposition), we would define -O (that is the
  // normal a fragmentation formula), +OH (as the left cap) and we
  // would have to set two more parameters in order to remove the
  // mass of the nitrogenous base (lateral chain).

  // 1. Ask that the mass of the whole monomer be removed (set
  // monomerContrib below to -1). But that would be removing too
  // much stuff. We should now readd the mass of the skeleton, so
  // that the net mass loss corresponds to decomposing only the base
  // (adenine, for example).

  // 2. Ask that the mass of the skeleton is readded. This is done
  // by appending +C5H8O5P to the -O formula of the normal a
  // fragmentation pattern.

  // Finally, if we had to perform something similar wity protein,
  // we would be having the same mechanism, unless the skeleton part
  // of a proteinaceous residual chain is CH.


  int monomerContrib = m_ui.monomerContributionSpinBox->value();

  // Now that all data were validated, we can set them all.

  fragmentation_pathway_sp->setName(editName);
  fragmentation_pathway_sp->setFragEnd(fragEnd);
  fragmentation_pathway_sp->setFormula(editFormula);
  fragmentation_pathway_sp->setComment(m_ui.fragSpecCommentLineEdit->text());
  fragmentation_pathway_sp->setMonomerContribution(monomerContrib);


  // Update the list widget item.

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

  setModified();
}


void
FragmentationPathwayDefDlg::applyFragRulePushButtonClicked()
{
  // We are asked to apply the data for the fragRule.

  // If no fragRule is selected, just return.

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

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

  // Get the fragSpec to which the fragRule belongs.
  int index = m_ui.fragSpecListWidget->currentRow();
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

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

  // Get the fragRule itself from the fragSpec.
  libXpertMassCore::FragmentationRuleSPtr frag_rule_sp =
    fragmentation_pathway_sp->getRulesCstRef().at(index);

  QString editName    = m_ui.fragRuleNameLineEdit->text();
  QString editFormula = m_ui.fragRuleFormulaLineEdit->text();

  QString prevCode = m_ui.prevCodeLineEdit->text();
  QString currCode = m_ui.currCodeLineEdit->text();
  QString nextCode = m_ui.nextCodeLineEdit->text();

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

  std::vector<libXpertMassCore::FragmentationRuleSPtr> frag_rules =
    fragmentation_pathway_sp->getRulesCstRef();
  std::vector<libXpertMassCore::FragmentationRuleSPtr>::iterator the_iterator =
    std::find_if(
      frag_rules.begin(),
      frag_rules.end(),
      [editName](const libXpertMassCore::FragmentationRuleSPtr frag_rule_sp) {
        return frag_rule_sp->getName() == editName;
      });

  if(the_iterator != frag_rules.end() &&
     std::distance(frag_rules.begin(), the_iterator) != index)
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Fragmentation pathway definition"),
                           tr("A fragmentation pathway rule with same name "
                              "exists already."),
                           QMessageBox::Ok);
      return;
    }

  if(!prevCode.isEmpty())
    {
      if(msp_polChemDef->getMonomerIndexByCode(prevCode) == -1)
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - Fragmentation pathway definition"),
            tr("The previous code is not known."),
            QMessageBox::Ok);
          return;
        }
    }

  if(!currCode.isEmpty())
    {
      if(msp_polChemDef->getMonomerIndexByCode(currCode) == -1)
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - Fragmentation pathway definition"),
            tr("The current code is not known."),
            QMessageBox::Ok);
          return;
        }
    }

  if(!nextCode.isEmpty())
    {
      if(msp_polChemDef->getMonomerIndexByCode(nextCode) == -1)
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - Fragmentation pathway definition"),
            tr("The next code is not known."),
            QMessageBox::Ok);
          return;
        }
    }

  libXpertMassCore::Formula formula(editFormula);

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

  libXpertMassCore::ErrorList error_list;
  if(!formula.validate(isotopic_data_sp, &error_list))
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - Fragmentation pathway definition"),
        QString("The formula failed to validate with errors:\n%1\n.")
          .arg(libXpertMassCore::Utils::joinErrorList(error_list)),
        QMessageBox::Ok);
      return;
    }


  frag_rule_sp->setName(editName);
  frag_rule_sp->setFormula(editFormula);

  frag_rule_sp->setPrevCode(prevCode);
  frag_rule_sp->setCurrCode(currCode);
  frag_rule_sp->setNextCode(nextCode);

  frag_rule_sp->setComment(m_ui.fragRuleCommentLineEdit->text());

  // Update the list widget item.

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

  setModified();
}


bool
FragmentationPathwayDefDlg::validatePushButtonClicked()
{
  // All we have to do is validate the fragSpec 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
  // fragSpec list and the listwidget.

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

  if(itemCount != mref_fragmentationPathways.size())
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Fragmentation pathway definition"),
                           tr("\nThe number of fragmentations in "
                              "the list widget \n"
                              "and in the list of fragmentations "
                              "is not identical.\n"),
                           QMessageBox::Ok);
      return false;
    }

  libXpertMassCore::ErrorList error_list;

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

      libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
        mref_fragmentationPathways.at(iter);

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

      fragmentation_pathway_sp->validate(&error_list);
    }

  if(error_list.size())
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - Fragmentation pathway definition"),
        // error_list.join("\n"),
        QString(
          "Failed to validate the fragmentation pathway with errors:\n %1\n")
          .arg(libXpertMassCore::Utils::joinErrorList(error_list)),
        QMessageBox::Ok);
      return false;
    }
  else
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Fragmentation pathway definition"),
                           ("Validation: success\n"),
                           QMessageBox::Ok);
    }

  return true;
}


void
FragmentationPathwayDefDlg::fragSpecListWidgetItemSelectionChanged()
{
  // The fragSpec item has changed. Empty the fragRule list and update its
  // contents. Update the details for the fragSpec.

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

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

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

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

  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

  // Set the data of the fragSpec to their respective widgets.
  updateFragSpecDetails(fragmentation_pathway_sp);

  // The list of fragRules
  m_ui.fragRuleListWidget->clear();

  for(libXpertMassCore::FragmentationRuleSPtr frag_rule_sp :
      fragmentation_pathway_sp->getRulesCstRef())
    m_ui.fragRuleListWidget->addItem(frag_rule_sp->getName());

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


void
FragmentationPathwayDefDlg::fragRuleListWidgetItemSelectionChanged()
{
  // The fragRule item has changed. Update the details for the fragRule.

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

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

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

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

  // Find the fragRule object in the list of fragRules.
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp =
    mref_fragmentationPathways.at(index);

  // Get the index of the current fragRule.
  index = m_ui.fragRuleListWidget->currentRow();

  // Get the fragRule that is currently selected from the fragSpec's list
  // of fragRules.
  libXpertMassCore::FragmentationRuleSPtr fragmentation_rule_sp =
    fragmentation_pathway_sp->getRulesCstRef().at(index);

  // Set the data of the fragRule to their respective widgets.
  updateFragRuleDetails(fragmentation_rule_sp);
}


void
FragmentationPathwayDefDlg::updateFragSpecDetails(
  libXpertMassCore::FragmentationPathwaySPtr fragmentation_pathway_sp)
{
  if(fragmentation_pathway_sp)
    {
      m_ui.fragSpecNameLineEdit->setText(fragmentation_pathway_sp->getName());
      m_ui.fragSpecFormulaLineEdit->setText(
        fragmentation_pathway_sp->getFormulaCstRef().getActionFormula());

      int index = 0;

      if(fragmentation_pathway_sp->getFragEnd() ==
         libXpertMassCore::Enums::FragEnd::NE)
        index = m_ui.fragEndComboBox->findText("NE");
      else if(fragmentation_pathway_sp->getFragEnd() ==
              libXpertMassCore::Enums::FragEnd::LE)
        index = m_ui.fragEndComboBox->findText("LE");
      else if(fragmentation_pathway_sp->getFragEnd() ==
              libXpertMassCore::Enums::FragEnd::RE)
        index = m_ui.fragEndComboBox->findText("RE");
      m_ui.fragEndComboBox->setCurrentIndex(index);
      m_ui.monomerContributionSpinBox->setValue(
        fragmentation_pathway_sp->getMonomerContribution());
      m_ui.fragSpecCommentLineEdit->setText(
        fragmentation_pathway_sp->getComment());
    }
  else
    {
      m_ui.fragSpecNameLineEdit->setText("");
      m_ui.fragSpecFormulaLineEdit->setText("");
      int index = m_ui.fragEndComboBox->findText("N/A");
      m_ui.fragEndComboBox->setCurrentIndex(index);
      m_ui.monomerContributionSpinBox->setValue(0);
      m_ui.fragSpecCommentLineEdit->setText("");
    }
}


void
FragmentationPathwayDefDlg::updateFragRuleDetails(
  libXpertMassCore::FragmentationRuleSPtr fragmentation_rule_sp)
{
  if(fragmentation_rule_sp)
    {
      m_ui.fragRuleNameLineEdit->setText(fragmentation_rule_sp->getName());
      m_ui.fragRuleFormulaLineEdit->setText(
        fragmentation_rule_sp->getFormulaCstRef().getActionFormula());

      m_ui.prevCodeLineEdit->setText(fragmentation_rule_sp->getPrevCode());
      m_ui.currCodeLineEdit->setText(fragmentation_rule_sp->getCurrCode());
      m_ui.nextCodeLineEdit->setText(fragmentation_rule_sp->getNextCode());

      m_ui.fragRuleCommentLineEdit->setText(
        fragmentation_rule_sp->getComment());
    }
  else
    {
      m_ui.fragRuleNameLineEdit->setText("");
      m_ui.fragRuleFormulaLineEdit->setText("");

      m_ui.prevCodeLineEdit->setText("");
      m_ui.currCodeLineEdit->setText("");
      m_ui.nextCodeLineEdit->setText("");

      m_ui.fragRuleCommentLineEdit->setText("");
    }
}


void
FragmentationPathwayDefDlg::clearAllDetails()
{
  m_ui.fragSpecNameLineEdit->setText("");
  m_ui.fragSpecFormulaLineEdit->setText("");
  m_ui.monomerContributionSpinBox->setValue(0);
  int index = m_ui.fragEndComboBox->findText("N/A");
  m_ui.fragEndComboBox->setCurrentIndex(index);
  m_ui.monomerContributionSpinBox->setValue(0);
  m_ui.fragSpecCommentLineEdit->setText("");

  m_ui.fragRuleNameLineEdit->setText("");
  m_ui.fragRuleFormulaLineEdit->setText("");

  m_ui.prevCodeLineEdit->setText("");
  m_ui.currCodeLineEdit->setText("");
  m_ui.nextCodeLineEdit->setText("");

  m_ui.fragRuleCommentLineEdit->setText("");
}


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

} // namespace MassXpert

} // namespace MsXpS
