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


/////////////////////// Local includes
#include "MonomerModificationDlg.hpp"


namespace MsXpS
{

namespace MassXpert
{


MonomerModificationDlg::MonomerModificationDlg(
  SequenceEditorWnd *editorWnd,
  /* no polymer **/
  /* no libXpertMassCore::PolChemDef **/
  const QString &settings_file_path,
  const QString &application_name,
  const QString &description)
  : AbstractSeqEdWndDependentDlg(editorWnd,
                                 0, /*polymer **/
                                 0, /*polChemDef **/
                                 settings_file_path,
                                 "MonomerModificationDlg",
                                 application_name,
                                 description)
{

  if(!initialize())
    qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);
}


MonomerModificationDlg::~MonomerModificationDlg()
{
}


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

  setWindowIcon(qApp->windowIcon());

  // Update the window title because the window title element in m_ui might be
  // either erroneous or empty.
  setWindowTitle(
    QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription));

  populateAvailableModifList();
  populateListOfMonomersInPolChemDef();
  populateListsOfModifsAndModifiedMonomers();

  updateSelectionData();
  if(m_indexRangeCollection.size() >= 1)
    m_ui.currentSelectionRadioButton->setChecked(true);

  connect(m_ui.updateCurrentSelectionDataPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(updateSelectionData()));

  connect(m_ui.modifiedMonomerListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(modifiedMonomerListWidgetItemSelectionChanged()));

  connect(m_ui.modifListWidget,
          SIGNAL(itemSelectionChanged()),
          this,
          SLOT(modifListWidgetItemSelectionChanged()));

  m_ui.displayAllModifsCheckBox->setChecked(true);
  connect(m_ui.displayAllModifsCheckBox,
          SIGNAL(stateChanged(int)),
          this,
          SLOT(displayAllModifsChanged(int)));

  connect(m_ui.modifyPushButton, SIGNAL(clicked()), this, SLOT(modify()));

  connect(m_ui.unmodifyPushButton, SIGNAL(clicked()), this, SLOT(unmodify()));

  connect(this, SIGNAL(rejected()), this, SLOT(close()));

  return true;
}


void
MonomerModificationDlg::readSettings()
{
  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
  settings.beginGroup(m_wndTypeName);
  restoreGeometry(settings.value("geometry").toByteArray());
  m_ui.hSplitter->restoreState(settings.value("hSplitter").toByteArray());
  m_ui.vSplitter->restoreState(settings.value("vSplitter").toByteArray());
  settings.endGroup();
}


void
MonomerModificationDlg::writeSettings()
{

  QSettings settings(m_configSettingsFilePath, QSettings::IniFormat);
  settings.beginGroup(m_wndTypeName);
  settings.setValue("geometry", saveGeometry());
  settings.setValue("hSplitter", m_ui.hSplitter->saveState());
  settings.setValue("vSplitter", m_ui.vSplitter->saveState());
  settings.endGroup();
}


bool
MonomerModificationDlg::populateAvailableModifList()
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    mp_editorWnd->getPolChemDefRenderingCstRPtr()->getPolChemDefCstSPtr();

  if(pol_chem_def_csp == nullptr)
    qFatal() << "Programming error.";

  for(libXpertMassCore::ModifCstSPtr modif_csp :
      pol_chem_def_csp->getModifsCstRef())
    m_ui.availableModifListWidget->addItem(modif_csp->getName());

  return true;
}


