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


/////////////////////// Local includes
#include <massxpert/gui/MonomerCrossLinkDlg.hpp>


namespace msXpSmassXpert
{

MonomerCrossLinkDlg::MonomerCrossLinkDlg(SequenceEditorWnd *editorWnd,
                                         /* no polymer **/
                                         /* no PolChemDef **/
                                         const QString &settingsFilePath)
  : AbstractSeqEdWndDependentDlg(editorWnd,
                                 0, /*polymer **/
                                 0, /*polChemDef **/
                                 settingsFilePath,
                                 "MonomerCrossLinkDlg",
                                 "Monomer cross-link")
{

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


MonomerCrossLinkDlg::~MonomerCrossLinkDlg()
{
}


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

  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()
{
  PolChemDef *polChemDef = mp_editorWnd->polChemDef();
  Q_ASSERT(polChemDef);

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

  for(int iter = 0; iter < polChemDef->crossLinkerList().size(); ++iter)
    {
      CrossLinker *crossLinker = polChemDef->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.
  Polymer *polymer = mp_editorWnd->polymer();

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

  for(int iter = 0; iter < polymer->crossLinkList().size(); ++iter)
    {
      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 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.
  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)
        {
          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('/', QString::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 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)
        {
          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.

  PolChemDef *polChemDef = mp_editorWnd->polChemDef();
  Q_ASSERT(polChemDef);

  CrossLinker crossLinker(polChemDef, "NOT_SET", "NOT_SET");

  if(!polChemDef->crossLinker(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)
    {
      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.
  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('/', QString::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;
    }

  Monomer *monomer = reinterpret_cast<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.
  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('/', QString::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;
    }

  CrossLink *crossLink = reinterpret_cast<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
  // CrossLink).  Make sure we remove all the items in the
  // crossLinkModifListWidget !
  m_ui.crossLinkModifListWidget->clear();

  for(int iter = 0; iter < crossLink->modifList().size(); ++iter)
    {
      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 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.
  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('/', QString::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);

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

  CoordinateList coordinateList;

  for(int iter = 0; iter < crossLink->monomerList()->size(); ++iter)
    {
      const 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);


      Coordinates *coordinates = new 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 Monomer *> *monomerList)
{
  Q_ASSERT(monomerList);

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

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

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

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

  // We'll need a pointer to the polymer sequence.
  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 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;
    }

  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 CrossLink proper.

  CrossLinker *crossLinker =
    new CrossLinker(polymer->polChemDef(), "NOT_SET", "NOT_SET");

  bool res = polymer->polChemDef()->crossLinker(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 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();

  CrossLink *myCrossLink = new 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 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 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;
    }

  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('/', QString::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;
    }

  CrossLink *crossLink = reinterpret_cast<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 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(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 msXpSmassXpert
