/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.massxpert.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.
   

   This software 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 software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/


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


/////////////////////// Local includes
#include "application.hpp"
#include "monomerCrossLinkDlg.hpp"


namespace massXpert
{

  MonomerCrossLinkDlg::MonomerCrossLinkDlg(QWidget *parent)
    : QDialog(parent)
  {
    Q_ASSERT(parent);

    m_polymerSequenceModified = false;
    
    m_ui.setupUi(this);
  
    mp_editorWnd = static_cast<SequenceEditorWnd *>(parent);

    populateCrossLinkerList();
    populateCrossLinkedMonomerList();
    populateCrossLinkList();
  
    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
  
    settings.beginGroup("monomer_cross_link_dlg");

    restoreGeometry(settings.value("geometry").toByteArray());

    m_ui.leftSplitter->
      restoreState(settings.value("leftSplitterSize").toByteArray());
    m_ui.rightSplitter->
      restoreState(settings.value("leftSplitterSize").toByteArray());
  
    settings.endGroup();


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


  MonomerCrossLinkDlg::~MonomerCrossLinkDlg()
  {

  }


  void 
  MonomerCrossLinkDlg::closeEvent(QCloseEvent *event)
  {
    if (event)
      printf("%s", "");
  
    QSettings settings 
     (static_cast<Application *>(qApp)->configSettingsFilePath(), 
       QSettings::IniFormat);
  
    settings.beginGroup("monomer_cross_link_dlg");

    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 third string in the list.
    bool ok = false;
    quintptr monomerCastToInt = stringList.at(2).toInt(&ok);
  
    Monomer *monomer =reinterpret_cast<Monomer *>(monomerCastToInt);
    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 seqeunce 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 crossLinkCastToInt = stringList.at(2).toInt(&ok);
  
    CrossLink *crossLink =(CrossLink *) crossLinkCastToInt;
  
    // 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 crossLinkCastToInt = stringList.at(2).toInt(&ok);
  
    CrossLink *crossLink =(CrossLink *) crossLinkCastToInt;

    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 crossLinkCastToInt = stringList.at(2).toInt(&ok);
  
    CrossLink *crossLink =(CrossLink *) crossLinkCastToInt;
  
    // 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 massXpert