bool
MonomerModificationDlg::populateListsOfModifsAndModifiedMonomers()
{
  // We'll need a pointer to the polymer sequence.
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  m_ui.modifiedMonomerListWidget->clear();
  m_ui.modifListWidget->clear();

  // For all the monomers in the polymer sequence, check which
  // modifications they bear. If any, add their name and monomer
  // pointer to the list.

  // The Monomer class has two containers:
  // - m_modifs that contains ModifSPt
  // - m_uuidModifPairs that contain Uuid-ModifWPtr pairs. The ModifWPtr
  // is a non-reference-incrementing pointer to the SPtr.

  // The Uuid string in the pair serves to identify unambiguously the ModifSPtr
  // where such a pointer is required from places that cannot store that
  // pointer. This the case here: we want the list widget item to store the
  // ModifSPtr but that is done by storing the Uuid string.

  std::size_t monomer_position = 0;

  for(QString monomer_uuid : polymer_sp->getSequenceRef().getAllMonomerUuids())
    {
      ++monomer_position;

      libXpertMassCore::MonomerCstSPtr monomer_csp =
        polymer_sp->getSequenceRef().getMonomerForUuid(monomer_uuid);

      if(monomer_csp == nullptr)
        qFatal() << "Inconsistency between the <object>_s and "
                          "<uuid-object> pairs.";

      if(!monomer_csp->isModified())
        continue;

      // At this point we know the monomer is modified. Let's take the
      // opportunity to add this monomer to the list of modified
      // monomers.

      QString item_text =
        QString("%1/%2").arg(monomer_csp->getCode()).arg(monomer_position);

      QListWidgetItem *item = new QListWidgetItem(item_text);
      item->setData(Qt::UserRole, monomer_uuid);
      m_ui.modifiedMonomerListWidget->addItem(item);

      // And now handle the Modif objects.
      std::vector<QString> all_modif_uuids = monomer_csp->getAllModifUuids();

      for(const QString &modif_uuid : all_modif_uuids)
        {
          libXpertMassCore::ModifSPtr modif_sp =
            monomer_csp->getModifForUuid(modif_uuid);

          if(modif_sp == nullptr)
            qFatal() << "Inconsistency between the <object>_s and "
                              "<uuid-object> pairs.";

          QString item_text = QString("%1/%2/%3")
                                .arg(modif_sp->getName())
                                .arg(monomer_csp->getCode())
                                .arg(monomer_position);

          QString item_user_role_text =
            QString("%1/%2").arg(modif_uuid).arg(monomer_uuid);
          QListWidgetItem *item = new QListWidgetItem(item_text);
          item->setData(Qt::UserRole, item_user_role_text);
          m_ui.modifListWidget->addItem(item);
        }
    }

  return true;
}

bool
MonomerModificationDlg::populateListOfModifiedMonomers()
{
  // We'll need a pointer to the polymer sequence.
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  m_ui.modifiedMonomerListWidget->clear();

  // For all the monomers in the polymer sequence, check if they are
  // modified. If so, add their code position and monomer pointer to
  // the list.

  std::size_t monomer_position = 0;

  for(QString monomer_uuid : polymer_sp->getSequenceRef().getAllMonomerUuids())
    {
      ++monomer_position;

      libXpertMassCore::MonomerCstSPtr monomer_csp =
        polymer_sp->getSequenceRef().getMonomerForUuid(monomer_uuid);

      if(monomer_csp == nullptr)
        qFatal() << "Inconsistency between the <object>_s and "
                          "<uuid-object> pairs.";

      if(!monomer_csp->isModified())
        continue;

      // At this point we know the monomer is modified. Let's take the
      // opportunity to add this monomer to the list of modified
      // monomers.

      QString itemText =
        QString("%1/%2").arg(monomer_csp->getCode()).arg(monomer_position);

      QListWidgetItem *item = new QListWidgetItem(itemText);
      item->setData(Qt::UserRole, monomer_uuid);
      m_ui.modifiedMonomerListWidget->addItem(item);
    }

  return true;
}


