/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 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>
#include <libXpertMass/PolChemDefEntity.hpp>


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


namespace MsXpS
{

namespace massxpert
{


  MonomerModificationDlg::MonomerModificationDlg(
    SequenceEditorWnd *editorWnd,
    /* no polymer **/
    /* no libXpertMass::PolChemDef **/
    const QString &settingsFilePath,
    const QString &applicationName,
    const QString &description)
    : AbstractSeqEdWndDependentDlg(editorWnd,
                                   0, /*polymer **/
                                   0, /*polChemDef **/
                                   settingsFilePath,
                                   "MonomerModificationDlg",
                                   applicationName,
                                   description)
  {

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


  MonomerModificationDlg::~MonomerModificationDlg()
  {
  }


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

    // 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();
    populateMonomerList();
    populateModifAndModifiedMonomerLists();

    updateSelectionData();
    if(m_coordinateList.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()
  {
    libXpertMass::PolChemDefCstSPtr polChemDefCstSPtr =
      mp_editorWnd->polChemDefRendering()->getPolChemDef();
    Q_ASSERT(polChemDefCstSPtr);

    for(int iter = 0; iter < polChemDefCstSPtr->modifList().size(); ++iter)
      {
        libXpertMass::Modif *modif = polChemDefCstSPtr->modifList().at(iter);
        Q_ASSERT(modif);

        m_ui.availableModifListWidget->addItem(modif->name());
      }

    return true;
  }


  bool
  MonomerModificationDlg::populateModifAndModifiedMonomerLists()
  {
    // We'll need a pointer to the polymer sequence.
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    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.

    for(int iter = 0; iter < polymer->size(); ++iter)
      {
        const libXpertMass::Monomer *monomer = polymer->at(iter);

        QList<libXpertMass::Modif *> *list = monomer->modifList();

        if(!list || list->isEmpty())
          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 text = QString("%1/%2/%3")
                         .arg(monomer->code())
                         .arg(iter + 1)
                         .arg((quintptr)monomer);

        m_ui.modifiedMonomerListWidget->addItem(text);

        for(int jter = 0; jter < list->size(); ++jter)
          {
            libXpertMass::Modif *modif = list->at(jter);

            QString text = QString("%1/%2/%3/%4/%5")
                             .arg(modif->name())
                             .arg(monomer->code())
                             .arg(iter + 1)
                             .arg((quintptr)modif)
                             .arg((quintptr)monomer);

            // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
            //<< "monomer ptr:" << monomer
            //<< "modif ptr:" << modif
            //<< text;

            m_ui.modifListWidget->addItem(text);
          }
      }

    return true;
  }


  bool
  MonomerModificationDlg::populateModifiedMonomerList()
  {
    // We'll need a pointer to the polymer sequence.
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    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.

    for(int iter = 0; iter < polymer->size(); ++iter)
      {
        const libXpertMass::Monomer *monomer = polymer->at(iter);

        QList<libXpertMass::Modif *> *list = monomer->modifList();

        if(!list || list->isEmpty())
          continue;

        QString text = QString("%1/%2/%3")
                         .arg(monomer->code())
                         .arg(iter + 1)
                         .arg((quintptr)monomer);

        // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
        //<< "monomer ptr:" << monomer
        //<< text;

        m_ui.modifiedMonomerListWidget->addItem(text);
      }

    return true;
  }


  bool
  MonomerModificationDlg::populateModifList(bool all)
  {
    // We'll need a pointer to the polymer sequence.
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    // 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
        // pointer to the list.

        for(int iter = 0; iter < polymer->size(); ++iter)
          {
            const libXpertMass::Monomer *monomer = polymer->at(iter);

            QList<libXpertMass::Modif *> *modifList = monomer->modifList();

            if(!modifList || modifList->isEmpty())
              continue;

            for(int jter = 0; jter < modifList->size(); ++jter)
              {
                libXpertMass::Modif *modif = modifList->at(jter);

                QString text = QString("%1/%2/%3/%4/%5")
                                 .arg(modif->name())
                                 .arg(monomer->code())
                                 .arg(iter + 1)
                                 .arg((quintptr)modif)
                                 .arg((quintptr)monomer);

                // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ <<
                // "()"
                //<< "monomer ptr:" << monomer
                //<< "modif ptr:" << modif
                //<< text;

                m_ui.modifListWidget->addItem(text);
              }
          }
      }
    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 ?
            QString text = item->text();

            // The string is in the form "code/pos/monomerPtr"

            QStringList stringList =
              text.split('/', Qt::SkipEmptyParts, Qt::CaseSensitive);

            // The monomer position is the second string in the list.
            bool ok = false;
            int pos = stringList.at(1).toInt(&ok);

            if(!pos && !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(pos - 1 < 0 || pos > polymer->size())
              {
                QMessageBox::warning(this,
                                     tr("massXpert - 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);

                populateModifAndModifiedMonomerLists();

                return false;
              }

            // The monomer(quintptr) pointer is the third string in the list.
            ok                        = false;
            quintptr pointerCastToInt = stringList.at(2).toLongLong(&ok, 10);
            libXpertMass::Monomer *monomer =
              reinterpret_cast<libXpertMass::Monomer *>(pointerCastToInt);

            // 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 != polymer->at(pos - 1))
              {
                QMessageBox::warning(this,
                                     tr("massXpert - 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);

                populateModifAndModifiedMonomerLists();

                return false;
              }

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

            QList<libXpertMass::Modif *> *modifList = monomer->modifList();

            // It is not possible that the monomer has no modif,
            // otherwise how come would it be listed in the list of
            // modified monomers.

            Q_ASSERT(modifList && !modifList->isEmpty());

            for(int jter = 0; jter < modifList->size(); ++jter)
              {
                libXpertMass::Modif *modif = modifList->at(jter);

                QString text = QString("%1/%2/%3/%4/%5")
                                 .arg(modif->name())
                                 .arg(monomer->code())
                                 .arg(pos)
                                 .arg((quintptr)modif)
                                 .arg((quintptr)monomer);

                m_ui.modifListWidget->addItem(text);
              }
          }
        // 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::populateMonomerList()
  {
    libXpertMass::PolChemDefCstSPtr polChemDefCstSPtr =
      mp_editorWnd->polChemDefRendering()->getPolChemDef();
    Q_ASSERT(polChemDefCstSPtr);

    for(int iter = 0; iter < polChemDefCstSPtr->monomerList().size(); ++iter)
      {
        libXpertMass::Monomer *monomer = polChemDefCstSPtr->monomerList().at(iter);
        Q_ASSERT(monomer);

        QString text = monomer->code() + '=' + monomer->name();

        m_ui.monomerListWidget->addItem(text);
      }

    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.

    populateModifList(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)
      populateModifList(true);
    else
      populateModifList(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_coordinateList);

    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_coordinateList.size() > 1)
          {
            // Apparently there are multiple regions selected.

            m_ui.currentSelectionLineEdit->setText(
              m_coordinateList.positionsAsText());
          }
        else
          {
            libXpertMass::Coordinates *coordinates = m_coordinateList.first();

            if(coordinates->start() == coordinates->end())
              {

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

                libXpertMass::Polymer *polymer = mp_editorWnd->polymer();
                const libXpertMass::Monomer *monomer =
                  polymer->monomerList().at(coordinates->start());

                QString text = tr("%1 at pos. %2")
                                 .arg(monomer->code())
                                 .arg(coordinates->start() + 1);

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

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


  int
  MonomerModificationDlg::prepareIndicesList()
  {
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    m_indicesList.clear();

    bool isSelectionPresent = updateSelectionData();

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

        // Now, for each libXpertMass::Coordinates in the CoordinateList, append the
        // indices...

        for(int iter = 0; iter < m_coordinateList.size(); ++iter)
          {
            libXpertMass::Coordinates *coordinates = m_coordinateList.at(iter);

            for(int iter = coordinates->start(); iter < coordinates->end() + 1;
                ++iter)
              m_indicesList.append(iter);
          }
      }
    else if(m_ui.sameCodeRadioButton->isChecked())
      {
        // Get the code of the currently selected monomer.
        if(!isSelectionPresent)
          return 0;

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

        libXpertMass::Coordinates *coordinates = m_coordinateList.at(0);

        if(coordinates->start() != coordinates->end())
          return 0;

        QString code = polymer->monomerList().at(coordinates->start())->code();

        for(int iter = 0; iter < polymer->monomerList().size(); ++iter)
          {
            const libXpertMass::Monomer *monomer = polymer->monomerList().at(iter);
            Q_ASSERT(monomer);

            if(monomer->code() == code)
              m_indicesList.append(iter);
          }
      }
    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);

            for(int jter = 0; jter < polymer->monomerList().size(); ++jter)
              {
                const libXpertMass::Monomer *monomer =
                  polymer->monomerList().at(jter);
                Q_ASSERT(monomer);

                if(monomer->code() == code)
                  m_indicesList.append(jter);
              }
          }
      }
    else if(m_ui.allRadioButton->isChecked())
      {
        for(int iter = 0; iter < polymer->monomerList().size(); ++iter)
          m_indicesList.append(iter);
      }
    else
      {
        QMessageBox::warning(this,
                             tr("massXpert - Modify monomers"),
                             tr("No target is selected."),
                             QMessageBox::Ok);
        return 0;
      }

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

    return m_indicesList.size();
  }


  bool
  MonomerModificationDlg::parseModifDefinition(libXpertMass::Modif *modif)
  {
    Q_ASSERT(modif);

    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())
      return false;

    text = m_ui.modifTargetsLineEdit->text();

    modif->setTargets(text);

    if(!modif->validate())
      return false;

    return true;
  }


  void
  MonomerModificationDlg::modify()
  {
    QStringList errorList;

    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

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

    libXpertMass::Modif *modif =
      new libXpertMass::Modif(polymer->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("massXpert - 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("massXpert - 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.

        bool res = polymer->getPolChemDefCstSPtr()->referenceModifByName(text, modif);
        if(!res)
          qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

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

    for(int iter = 0; iter < m_indicesList.size(); ++iter)
      {
        int index = m_indicesList.at(iter);

        const libXpertMass::Monomer *monomer = polymer->monomerList().at(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.

        errorList.append(
          tr("Pos. %1: %2 --------------").arg(iter + 1).arg(monomer->code()));

        // Upon modification, the monomer takes ownership of the modif,
        // so we have to allocate one each time using the template
        // prepared above.

        libXpertMass::Modif *newModif = new libXpertMass::Modif(*modif);

        int ret = const_cast<libXpertMass::Monomer *>(monomer)->modify(
          newModif, override, errorList);

        // The modification went ok, which means we can remove the last
        // string that we prudentially added in case an error was to be
        // output. If the modification failed(ret == false) we do not
        // perform any modification for the monomer dealt-with.

        // qDebug() << __FILE__ << __LINE__
        // << "Modifying cast monomer ptr: " << pointerCastToInt
        // << "with Monomer* ptr: " << monomer;

        if(!ret)
          {
            errorList.append("\n");
          }
        else
          {
            errorList.removeLast();

            mp_editorWnd->setWindowModified(true);

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

            ret = mp_editorWnd->mpa_editorGraphicsView->modifyVignetteAt(
              index, newModif);

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

    m_ui.messagesTextEdit->append(
      tr("New operation: modify with %1\n").arg(modif->name()) +
      errorList.join(""));

    // At this point we can delete the modif template.
    delete modif;

    populateModifAndModifiedMonomerLists();

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

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


  void
  MonomerModificationDlg::unmodify()
  {
    // We only can unmodify in the following cases:

    // 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)
      {
        QListWidgetItem *item = selectedList.at(iter);

        // Get the text which is of the form

        // "Phosphorylation/S/12/136958312/136678312",

        // which is the name of the modification, the code of the
        // modified monomer and its position, the pointer to
        // the modification and finally the pointer to
        // the modified monomer".

        QString text = item->text();

        QStringList stringList =
          text.split('/', Qt::SkipEmptyParts, Qt::CaseSensitive);

        bool ok = false;

        // The modif(quintptr) pointer is the third string in the list.
        quintptr pointerCastToInt = stringList.at(3).toLongLong(&ok, 10);
        libXpertMass::Modif *modif =
          reinterpret_cast<libXpertMass::Modif *>(pointerCastToInt);

        // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
        //<< "modif ptr str:" << stringList.at(3)
        //<< "modif ptr:" << modif;

        // The monomer(quintptr) pointer is the fourth string in the
        // list.
        ok               = false;
        pointerCastToInt = stringList.at(4).toLongLong(&ok, 10);
        libXpertMass::Monomer *monomer =
          reinterpret_cast<libXpertMass::Monomer *>(pointerCastToInt);

        // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
        //<< "monomer ptr str:" << stringList.at(4)
        //<< "monomer ptr:" << monomer;

        // qDebug() << __FILE__ << __LINE__
        // << "Demodifying cast monomer ptr: " << pointerCastToInt
        // << "with Monomer* ptr: " << monomer;

        // Because the sequence might have changed since the moment
        // this window was opened, we should make sure the monomer to
        // unmodify is still there !

        int index = mp_editorWnd->polymer()->monomerIndex(monomer);

        if(index == -1)
          {
            // Hmmm, the monomer is no more in the sequence, the
            // sequence has been modified and the monomer has been
            // erased.

            QMessageBox::warning(this,
                                 tr("massXpert - 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);

            populateModifAndModifiedMonomerLists();

            return;
          }


        // The monomer index at the time the monomer modification window
        // was opened. Note that the index might change if the user
        // edits the sequence after having opened *this window. Also,
        // note that for the reader to use the value in the list item,
        // it was set as a position, not an index, which means we have
        // to remove one from the value.
        //	ok = false;
        //	int index = stringList.at(2).toInt(&ok) - 1;


        // At this point we know which modification we have to remove
        // and from which monomer.

        int ret = monomer->unmodify(modif);
        if(!ret)
          qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

        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);

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

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

    populateModifAndModifiedMonomerLists();

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

} // namespace massxpert

} // namespace MsXpS
