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


/////////////////////// libmass includes
#include <libXpertMass/PolChemDefEntity.hpp>
#include <libXpertMass/CrossLink.hpp>


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


namespace MsXpS
{

namespace massxpert
{


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

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


  MonomerCrossLinkDlg::~MonomerCrossLinkDlg()
  {
  }


  bool
  MonomerCrossLinkDlg::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));

    populateCrossLinkerList();
    populateCrossLinkedMonomerList();
    populateCrossLinkList();

    readSettings();

    connect(m_ui.crossLinkerListWidget,
            SIGNAL(itemSelectionChanged()),
            this,
            SLOT(crossLinkerListWidgetItemSelectionChanged()));

    connect(m_ui.crossLinkedMonomerListWidget,
            SIGNAL(itemSelectionChanged()),
            this,
            SLOT(crossLinkedMonomerListWidgetItemSelectionChanged()));

    connect(m_ui.crossLinkedMonomerListWidget,
            SIGNAL(itemActivated(QListWidgetItem *)),
            this,
            SLOT(crossLinkedMonomerListWidgetItemActivated(QListWidgetItem *)));

    connect(m_ui.crossLinkListWidget,
            SIGNAL(itemSelectionChanged()),
            this,
            SLOT(crossLinkListWidgetItemSelectionChanged()));

    connect(m_ui.crossLinkListWidget,
            SIGNAL(itemActivated(QListWidgetItem *)),
            this,
            SLOT(crossLinkListWidgetItemActivated(QListWidgetItem *)));

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

    connect(
      m_ui.crossLinkPushButton, SIGNAL(clicked()), this, SLOT(crossLink()));

    connect(
      m_ui.uncrossLinkPushButton, SIGNAL(clicked()), this, SLOT(uncrossLink()));

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

    return true;
  }


  void
  MonomerCrossLinkDlg::readSettings()
  {

    QSettings settings(mp_editorWnd->configSettingsFilePath(),
                       QSettings::IniFormat);


    settings.beginGroup("MonomerCrossLinkDlg");
    restoreGeometry(settings.value("geometry").toByteArray());
    m_ui.leftSplitter->restoreState(
      settings.value("leftSplitterSize").toByteArray());
    m_ui.rightSplitter->restoreState(
      settings.value("leftSplitterSize").toByteArray());
    settings.endGroup();
  }


  void
  MonomerCrossLinkDlg::writeSettings()
  {

    QSettings settings(mp_editorWnd->configSettingsFilePath(),
                       QSettings::IniFormat);

    settings.beginGroup("MonomerCrossLinkDlg");

    settings.setValue("geometry", saveGeometry());

    settings.setValue("leftSplitterSize", m_ui.leftSplitter->saveState());
    settings.setValue("rightSplitterSize", m_ui.rightSplitter->saveState());

    settings.endGroup();
  }


  bool
  MonomerCrossLinkDlg::populateCrossLinkerList()
  {
    libXpertMass::PolChemDefCstSPtr polChemDefCstSPtr =
      mp_editorWnd->polChemDefRendering()->getPolChemDef();
    Q_ASSERT(polChemDefCstSPtr);

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

    for(int iter = 0; iter < polChemDefCstSPtr->crossLinkerList().size();
        ++iter)
      {
        libXpertMass::CrossLinker *crossLinker =
          polChemDefCstSPtr->crossLinkerList().at(iter);
        Q_ASSERT(crossLinker);

        m_ui.crossLinkerListWidget->addItem(crossLinker->name());
      }

    return true;
  }


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

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

    for(int iter = 0; iter < polymer->crossLinkList().size(); ++iter)
      {
        libXpertMass::CrossLink *crossLink = polymer->crossLinkList().at(iter);
        Q_ASSERT(crossLink);

        // For the currently iterated crossLink, iterate in the
        // monomerList and for each monomer get to know the code and
        // index.

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

            int index = polymer->monomerIndex(monomer);

            // Convert from index to position(+ 1 below).
            QString itemText = QString("%1/%2/%3")
                                 .arg(monomer->code())
                                 .arg(index + 1)
                                 .arg((quintptr)monomer);

            m_ui.crossLinkedMonomerListWidget->addItem(itemText);
          }
      }

    return true;
  }


  bool
  MonomerCrossLinkDlg::populateCrossLinkList(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.crossLinkListWidget->clear();

    if(all)
      {
        // We should list all the cross-links in the polymer, and not
        // only the ones that are involved by the currently selected
        // cross-linked monomer.

        for(int iter = 0; iter < polymer->crossLinkList().size(); ++iter)
          {
            libXpertMass::CrossLink *crossLink = polymer->crossLinkList().at(iter);
            Q_ASSERT(crossLink);

            QString text = QString("%1/%2/%3")
                             .arg(crossLink->name())
                             .arg(crossLink->monomerPosText())
                             .arg((quintptr)crossLink);

            m_ui.crossLinkListWidget->addItem(text);
          }

        return true;
      }
    else
      {
        // We are interested only in the crossLinks for the currently
        // selected cross-linked monomer(if any) in the
        // crossLinkedMonomerListWidget.

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

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

        // Get the index of the current item.
        int index = m_ui.crossLinkedMonomerListWidget->currentRow();
        // What's the item ?
        QListWidgetItem *item = m_ui.crossLinkedMonomerListWidget->item(index);
        // What's the text of the item ?
        QString text = item->text();

        // And now deconstruct the text that is in the form "C/3" if for
        // example, the cross-linked monomer has code 'C' and is a
        // position 3(that is index 2).

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

        //      qDebug() << __FILE__ << __LINE__
        // 		<< "stringList:" << stringList.at(0) << stringList.at(1);

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

        index = --pos;

        // What if the sequence changed and the monomer is no more in a
        // valid range? We want to avoid a crash.
        if(index < 0 || index >= polymer->size() + 1)
          {
            QMessageBox::warning(this,
                                 tr("massXpert - Cross-link 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 cross-links.")
                                   .arg(__FILE__)
                                   .arg(__LINE__),
                                 QMessageBox::Ok);

            return false;
          }

        //       qDebug() << __FILE__ << __LINE__
        // 		<< "Monomer index is:" << index;

        const libXpertMass::Monomer *monomer = polymer->at(index);

        // At this point we have to find all the crosslinks that involve
        // the monomer.

        for(int iter = 0; iter < polymer->crossLinkList().size(); ++iter)
          {
            libXpertMass::CrossLink *crossLink = polymer->crossLinkList().at(iter);
            Q_ASSERT(crossLink);

            if(crossLink->involvesMonomer(monomer) != -1)
              {
                // This crossLink involves our monomer, make an item out
                // of it.

                QString text = QString("%1/%2/%3")
                                 .arg(crossLink->name())
                                 .arg(crossLink->monomerPosText())
                                 .arg((quintptr)crossLink);

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

    return true;
  }


  void
  MonomerCrossLinkDlg::crossLinkerListWidgetItemSelectionChanged()
  {
    // We have to update the modif list widget that details the
    // modifications that are defined in the currently selected
    // crosslinker item.

    // The crosslinker list is a single-selection list, thus the list
    // below might contain either 0 or 1 at most selected item.

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

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

    // That's the name of the selected crosslinker.
    QString name = selectedList.at(0)->text();

    // Find the crosslinker object in the polymer chemistry definition.

    libXpertMass::PolChemDefCstSPtr polChemDefCstSPtr =
      mp_editorWnd->polChemDefRendering()->getPolChemDef();
    Q_ASSERT(polChemDefCstSPtr);

    libXpertMass::CrossLinker crossLinker(polChemDefCstSPtr, "NOT_SET", "NOT_SET");

    if(!polChemDefCstSPtr->referenceCrossLinkerByName(name, &crossLinker))
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "CrossLinker '%3' is not known.")
                               .arg(__FILE__)
                               .arg(__LINE__)
                               .arg(name),
                             QMessageBox::Ok);

        return;
      }

    // Make sure we remove all the items in the crossLinkModifListWidget !

    m_ui.crossLinkModifListWidget->clear();

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

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

    m_ui.crossLinkerNameLineEdit->setText(crossLinker.name());

    // Finally, we have to remove bits of data from the details of the
    // crossLink.

    // The crossLink comment.
    m_ui.crossLinkCommentLineEdit->setText("");
    // The monomers involved in the crossLink.
    m_ui.targetPositionListWidget->clear();
    // The target position.
    m_ui.targetPositionLineEdit->setText("");
  }


  void
  MonomerCrossLinkDlg::crossLinkedMonomerListWidgetItemSelectionChanged()
  {
    // When an item is selected in the list of crossLinked monomers,
    // then that means that the user does not want *all* the crossLinks
    // to be listed.

    m_ui.displayAllCrossLinksCheckBox->setChecked(false);

    // Update the crossLink list data by listing only the crossLinks of
    // the currently selected monomer.

    populateCrossLinkList(false);

    // Remove all the data about the crossLink.

    // We want that the list of crossLinkers has no active selection.
    QListWidgetItem *item = m_ui.crossLinkerListWidget->currentItem();
    item->setSelected(false);

    // The crossLinker name.
    m_ui.crossLinkerNameLineEdit->setText("");
    // The modifications in the CrossLinker.
    m_ui.crossLinkModifListWidget->clear();
    // The crossLink comment.
    m_ui.crossLinkCommentLineEdit->setText("");
    // The monomers involved in the crossLink.
    m_ui.targetPositionListWidget->clear();
    // The target position.
    m_ui.targetPositionLineEdit->setText("");
  }


  void
  MonomerCrossLinkDlg::crossLinkedMonomerListWidgetItemActivated(
    QListWidgetItem *item)
  {
    // We'll need a pointer to the polymer sequence.
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    QString text = item->text();

    // And now deconstruct the text that is in the form "C/2/0x456F5"
    // if for example, the cross-linked monomer is at position 2 and
    // the monomer in memory has a pointer of which the integer
    // representation is 136958312.

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

    // The crossLink(quintptr) pointer is the second string in the list.
    bool ok                = false;
    quintptr pointerToCast = stringList.at(2).toLongLong(&ok, 10);

    if(!ok)
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The polymer sequence has changed.\n"
                                "Avoid modifying the sequence while "
                                "working with cross-links.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);

        return;
      }

    libXpertMass::Monomer *monomer =
      reinterpret_cast<libXpertMass::Monomer *>(pointerToCast);

    int index = polymer->monomerIndex(monomer);

    if((index + 1) != stringList.at(1).toInt(&ok))
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The polymer sequence has changed.\n"
                                "Avoid modifying the sequence while "
                                "working with cross-links.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);
      }


    mp_editorWnd->mpa_editorGraphicsView->resetSelection();

    mp_editorWnd->mpa_editorGraphicsView->setSelection(
      index, index, false, false);
  }

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

    // When a crossLink is selected, then its data have to be displayed
    // in the details widgets.


    // Let's start by clearing all the data about the crossLink, so that
    // the data that we'll display below are not crippled with
    // previous-operation-dirtyness.

    // The crossLinker name.
    m_ui.crossLinkerNameLineEdit->setText("");
    // The modifications in the CrossLinker.
    m_ui.crossLinkModifListWidget->clear();
    // The crossLink comment.
    m_ui.crossLinkCommentLineEdit->setText("");
    // The monomers involved in the crossLink.
    m_ui.targetPositionListWidget->clear();
    // The target position.
    m_ui.targetPositionLineEdit->setText("");

    // We want that the list of crossLinkers has no active selection.
    QListWidgetItem *item = m_ui.crossLinkerListWidget->currentItem();
    item->setSelected(false);

    // The crossLinkListWidget is a single-selection list.

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

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

    // Get the index of the current item.
    int index = m_ui.crossLinkListWidget->currentRow();
    // What's the item ?
    item = m_ui.crossLinkListWidget->item(index);
    // What's the text of the item(like "DisulfideBond/;2;6;/136958312")?
    QString text = item->text();

    // And now deconstruct the text that is in the form
    // "DisulfideBond/;2;6;/0x456F5" if for example, the cross-linked
    // monomers are at positions 2 and 6 and are crossLinked with a
    // DisulfideBond. Also, the crossLink in memory has a pointer of
    // which the integer representation is 136958312.

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

    // The crossLink(quintptr) pointer is the third string in the list.
    bool ok                = false;
    quintptr pointerToCast = stringList.at(2).toLongLong(&ok, 10);

    if(!ok)
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The polymer sequence has changed.\n"
                                "Avoid modifying the sequence while "
                                "working with cross-links.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);

        return;
      }

    libXpertMass::CrossLink *crossLink =
      reinterpret_cast<libXpertMass::CrossLink *>(pointerToCast);

    // At this point, we can simply show all the details of the
    // crossLink.

    // The name of the crossLink(or crossLinker, are the same).
    m_ui.crossLinkerNameLineEdit->setText(crossLink->name());

    // The modifications of the crossLinker(parent class of
    // libXpertMass::CrossLink).  Make sure we remove all the items in the
    // crossLinkModifListWidget !
    m_ui.crossLinkModifListWidget->clear();

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

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


    // The crossLink comment
    m_ui.crossLinkCommentLineEdit->setText(crossLink->comment());

    // The monomers involved in the crossLink.
    m_ui.targetPositionListWidget->clear();

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

        int index = polymer->monomerIndex(monomer);

        // Convert from index to position(+ 1 below).
        QString itemText = QString("%1/%2").arg(monomer->code()).arg(index + 1);

        m_ui.targetPositionListWidget->addItem(itemText);
      }
  }


  void
  MonomerCrossLinkDlg::crossLinkListWidgetItemActivated(QListWidgetItem *item)
  {
    // We'll need a pointer to the polymer sequence.
    libXpertMass::Polymer *polymer = mp_editorWnd->polymer();

    QString text = item->text();

    // And now deconstruct the text that is in the form
    // "DisulfideBond/;2;6;/0x456F5" if for example, the cross-linked
    // monomers are at positions 2 and 6 and are crossLinked with a
    // DisulfideBond. Also, the crossLink in memory has a pointer of
    // which the integer representation is 136958312.

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

    // The crossLink(quintptr) pointer is the third string in the list.
    bool ok                = false;
    quintptr pointerToCast = stringList.at(2).toLongLong(&ok, 10);

    if(!ok)
      QMessageBox::warning(this,
                           tr("massXpert - Cross-link monomers"),
                           tr("%1@%2\n"
                              "The polymer sequence has changed.\n"
                              "Avoid modifying the sequence while "
                              "working with cross-links.")
                             .arg(__FILE__)
                             .arg(__LINE__),
                           QMessageBox::Ok);

    libXpertMass::CrossLink *crossLink =
      reinterpret_cast<libXpertMass::CrossLink *>(pointerToCast);

    libXpertMass::CoordinateList coordinateList;

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

        int index = polymer->monomerIndex(monomer);

        // Convert from index to position(+ 1 below).
        QString itemText = QString("%1/%2").arg(monomer->code()).arg(index + 1);


        libXpertMass::Coordinates *coordinates =
          new libXpertMass::Coordinates(index, index);
        coordinateList.append(coordinates);
      }

    mp_editorWnd->mpa_editorGraphicsView->resetSelection();

    mp_editorWnd->mpa_editorGraphicsView->setSelection(
      coordinateList, true, true);
  }


  void
  MonomerCrossLinkDlg::displayAllCrossLinksChanged(int checkState)
  {
    // When checked, we should list all the crossLinks in the
    // crossLinkListWidget, and not only the crossLinks for the
    // currently selected cross-linked monomer.

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


  bool
  MonomerCrossLinkDlg::parseTargetMonomerString(
    QList<const libXpertMass::Monomer *> *monomerList)
  {
    Q_ASSERT(monomerList);

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

    // Unspacify text.
    text.remove(QRegularExpression("\\s+"));

    QStringList positionList = text.split(";", Qt::SkipEmptyParts);

    if(!positionList.size())
      return false;

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

    // For each position, make sure that we can get a monomer pointer
    // for it.

    for(int iter = 0; iter < positionList.size(); ++iter)
      {
        QString position = positionList.at(iter);

        // Convert to int.
        bool ok   = false;
        int index = position.toInt(&ok, 10) - 1;

        // Check conversion and polymer sequence array boundaries.

        if((!index && !ok) || index >= polymer->size() || index < 0)
          {
            QMessageBox::warning(this,
                                 tr("massXpert - Cross-link monomers"),
                                 tr("%1@%2\n"
                                    "One monomer position is erroneous:")
                                   .arg(__FILE__)
                                   .arg(__LINE__)
                                   .arg(position),
                                 QMessageBox::Ok);

            return false;
          }

        const libXpertMass::Monomer *monomer = polymer->at(index);

        monomerList->append(monomer);
      }

    return true;
  }


  void
  MonomerCrossLinkDlg::crossLink()
  {
    if(m_polymerSequenceModified)
      {
        int ret = QMessageBox::warning(this,
                                       tr("massXpert - Cross-link monomers"),
                                       tr("%1@%2\n"
                                          "The polymer sequence has changed, "
                                          "continue cross-link?")
                                         .arg(__FILE__)
                                         .arg(__LINE__),
                                       QMessageBox::Yes | QMessageBox::No);

        if(ret == QMessageBox::No)
          return;
      }

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

    // Get the crossLink currently selected.
    QList<QListWidgetItem *> selectedList =
      m_ui.crossLinkerListWidget->selectedItems();

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

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

    // With the name of the cross-link get to the libXpertMass::CrossLink proper.

    libXpertMass::CrossLinker *crossLinker = new libXpertMass::CrossLinker(
      polymer->getPolChemDefCstSPtr(), "NOT_SET", "NOT_SET");

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

    //   if (!res)
    //     {
    //       QMessageBox::warning(this,
    // 			    tr("massXpert - Cross-link monomers"),
    // 			    tr("%1@%2\n"
    // 				"CrossLinker '%3' is not known.")
    // 			    .arg(__FILE__)
    // 			    .arg(__LINE__)
    // 			    .arg(text),
    // 			    QMessageBox::Ok);
    //       return;
    //     }


    // The user entered a string in the form "3;33" or more("3;33;66",
    // for example) to represent the positions at which the monomers are
    // getting crossLinked. Deconstruct that string and using the
    // indices(2 and 32 if string is "3;33") get to the pointers to the
    // monomers.

    QList<const libXpertMass::Monomer *> targetMonomerList;

    if(!parseTargetMonomerString(&targetMonomerList))
      return;

    int targetNumber = targetMonomerList.size();

    // The logic here is that if the crosslinker has no modification,
    // the user might enter 2 monomers or more. Instead, if the
    // crosslinker defines x modifications(with x >= 2), then the
    // number of monomers must match that x. The numer of modifications
    // cannot be 1 because the number of target monomers cannot be less
    // than 2.

    if(targetNumber < 2)
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The number of monomers engaged in the ."
                                "crossLink cannot be less than 2.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);
        return;
      }

    int modifNumber = m_ui.crossLinkModifListWidget->count();

    if(modifNumber && targetNumber < modifNumber)
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The number of monomers engaged in the ."
                                "crossLink must match the number of "
                                "modifications defined in the crossLinker.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);
        return;
      }

    // The parsing of the user data seems correct, thus we can finally
    // create a crossLink instance. First get the comment, if any, so
    // that we can initialize the crossLink correctly.

    QString comment = m_ui.crossLinkCommentLineEdit->text();

    libXpertMass::CrossLink *myCrossLink =
      new libXpertMass::CrossLink(*crossLinker, polymer, comment);

    // Now use the temporary list of monomer pointers that point to the
    // monomers to crossLink : we want to copy these monomer pointers
    // into the list of monomer pointer that belongs to the crossLink
    // instance allocated above. Attention, order with which the
    // monomers is relevant, so append() is required, so that the
    // monomers are set accordingly to the order set by the user in the
    // lineEdit widget.

    for(int iter = 0; iter < targetMonomerList.size(); ++iter)
      {
        const libXpertMass::Monomer *monomer = targetMonomerList.at(iter);

        myCrossLink->appendMonomer(monomer);
      }

    // Now ask the polymer sequence to perform the crossLink using the
    // crossLink fully qualified here.

    if(!polymer->crossLink(myCrossLink))
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "Cross-link with %3 failed.")
                               .arg(__FILE__)
                               .arg(__LINE__)
                               .arg(text),
                             QMessageBox::Ok);
        return;
      }

    mp_editorWnd->setWindowModified(true);
    // But we know we have done that modif:
    m_polymerSequenceModified = false;

    // At this point the cross-link was chemically performed, but we
    // still need to make sure that the cross-link is rendered
    // graphically on each partner monomer.

    for(int iter = 0; iter < targetMonomerList.size(); ++iter)
      {
        const libXpertMass::Monomer *monomer = targetMonomerList.at(iter);

        // Get the index in the polymer of the currently iterated
        // monomer.
        int index = polymer->monomerIndex(monomer);

        // At this point we can ask for the monomer's vignette
        // cross-link.

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

        int ret = mp_editorWnd->mpa_editorGraphicsView->crossLinkVignetteAt(
          index, myCrossLink);

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

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


  void
  MonomerCrossLinkDlg::uncrossLink()
  {
    if(m_polymerSequenceModified)
      {
        int ret = QMessageBox::warning(this,
                                       tr("massXpert - Cross-link monomers"),
                                       tr("%1@%2\n"
                                          "The polymer sequence has changed, "
                                          "continue uncross-link?")
                                         .arg(__FILE__)
                                         .arg(__LINE__),
                                       QMessageBox::Yes | QMessageBox::No);

        if(ret == QMessageBox::No)
          return;
      }

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

    // The crossLinkListWidget is a single-selection list.

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

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

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

    // VERY FIRST STEP IS TO REMOVE THE ITEM FROM THE LIST
    QListWidgetItem *item = m_ui.crossLinkListWidget->takeItem(index);

    // What's the text of the item(like "DisulfideBond/;2;6;/136958312")?
    QString text = item->text();

    // Clear the crossLink data, as these are no more faithful of the
    // newly selected item, as we have removed the item in question from
    // the listwidget.

    //  The crossLinker name.
    m_ui.crossLinkerNameLineEdit->setText("");
    // The modifications in the CrossLinker.
    m_ui.crossLinkModifListWidget->clear();
    // The crossLink comment.
    m_ui.crossLinkCommentLineEdit->setText("");
    // The monomers involved in the crossLink.
    m_ui.targetPositionListWidget->clear();
    // The target position.
    m_ui.targetPositionLineEdit->setText("");

    // And now deconstruct the text that is in the form
    // "DisulfideBond/;2;6;/0x456F5" if for example, the cross-linked
    // monomers are at positions 2 and 6 and are crossLinked with a
    // DisulfideBond. Also, the crossLink in memory has a pointer of
    // which the integer representation is 136958312.

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

    // The crossLink(quintptr) pointer is the third string in the list.
    bool ok                = false;
    quintptr pointerToCast = stringList.at(2).toLongLong(&ok, 10);

    if(!ok)
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "The polymer sequence has changed.\n"
                                "Avoid modifying the sequence while "
                                "working with cross-links.")
                               .arg(__FILE__)
                               .arg(__LINE__),
                             QMessageBox::Ok);

        return;
      }

    libXpertMass::CrossLink *crossLink =
      reinterpret_cast<libXpertMass::CrossLink *>(pointerToCast);

    // For each monomer in the cross-link we have to uncrossLink the
    // vignette. Next, we'll destroy the cross-link from the polymer
    // sequence itself.

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

        // Get the index in the polymer of the currently iterated
        // monomer.
        int index = polymer->monomerIndex(monomer);

        // At this point we can ask for the monomer's vignette
        // uncross-link.

        //// CAUTION, note that we are using the crossLink pointer here
        //// to identify the crossLink of which the vignettes should be
        //// removed. While the pointer is still "valid", that is non-0,
        //// what it points to is now deleted...

        bool val = mp_editorWnd->mpa_editorGraphicsView->uncrossLinkVignetteAt(
          index, crossLink);

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

    // Now ask the polymer sequence to perform the uncrossLink using
    // the crossLink fully qualified here.

    if(!polymer->uncrossLink(crossLink))
      {
        QMessageBox::warning(this,
                             tr("massXpert - Cross-link monomers"),
                             tr("%1@%2\n"
                                "Uncross-link for %3 failed.")
                               .arg(__FILE__)
                               .arg(__LINE__)
                               .arg(text),
                             QMessageBox::Ok);

        return;
      }

    mp_editorWnd->setWindowModified(true);
    // But we know we have done that modif:
    m_polymerSequenceModified = false;

    // No need to delete the crossLink, as the uncrossLink'ing has done
    // that work.

    mp_editorWnd->updateWholeSequenceMasses(true);

    mp_editorWnd->mpa_editorGraphicsView->resetSelection();
    mp_editorWnd->updateSelectedSequenceMasses(true);
  }


  void
  MonomerCrossLinkDlg::crossLinkChangedSlot(libXpertMass::Polymer *polymer)
  {
    Q_ASSERT(polymer);
    Q_ASSERT(polymer == mp_editorWnd->polymer());

    // We simply have to redisplay all the stuff.

    populateCrossLinkedMonomerList();
    populateCrossLinkList(true);
  }

  void
  MonomerCrossLinkDlg::polymerSequenceModifiedSlot()
  {
    m_polymerSequenceModified = true;
  }

} // namespace massxpert

} // namespace MsXpS