bool
MonomerModificationDlg::populateListOfModifs(bool all)
{
  // We'll need a pointer to the polymer sequence.
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  // First-off remove all the items.
  m_ui.modifListWidget->clear();

  if(all)
    {
      // For all the monomers in the polymer sequence, check which
      // modifications they bear. If any, add their name and monomer
      // uuid to the list.

      std::size_t monomer_position = 0;

      for(QString monomer_uuid :
          polymer_sp->getSequenceRef().getAllMonomerUuids())
        {
          ++monomer_position;

          libXpertMassCore::MonomerCstSPtr monomer_csp =
            polymer_sp->getSequenceRef().getMonomerForUuid(monomer_uuid);

          if(monomer_csp == nullptr)
            qFatal() << "Inconsistency between the <object>_s and "
                              "<uuid-object> pairs.";

          if(!monomer_csp->isModified())
            continue;

          // And now handle the Modif objects.
          std::vector<QString> all_modif_uuids =
            monomer_csp->getAllModifUuids();

          for(const QString &modif_uuid : all_modif_uuids)
            {
              libXpertMassCore::ModifSPtr modif_sp =
                monomer_csp->getModifForUuid(modif_uuid);

              if(modif_sp == nullptr)
                qFatal() << "Inconsistency between the <object>_s and "
                                  "<uuid-object> pairs.";

              QString item_text = QString("%1/%2/%3")
                                    .arg(modif_sp->getName())
                                    .arg(monomer_csp->getCode())
                                    .arg(monomer_position);

              QString item_user_role_text =
                QString("%1/%2").arg(modif_uuid).arg(monomer_uuid);
              QListWidgetItem *item = new QListWidgetItem(item_text);
              item->setData(Qt::UserRole, item_user_role_text);
              m_ui.modifListWidget->addItem(item);
            }
        }
    }
  else
    {
      // We are interested only in the modifs for the currently
      // selected cross-linked monomers(if any) in the
      // modifiedMonomerListWidget.

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

      if(!selectedList.size())
        return true;

      for(int iter = 0; iter < selectedList.size(); ++iter)
        {
          QListWidgetItem *item = selectedList.at(iter);

          // What's the text of the item ? It contains in sequence
          // "Monomer code/Monomer position".
          QString item_text = item->text();

          qDebug() << "item_text:" << item_text;

          QStringList item_text_string_list =
            item_text.split('/', Qt::SkipEmptyParts, Qt::CaseSensitive);

          // The monomer position is the second string in the list.
          bool ok              = false;
          std::size_t position = item_text_string_list.at(1).toUInt(&ok);

          if(!position && !ok)
            return false;

          // What if the sequence changed and the monomer is no more
          // in a valid range? We want to avoid a crash. See below for
          // an even better sanity check.
          if(position < 1 || position > polymer_sp->size())
            {
              QMessageBox::warning(this,
                                   tr("MassXpert3 - Modify monomers"),
                                   tr("%1@%2\n"
                                      "The monomer index does not correspond "
                                      "to a valid polymer sequence range.\n"
                                      "Avoid modifying the sequence while "
                                      "working with modifications.")
                                     .arg(__FILE__)
                                     .arg(__LINE__),
                                   QMessageBox::Ok);

              populateListsOfModifsAndModifiedMonomers();

              return false;
            }

          // This one contains "Monomer uuid";
          QString monomer_uuid = item->data(Qt::UserRole).toString();
          qDebug() << "monomer_uuid:" << monomer_uuid;

          libXpertMassCore::MonomerCstSPtr monomer_csp =
            polymer_sp->getSequenceCstRef().getMonomerForUuid(monomer_uuid);

          // Sanity check, are we dealing with the same monomer now
          // compared to the one of which the item was displayed when
          // the window was opened ?
          if(monomer_csp !=
             polymer_sp->getSequenceCstRef().getMonomerCstSPtrAt(position - 1))
            {
              QMessageBox::warning(this,
                                   tr("MassXpert3 - Modify monomers"),
                                   tr("%1@%2\n"
                                      "The monomer selected does not "
                                      "correspond to a valid sequence "
                                      "monomer.\n"
                                      "Avoid modifying the sequence while "
                                      "working with modifications.")
                                     .arg(__FILE__)
                                     .arg(__LINE__),
                                   QMessageBox::Ok);

              populateListsOfModifsAndModifiedMonomers();

              return false;
            }

          // Iterate in the monomer's modifications and for each one
          // create a new item.

          // And now handle the Modif objects.
          std::vector<QString> all_modif_uuids =
            monomer_csp->getAllModifUuids();

          for(const QString &modif_uuid : all_modif_uuids)
            {
              libXpertMassCore::ModifSPtr modif_sp =
                monomer_csp->getModifForUuid(modif_uuid);

              if(modif_sp == nullptr)
                qFatal() << "Inconsistency between the <object>_s and "
                                  "<uuid-object> pairs.";

              QString item_text = QString("%1/%2/%3")
                                    .arg(modif_sp->getName())
                                    .arg(monomer_csp->getCode())
                                    .arg(position);

              QString item_user_role_text =
                QString("%1/%2").arg(modif_uuid).arg(monomer_uuid);
              QListWidgetItem *item = new QListWidgetItem(item_text);
              item->setData(Qt::UserRole, item_user_role_text);
              m_ui.modifListWidget->addItem(item);
            }
        }
      // End of
      // for (int iter = 0; iter < selectedList.size(); ++iter)
    }
  // End of else , that is we only display modifs for selected
  // monomers.

  return true;
}


bool
MonomerModificationDlg::populateListOfMonomersInPolChemDef()
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    mp_editorWnd->getPolChemDefRenderingRPtr()->getPolChemDefCstSPtr();

  for(const libXpertMassCore::MonomerCstSPtr monomer_csp :
      pol_chem_def_csp->getMonomersCstRef())
    m_ui.monomerListWidget->addItem(
      QString("%1=%2").arg(monomer_csp->getName()).arg(monomer_csp->getCode()));

  return true;
}


void
MonomerModificationDlg::modifiedMonomerListWidgetItemSelectionChanged()
{
  // When an item is selected in the list of modified monomers, then
  // that means that the user does not want *all* the modifs to be
  // listed.

  m_ui.displayAllModifsCheckBox->setChecked(false);

  // Update the modif list data by listing only the modifs of the
  // currently selected monomer.

  populateListOfModifs(false);
}


void
MonomerModificationDlg::modifListWidgetItemSelectionChanged()
{
  //   qDebug() << __FILE__ << __LINE__
  // 	    << "MonomerModificationDlg::"
  //     "modifListWidgetItemSelectionChanged()";
}


void
MonomerModificationDlg::displayAllModifsChanged(int checkState)
{
  // When checked, we should list all the modifs in the
  // modifListWidget, and not only the modifs for the currently
  // selected modified monomer.

  if(checkState == Qt::Checked)
    populateListOfModifs(true);
  else
    populateListOfModifs(false);
}


bool
MonomerModificationDlg::updateSelectionData()
{
  // There might be more than one region selections. So get all
  // these coordinates !

  bool res = mp_editorWnd->mpa_editorGraphicsView->selectionIndices(
    m_indexRangeCollection);

  if(res)
    {
      // If there are more than one region selection or if there is
      // one region selection but spanning more than one monomer,
      // then set the target to be "current selected sequence".

      if(m_indexRangeCollection.size() > 1)
        {
          // Apparently there are multiple regions selected.

          m_ui.currentSelectionLineEdit->setText(
            m_indexRangeCollection.positionsAsText());
        }
      else
        {
          libXpertMassCore::IndexRange *index_range_p =
            m_indexRangeCollection.getRangesCstRef().front();

          if(index_range_p->m_start == index_range_p->m_stop)
            {

              // Construct a string with both the monomer code and
              // its position in the sequence.

              libXpertMassCore::PolymerQSPtr polymer_sp =
                mp_editorWnd->getPolymerSPtr();

              const libXpertMassCore::MonomerCstSPtr monomer_csp =
                polymer_sp->getSequenceCstRef().getMonomerCstSPtrAt(
                  index_range_p->m_start);

              QString text = tr("%1 at pos. %2")
                               .arg(monomer_csp->getCode())
                               .arg(index_range_p->m_start + 1);

              m_ui.currentSelectionLineEdit->setText(text);
            }
          else
            m_ui.currentSelectionLineEdit->setText(
              m_indexRangeCollection.positionsAsText());
        }
    }

  // Return if there was a selection(multiple-region or not) or
  // not.
  return res;
}


int
MonomerModificationDlg::prepareIndicesList()
{
  // We'll need a pointer to the polymer sequence.
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  const libXpertMassCore::Sequence &sequence_cst_ref =
    polymer_sp->getSequenceCstRef();

  m_indices.clear();

  bool isSelectionPresent = updateSelectionData();

  if(m_ui.currentSelectionRadioButton->isChecked())
    {
      // If there is no selection.
      if(!isSelectionPresent)
        return 0;

      // Now, for each libXpertMassCore::IndexRange in the index range collection,
      // append the indices...

      for(qsizetype iter = 0; iter < m_indexRangeCollection.size(); ++iter)
        {
          libXpertMassCore::IndexRange index_range(
            m_indexRangeCollection.getRangeRefAt(iter));

          for(qsizetype iter = index_range.m_start;
              iter < index_range.m_stop + 1;
              ++iter)
            m_indices.push_back(iter);
        }
    }
  else if(m_ui.sameCodeRadioButton->isChecked())
    {
      // Get the code of the currently selected monomer.
      if(!isSelectionPresent)
        return 0;

      if(m_indexRangeCollection.size() > 1)
        return 0;

      libXpertMassCore::IndexRange index_range(
        m_indexRangeCollection.getRangeRefAt(0));

      if(index_range.m_start != index_range.m_stop)
        return 0;

      QString code =
        sequence_cst_ref.getMonomerCstSPtrAt(index_range.m_start)->getCode();

      std::size_t monomer_index = 0;

      for(const libXpertMassCore::MonomerCstSPtr monomer_csp :
          sequence_cst_ref.getMonomersCstRef())
        {
          if(monomer_csp->getCode() == code)
            m_indices.push_back(monomer_index);

          ++monomer_index;
        }
    }
  else if(m_ui.fromListRadioButton->isChecked())
    {
      QList<QListWidgetItem *> selectedList =
        m_ui.monomerListWidget->selectedItems();

      if(!selectedList.size())
        return 0;

      for(int iter = 0; iter < selectedList.size(); ++iter)
        {
          QListWidgetItem *item = selectedList.at(iter);

          QString text = item->text();

          int index = text.indexOf('=');
          Q_ASSERT(index > 0);

          QString code = text.left(index);

          std::size_t monomer_index = 0;

          for(const libXpertMassCore::MonomerCstSPtr monomer_csp :
              sequence_cst_ref.getMonomersCstRef())
            {
              if(monomer_csp->getCode() == code)
                m_indices.push_back(monomer_index);

              ++monomer_index;
            }
        }
    }
  else if(m_ui.allRadioButton->isChecked())
    {
      std::size_t monomer_index = 0;

      for(const libXpertMassCore::MonomerCstSPtr monomer_csp :
          sequence_cst_ref.getMonomersCstRef())
        {
          m_indices.push_back(monomer_index);

          ++monomer_index;
        }
    }
  else
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - Modify monomers"),
                           tr("No target is selected."),
                           QMessageBox::Ok);
      return 0;
    }

  //   qDebug() << "Indices:" << m_indices.size();

  return m_indices.size();
}


bool
MonomerModificationDlg::parseModifDefinition(libXpertMassCore::Modif &modif)
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  // We do not allow maxCount to be entered in the GUI, set it to 1.
  modif.setMaxCount(1);

  QString text = m_ui.modifNameLineEdit->text();

  modif.setName(text);

  text = m_ui.modifFormulaLineEdit->text();

  modif.setFormula(text);

  // Attention, we have to compute the masses of the modif !

  if(!modif.calculateMasses(
       polymer_sp->getPolChemDefCstSPtr()->getIsotopicDataCstSPtr()))
    {
      qCritical() << "Failed to compute the masses of defined Modif"
                  << modif.toString();
      return false;
    }

  text = m_ui.modifTargetsLineEdit->text();

  modif.setTargets(text);

  libXpertMassCore::ErrorList error_list;
  if(!modif.validate(&error_list))
    {
      qCritical() << "Failed to validate the defined Modif" << modif.toString();
      return false;
    }

  return true;
}


void
MonomerModificationDlg::modify()
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  // There are two ways to perform a modification: either select one
  // modification from the list of available modifications as defined
  // in the polymer chemistry definition, or perform a quick and dirty
  // modification definition in the dialog.

  QString text;

  libXpertMassCore::Modif modif(
    polymer_sp->getPolChemDefCstSPtr(), "NOT_SET", "NOT_SET");

  if(m_ui.defineModifGroupBox->isChecked())
    {
      // The user wants to use a self-defined modification.

      if(!parseModifDefinition(modif))
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Modify monomers"),
                               tr("The defined modification failed "
                                  "to parse."),
                               QMessageBox::Ok);
          return;
        }

      // At this point the modification is correct.
    }
  else
    {
      // Get the modification currently selected.
      QList<QListWidgetItem *> selectedList =
        m_ui.availableModifListWidget->selectedItems();

      if(selectedList.size() != 1)
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Modify monomers"),
                               tr("No modification is selected "
                                  "in the list."),
                               QMessageBox::Ok);
          return;
        }

      text = selectedList.at(0)->text();
      Q_ASSERT(!text.isEmpty());

      // With the name of the modification get to the modif proper.

      libXpertMassCore::ModifCstSPtr modif_csp =
        polymer_sp->getPolChemDefCstSPtr()->getModifCstSPtrByName(text);

      if(modif_csp == nullptr)
        qFatal() << "Programming error.";

      modif = *modif_csp;

      // At this point the modification is correct. Set a bool to true
      // to know later that the modif was from the polChemDef.
    }

  // Is the modification work with allowed overriding of the target
  // and the max count limitations?
  bool override = m_ui.overrideLimitationsCheckBox->isChecked();

  // Construct a list of all the indices where the modification should
  // apply.
  if(!prepareIndicesList())
    return;

  libXpertMassCore::ErrorList error_list;
  std::size_t index_position = 0;

  for(const std::size_t &index : m_indices)
    {
      ++index_position;

      const libXpertMassCore::MonomerSPtr monomer_sp =
        polymer_sp->getSequenceRef().getMonomerSPtrAt(index);

      // Append the position and code of the currently iterated
      // monomer. We will remove that string if the result is not
      // bad. If the result below is bad, then the corresponding error
      // will have been appended and we will not remove anything.

      error_list.push_back(tr("Pos. %1: %2 --------------")
                             .arg(index_position)
                             .arg(monomer_sp->getCode()));

      QString uuid = monomer_sp->modify(modif, override, &error_list);

      if(uuid.isEmpty())
        {
          // The modification failed.
          error_list.push_back("\n");
        }
      else
        {
          // The modification succeeded, remove the provisionary string.
          error_list.erase(std::prev(error_list.end()));

          mp_editorWnd->setWindowModified(true);

          // We have to make sure that the vignette knows for which
          // chemical entity it is created.

          libXpertMassCore::ModifSPtr modif_sp = monomer_sp->getModifForUuid(uuid);

          int ret = mp_editorWnd->mpa_editorGraphicsView->modifyVignetteAt(
            index, *modif_sp, uuid);

          Q_ASSERT(ret);
        }
    }
  // End of
  // for (int iter = 0; iter < m_indices.size(); ++iter)

  m_ui.messagesTextEdit->append(
    tr("New operation: modify with %1\n").arg(modif.getName()) +
    libXpertMassCore::Utils::joinErrorList(error_list, " "));

  populateListsOfModifsAndModifiedMonomers();

  mp_editorWnd->updateWholeSequenceMasses(true);
  mp_editorWnd->updateSelectedSequenceMasses(true);

  //  mp_editorWnd->mpa_editorGraphicsView->updateSequence();
}


void
MonomerModificationDlg::unmodify()
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();
  libXpertMassCore::Sequence &sequence_ref  = polymer_sp->getSequenceRef();

  // We only can unmodify if one or more modifications are selected in the
  // m_ui.modifListWidget (which is a multiple selection list).

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

  if(!selectedList.size())
    return;

  // For each item in the selection list, get the item, get to the
  // monomer and perform the unmodification.

  for(int iter = 0; iter < selectedList.size(); ++iter)
    {
      // We must get two different texts:
      // 1. the text that is displayed in the item
      // 2. the text that was stored as user data in the item.

      QListWidgetItem *item = selectedList.at(iter);

      // 1. "Phosphorylation/S/12"
      QString item_text = item->text();
      qDebug() << "Iterating in list widget item with text:" << item_text;

      // 2. The text that is hidden as user role text that
      // holds the Uuid string for the ModifSPtr and for the MonomerSPtr.
      QString item_user_role_text = item->data(Qt::UserRole).toString();

      QStringList item_text_string_list =
        item_text.split('/', Qt::SkipEmptyParts, Qt::CaseSensitive);
      QStringList item_user_role_text_string_list =
        item_user_role_text.split('/', Qt::SkipEmptyParts, Qt::CaseSensitive);

      QString modif_uuid   = item_user_role_text_string_list[0];
      QString monomer_uuid = item_user_role_text_string_list[1];

      libXpertMassCore::MonomerSPtr monomer_sp =
        sequence_ref.getMonomerForUuid(monomer_uuid);

      if(monomer_sp == nullptr)
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Modify monomers"),
                               tr("%1@%2\n"
                                  "The monomer to unmodify does not exist "
                                  "in the sequence anymore.\n"
                                  "Avoid modifying the sequence while "
                                  "working with modifications.")
                                 .arg(__FILE__)
                                 .arg(__LINE__),
                               QMessageBox::Ok);

          populateListsOfModifsAndModifiedMonomers();

          return;
        }

      qDebug() << "The Monomer being unmodified:" << monomer_sp->getCode()
               << "with modification status:" << monomer_sp->isModified();

      // Check that the Monomer is still in the Sequence.
      bool ok = false;

      std::size_t index = sequence_ref.monomerIndex(monomer_sp, ok);

      if(!ok)
        {
          // Hmmm, the monomer is no more in the sequence, the
          // sequence has been modified and the monomer has been
          // erased.

          QMessageBox::warning(this,
                               tr("MassXpert3 - Modify monomers"),
                               tr("%1@%2\n"
                                  "The monomer to unmodify does not exist "
                                  "in the sequence anymore.\n"
                                  "Avoid modifying the sequence while "
                                  "working with modifications.")
                                 .arg(__FILE__)
                                 .arg(__LINE__),
                               QMessageBox::Ok);

          populateListsOfModifsAndModifiedMonomers();

          return;
        }

      qDebug() << "The monomer being unmodified is located at index:" << index;

      libXpertMassCore::ModifSPtr modif_sp =
        monomer_sp->getModifForUuid(modif_uuid);

      qDebug() << "The Modif in the Monomer, as identified by the Uuid:"
               << modif_sp->getName() << ", now unmodifying the Monomer.";

      // We can finally unmodify the Monomer

      if(!monomer_sp->unmodify(modif_uuid))
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Modify monomers"),
                               tr("%1@%2\n"
                                  "Failed to unmodify the monomer.")
                                 .arg(__FILE__)
                                 .arg(__LINE__),
                               QMessageBox::Ok);

          populateListsOfModifsAndModifiedMonomers();

          return;
        }

      qDebug() << "The Monomer was effectively unmodified for the selected "
                  "Modif. Now unmodifying the corresponding Monomer vignette.";

      mp_editorWnd->setWindowModified(true);

      // At this point, we have to make sure we remove the
      // modification vignette.

      // Note that because a monomer might be modified more than once,
      // and with the same modification, we ought to remove the proper
      // vignette.

      bool val = mp_editorWnd->mpa_editorGraphicsView->unmodifyVignetteAt(
        index, modif_uuid);

      if(!val)
        qFatal() << "Programming error.";

      mp_editorWnd->mpa_editorGraphicsView->updateSequence();
    }

  populateListsOfModifsAndModifiedMonomers();

  mp_editorWnd->updateWholeSequenceMasses(true);
  mp_editorWnd->updateSelectedSequenceMasses(true);
}

} // namespace MassXpert

} // namespace MsXpS
