/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2026 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the MsXpertSuite project.
 *
 * The MsXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - MassXpert, model polymer chemistries and simulate mass spectrometric data;
 * - MineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


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


/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassCore/PolChemDef.hpp>
#include <MsXpS/libXpertMassCore/CrossLinker.hpp>


/////////////////////// Local includes
#include "CrossLinkerDefDlg.hpp"
#include "PolChemDefWnd.hpp"


namespace MsXpS
{

namespace MassXpert
{


CrossLinkerDefDlg::CrossLinkerDefDlg(
  libXpertMassCore::PolChemDefSPtr pol_chem_def_sp,
  PolChemDefWnd *pol_chem_def_wnd_p,
  const QString &settings_file_path,
  const QString &application_name,
  const QString &description)
  : AbstractPolChemDefDependentDlg(pol_chem_def_sp,
                                   pol_chem_def_wnd_p,
                                   settings_file_path,
                                   "CrossLinkerDefDlg",
                                   application_name,
                                   description),
    mref_crossLinkers(pol_chem_def_sp->getCrossLinkersRef())
{
  if(!initialize())
    qFatal() << "Failed to initialize the dialog window.";
}


CrossLinkerDefDlg::~CrossLinkerDefDlg()
{
}

void
CrossLinkerDefDlg::closeEvent([[maybe_unused]] QCloseEvent *event)
{
  // No real close, because we did not ask that
  // close==destruction. Thus we only hide the dialog remembering its
  // position and size.

  mp_pol_chem_def_wnd_p->m_ui.crossLinkerPushButton->setChecked(false);

  writeSettings();
}


void
CrossLinkerDefDlg::readSettings()
{
  QSettings settings(m_settings_file_path, QSettings::IniFormat);

  settings.beginGroup(m_wndTypeName);
  restoreGeometry(settings.value("geometry").toByteArray());
  m_ui.splitter->restoreState(settings.value("splitter").toByteArray());
  settings.endGroup();
}


void
CrossLinkerDefDlg::writeSettings()
{
  QSettings settings(m_settings_file_path, QSettings::IniFormat);

  settings.beginGroup(m_wndTypeName);
  settings.setValue("geometry", saveGeometry());
  settings.setValue("splitter", m_ui.splitter->saveState());
  settings.endGroup();
}


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

  setWindowIcon(qApp->windowIcon());

  // Now we need to actually show the window title (that element is empty in
  // m_ui)
  displayWindowTitle();

  // Set all the crossLinkers to the list widget.

  for(libXpertMassCore::CrossLinkerSPtr cross_linker_sp : mref_crossLinkers)
    m_ui.crossLinkerListWidget->addItem(cross_linker_sp->getName());

  readSettings();

  populateAvailableModifList();

  // Make the connections.

  connect(m_ui.addCrossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addCrossLinkerPushButtonClicked()));

  connect(m_ui.removeCrossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeCrossLinkerPushButtonClicked()));

  connect(m_ui.moveUpCrossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpCrossLinkerPushButtonClicked()));

  connect(m_ui.moveDownCrossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownCrossLinkerPushButtonClicked()));

  connect(m_ui.addModifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(addModifPushButtonClicked()));

  connect(m_ui.removeModifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(removeModifPushButtonClicked()));

  connect(m_ui.moveUpModifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveUpModifPushButtonClicked()));

  connect(m_ui.moveDownModifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(moveDownModifPushButtonClicked()));

  connect(m_ui.applyCrossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyCrossLinkerPushButtonClicked()));

  connect(m_ui.applyModifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(applyModifPushButtonClicked()));

  connect(m_ui.validatePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(validatePushButtonClicked()));

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

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

  return true;
}


bool
CrossLinkerDefDlg::populateAvailableModifList()
{
  // The first item should be an empty item in case no modification
  // is present in the cross-linker.

  m_ui.modifNameComboBox->addItem("");
  for(libXpertMassCore::ModifSPtr modif_sp : msp_polChemDef->getModifsCstRef())
    m_ui.modifNameComboBox->addItem(modif_sp->getName());

  return true;
}


void
CrossLinkerDefDlg::addCrossLinkerPushButtonClicked()
{
  // We are asked to add a new crossLinker. We'll add it right after the
  // current item.

  // Returns -1 if the list is empty.
  int index = m_ui.crossLinkerListWidget->currentRow();

  libXpertMassCore::CrossLinkerSPtr cross_linker_sp =
    std::make_shared<libXpertMassCore::CrossLinker>(
      msp_polChemDef, "Type Name", "Type Formula", 0.0, 0.0);

  mref_crossLinkers.insert(mref_crossLinkers.begin() + index, cross_linker_sp);
  m_ui.crossLinkerListWidget->insertItem(index, cross_linker_sp->getName());

  setModified();

  // Needed so that the setCurrentRow() call below actually set the
  // current row!
  if(index <= 0)
    index = 0;

  m_ui.crossLinkerListWidget->setCurrentRow(index);

  // Erase cleavageRule data that might be left over by precedent current
  // crossLinker.
  updateModifDetails(0);

  // Set the focus to the lineEdit that holds the name of the crossLinker.
  m_ui.nameLineEdit->setFocus();
  m_ui.nameLineEdit->selectAll();
}


void
CrossLinkerDefDlg::removeCrossLinkerPushButtonClicked()
{
  QList<QListWidgetItem *> selectedList =
    m_ui.crossLinkerListWidget->selectedItems();

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

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

  QListWidgetItem *item = m_ui.crossLinkerListWidget->takeItem(index);
  delete item;

  mref_crossLinkers.erase(mref_crossLinkers.begin() + index);

  setModified();

  // If there are remaining items, we want to set the next item the
  // currentItem. If not, then, the currentItem should be the one
  // preceding the crossLinker that we removed.

  if(m_ui.crossLinkerListWidget->count() >= index + 1)
    {
      m_ui.crossLinkerListWidget->setCurrentRow(index);
      crossLinkerListWidgetItemSelectionChanged();
    }

  // If there are no more items in the crossLinker list, remove all the items
  // from the modifList.

  if(!m_ui.crossLinkerListWidget->count())
    {
      m_ui.modifListWidget->clear();
      clearAllDetails();
    }
}


void
CrossLinkerDefDlg::moveUpCrossLinkerPushButtonClicked()
{
  // Move the current row to one index less.

  // If no crossLinker is selected, just return.

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

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

  // Get the index of the crossLinker and the crossLinker itself.
  int index = m_ui.crossLinkerListWidget->currentRow();

  auto it = mref_crossLinkers.begin() + index;
  // If the item is already at top of list, do nothing.
  if(it == mref_crossLinkers.begin())
    return;
  std::iter_swap(it, it - 1); // Swap with the previous element

  QListWidgetItem *item = m_ui.crossLinkerListWidget->takeItem(index);

  m_ui.crossLinkerListWidget->insertItem(index - 1, item);
  m_ui.crossLinkerListWidget->setCurrentRow(index - 1);
  crossLinkerListWidgetItemSelectionChanged();

  setModified();
}


void
CrossLinkerDefDlg::moveDownCrossLinkerPushButtonClicked()
{
  // Move the current row to one index less.

  // If no crossLinker is selected, just return.

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

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

  // Get the index of the crossLinker and the crossLinker itself.
  int index = m_ui.crossLinkerListWidget->currentRow();

  auto it = mref_crossLinkers.begin() + index;
  // If the item is already at bottom of list, do nothing.
  if(it == mref_crossLinkers.end())
    return;
  std::iter_swap(it, it + 1); // Swap with the next element

  QListWidgetItem *item = m_ui.crossLinkerListWidget->takeItem(index);
  m_ui.crossLinkerListWidget->insertItem(index + 1, item);
  m_ui.crossLinkerListWidget->setCurrentRow(index + 1);
  crossLinkerListWidgetItemSelectionChanged();

  setModified();
}


void
CrossLinkerDefDlg::addModifPushButtonClicked()
{
  // We are asked to add a new modif. We'll add it right after the
  // current item. Note however, that one crossLinker has to be
  // selected.

  qDebug() << "addModifPushButtonClicked";

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

  if(selectedList.size() != 1)
    {
      QMessageBox::information(
        this,
        tr("MassXpert3 - libXpertMassCore::CrossLinker definition"),
        tr("Please, select a crossLinker first."),
        QMessageBox::Ok);
      return;
    }

  // Get the index of the current crossLinker so that we know to which
  // crossLinker we'll add the modif.
  int index = m_ui.crossLinkerListWidget->currentRow();

  qDebug() << "The current cross-linker item is at index:" << index;

  // What's the actual crossLinker?
  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

  qDebug() << "The currently selected cross-linker at index:" << index
           << "with name:" << cross_linker_sp->getName();

  // Allocate the new modif.
  libXpertMassCore::ModifSPtr modif_sp = std::make_shared<libXpertMassCore::Modif>(
    msp_polChemDef, "Select modification");

  qDebug() << "Allocated new Modif:" << modif_sp->getName();

  // Get the row index of the current modif item. Returns -1 if the
  // list is empty.
  index = m_ui.modifListWidget->currentRow();
  qDebug() << "The index of the current modif row:" << index;

  // Needed so that the setCurrentRow() call below actually sets the
  // current row!
  if(index <= 0)
    index = 0;

  m_ui.modifListWidget->insertItem(index, modif_sp->getName());

  std::vector<libXpertMassCore::ModifCstSPtr> &modifs =
    cross_linker_sp->getModifsRef();

  qDebug() << "The current cross-linker currently has" << modifs.size()
           << "modifs. Now inserting the new modif.";

  modifs.insert(modifs.begin() + index, modif_sp);

  qDebug() << "Done inserting new Modif into the modif list widget."
           << "and in the cross-linker itself, which now has:" << modifs.size();

  m_ui.modifListWidget->setCurrentRow(index);

  setModified();

  // Set the focus to the lineEdit that holds the mass of the modif.
  m_ui.modifNameComboBox->setFocus();
}


void
CrossLinkerDefDlg::removeModifPushButtonClicked()
{
  QList<QListWidgetItem *> selectedList = m_ui.modifListWidget->selectedItems();

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

  // Get the index of the current crossLinker so that we know from
  // which crossLinker we'll remove the modif.
  int index = m_ui.crossLinkerListWidget->currentRow();

  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

  // Get the index of the current modif.
  index = m_ui.modifListWidget->currentRow();

  // First remove the item from the listwidget because that will have
  // modifListWidgetItemSelectionChanged() triggered and we have to
  // have the item in the modif list in the crossLinker! Otherwise a crash
  // occurs.
  QListWidgetItem *item = m_ui.modifListWidget->takeItem(index);
  delete item;

  // Remove the modif from the crossLinker proper.
  std::vector<MsXpS::libXpertMassCore::ModifCstSPtr> modifs =
    cross_linker_sp->getModifsRef();
  modifs.erase(modifs.begin() + index);

  // If there are remaining items, we want to set the next item the
  // currentItem. If not, then, the currentItem should be the one
  // preceding the crossLinker that we removed.

  if(m_ui.modifListWidget->count() >= index + 1)
    {
      m_ui.modifListWidget->setCurrentRow(index);
      modifListWidgetItemSelectionChanged();
    }

  // If there are no more items in the modif list, remove all the
  // details.

  if(!m_ui.modifListWidget->count())
    {
      updateModifDetails(0);
    }
  else
    {
    }

  setModified();
}


void
CrossLinkerDefDlg::moveUpModifPushButtonClicked()
{
  // Move the current row to one index less.

  // If no modif is selected, just return.

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

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

  // Get the crossLinker to which the modif belongs.
  int index = m_ui.crossLinkerListWidget->currentRow();
  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

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

  auto it = cross_linker_sp->getModifsRef().begin() + index;
  // If the item is already at top of list, do nothing.
  if(it == cross_linker_sp->getModifsRef().begin())
    return;
  std::iter_swap(it, it - 1);

  QListWidgetItem *item = m_ui.modifListWidget->takeItem(index);
  m_ui.modifListWidget->insertItem(index - 1, item);
  m_ui.modifListWidget->setCurrentRow(index - 1);
  modifListWidgetItemSelectionChanged();

  setModified();
}


void
CrossLinkerDefDlg::moveDownModifPushButtonClicked()
{
  // Move the current row to one index less.

  // If no modif is selected, just return.

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

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

  // Get the crossLinker to which the modif belongs.
  int index = m_ui.crossLinkerListWidget->currentRow();
  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

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

  auto it = cross_linker_sp->getModifsRef().begin() + index;
  // If the item is already at bottom of list, do nothing.
  if(it == cross_linker_sp->getModifsRef().end())
    return;
  std::iter_swap(it, it + 1);

  QListWidgetItem *item = m_ui.modifListWidget->takeItem(index);
  m_ui.modifListWidget->insertItem(index + 1, item);
  m_ui.modifListWidget->setCurrentRow(index + 1);
  modifListWidgetItemSelectionChanged();

  setModified();
}


void
CrossLinkerDefDlg::applyCrossLinkerPushButtonClicked()
{
  // We are asked to apply the data for the crossLinker.

  // If no crossLinker is selected, just return.

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

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

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

  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

  // We do not want more than one crossLinker by the same name.

  bool wasEditFormulaEmpty = false;

  QString editName    = m_ui.nameLineEdit->text();
  QString editFormula = m_ui.formulaLineEdit->text();
  if(editFormula.isEmpty())
    {
      editFormula = "+Nul";

      wasEditFormulaEmpty = true;
    }

  // If a crossLinker is found in the list with the same name, and that
  // crossLinker is not the one that is current in the crossLinker list,
  // then we are making a double entry, which is not allowed.

  std::vector<libXpertMassCore::CrossLinkerSPtr>::iterator the_iterator =
    std::find_if(
      mref_crossLinkers.begin(),
      mref_crossLinkers.end(),
      [editName](const libXpertMassCore::CrossLinkerSPtr iter_cross_linker_sp) {
        return iter_cross_linker_sp->getName() == editName;
      });

  if(the_iterator != mref_crossLinkers.end() &&
     std::distance(mref_crossLinkers.begin(), the_iterator) != index)
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - CrossLinker definition"),
                           tr("A cross-linker with same name "
                              "exists already."),
                           QMessageBox::Ok);
      return;
    }

  // A crossLinker name cannot have the same name as a
  // modification. This is because the name of the crossLinker cannot
  // clash with the name of a modif when setting the graphical
  // vignette of the crossLinker.
  if(msp_polChemDef->getModifIndexByName(editName) != -1)
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - CrossLinker definition"),
                           tr("The name of the cross-linker is already "
                              "used by a modification."),
                           QMessageBox::Ok);
      return;
    }

  libXpertMassCore::Formula formula(editFormula);

  libXpertMassCore::IsotopicDataSPtr isotopic_data_sp =
    msp_polChemDef->getIsotopicDataSPtr();

  libXpertMassCore::ErrorList error_list;
  if(!formula.validate(isotopic_data_sp, &error_list))
    {
      if(wasEditFormulaEmpty)
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - libXpertMassCore::CrossLinker definition"),
            tr("The formula is empty, please enter a formula "
               "like '-H+H' if no reaction is required."),
            QMessageBox::Ok);
        }
      else
        {
          QMessageBox::warning(
            this,
            tr("MassXpert3 - libXpertMassCore::CrossLinker definition"),
            QString("The formula failed to validate with errors:\n%1\n.")
              .arg(libXpertMassCore::Utils::joinErrorList(error_list)),
            QMessageBox::Ok);
        }

      return;
    }

  // Finally we can update the crossLinker's data:

  cross_linker_sp->setName(editName);
  cross_linker_sp->setFormula(editFormula);

  // Update the list widget item.

  QListWidgetItem *item = m_ui.crossLinkerListWidget->currentItem();
  item->setData(Qt::DisplayRole, cross_linker_sp->getName());

  setModified();
}


void
CrossLinkerDefDlg::applyModifPushButtonClicked()
{
  // We are asked to apply the data for the modif.

  // If no modif is selected, just return.

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

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

  // Get the crossLinker to which the modif belongs.
  int index = m_ui.crossLinkerListWidget->currentRow();
  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

  // Get the index of the current cross-linker modif item.
  index = m_ui.modifListWidget->currentRow();

  QString modif_name = m_ui.modifNameComboBox->currentText();
  qDebug() << "The current modification in the Modif combo box widget:"
           << modif_name;

  // Make sure the modification name is known to the polymer chemistry
  // definition.

  libXpertMassCore::ModifCstSPtr reference_modif_csp =
    msp_polChemDef->getModifCstSPtrByName(modif_name);

  if(reference_modif_csp == nullptr)
    {
      QMessageBox::warning(this,
                           tr("MassXpert3 - CrossLinker definition"),
                           tr("The modification is not known to the "
                              "polymer chemistry definition."),
                           QMessageBox::Ok);
      return;
    }

  // Copy the reference modification to the modification that sits inside of the
  // CrossLinker being defined.

  // Get the modif itself from the crossLinker.
  libXpertMassCore::ModifCstSPtr modif_csp =
    cross_linker_sp->getModifsCstRef().at(index);

  qDebug() << "The current CrossLinker Modif instance index:" << index
           << "with name:" << modif_csp->getName();

#if 0
  // Method involving first insertion of the new pointer at index
  // and then erasure of the existing pointer that is now at index + 1.
  cross_linker_sp->getModifsRef().insert(
    cross_linker_sp->getModifsRef().begin() + index, reference_modif_csp);

  cross_linker_sp->getModifsRef().erase(
    cross_linker_sp->getModifsRef().begin() + index + 1);
#endif

  // Simpler method with modification in place using the  [ ] syntax.
  // Shown to work fine.
  cross_linker_sp->getModifsRef()[index] = reference_modif_csp;

  // Update the list widget item.

  QListWidgetItem *item = m_ui.modifListWidget->currentItem();
  item->setData(Qt::DisplayRole,
                cross_linker_sp->getModifsCstRef().at(index)->getName());

  qDebug() << "Set the Modif:"
           << cross_linker_sp->getModifsCstRef().at(index)->toString();

  setModified();
}


bool
CrossLinkerDefDlg::validatePushButtonClicked()
{

  MsXpS::libXpertMassCore::ErrorList error_list;

  // All we have to do is validate the crossLinker definition. For that we'll
  // go in the listwidget items one after the other and make sure that
  // everything is fine and that colinearity is perfect between the
  // crossLinker list and the listwidget.

  std::size_t item_count = m_ui.crossLinkerListWidget->count();

  if(item_count != mref_crossLinkers.size())
    {
      error_list.append(
        QString(tr("\nThe number of cross-linkers in the "
                   "list widget \n"
                   "and in the list of cross-linkers "
                   "is not identical.\n")));

      QMessageBox::warning(
        this,
        tr("MassXpert3 - CrossLinker definition"),
        libXpertMassCore::Utils::joinErrorList(error_list, "\n"),
        QMessageBox::Ok);
      return false;
    }

  for(std::size_t iter = 0; iter < mref_crossLinkers.size(); ++iter)
    {
      QListWidgetItem *item = m_ui.crossLinkerListWidget->item(iter);

      libXpertMassCore::CrossLinkerSPtr cross_linker_sp =
        mref_crossLinkers.at(iter);

      if(item->text() != cross_linker_sp->getName())
        error_list.append(
          QString(tr("\nCrossLinker at index %1 has not the same\n"
                     "name as the list widget item at the\n"
                     "same index.\n")
                    .arg(iter)));


      if(!cross_linker_sp->validate(&error_list))
        error_list.append(
          QString("\nCrossLinker at index %1 failed "
                  "to validate.\n"
                  "Errors: %2\n\nPlease, make sure that there are either no"
                  " or at least two modifications for the cross-link.\n")
            .arg(iter)
            .arg(libXpertMassCore::Utils::joinErrorList(error_list)));
    }

  if(error_list.size())
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - libXpertMassCore::CrossLinker definition"),
        libXpertMassCore::Utils::joinErrorList(error_list, "\n"),
        QMessageBox::Ok);
      return false;
    }
  else
    {
      QMessageBox::warning(
        this,
        tr("MassXpert3 - libXpertMassCore::CrossLinker definition"),
        ("Validation: success\n"),
        QMessageBox::Ok);
    }

  return true;
}


void
CrossLinkerDefDlg::crossLinkerListWidgetItemSelectionChanged()
{
  // The crossLinker item has changed. Empty the modif list and update its
  // contents. Update the details for the crossLinker.

  // The list is a single-item-selection list.

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

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

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

  libXpertMassCore::CrossLinkerSPtr cross_linker_sp = mref_crossLinkers.at(index);

  // Set the data of the crossLinker to their respective widgets.
  updateCrossLinkerIdentityDetails(cross_linker_sp);

  // The list of modifs
  m_ui.modifListWidget->clear();

  for(libXpertMassCore::ModifCstSPtr modif_csp : cross_linker_sp->getModifsCstRef())
    m_ui.modifListWidget->addItem(modif_csp->getName());

  if(!m_ui.modifListWidget->count())
    updateModifDetails(0);
  else
    {
      // And now select the first row in the modif list widget.
      m_ui.modifListWidget->setCurrentRow(0);
    }
}


void
CrossLinkerDefDlg::modifListWidgetItemSelectionChanged()
{
  // The modif item has changed. Update the details for the modif.

  // The list is a single-item-selection list.

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

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

  // Get the index of the current crossLinker.
  int index = m_ui.crossLinkerListWidget->currentRow();
  qDebug() << "The current cross-linker row is:" << index;

  // Find the modif object in the list of modifs.
  qDebug() << "There are" << mref_crossLinkers.size() << "cross-linkers.";

  libXpertMassCore::CrossLinkerSPtr cross_linker_sp =
    mref_crossLinkers.at(index);

  qDebug() << "The currently selected cross-linker at index:" << index
           << "with name:" << cross_linker_sp->getName();

  // Get the index of the current modif.
  index = m_ui.modifListWidget->currentRow();
  qDebug() << "The current modif row is:" << index;

  // Get the modif that is currently selected from the crossLinker's list
  // of modifs.
  qDebug() << "There are" << cross_linker_sp->getModifsCstRef().size()
           << "modifs in the cross-linker.";

  libXpertMassCore::ModifCstSPtr modif_csp =
    cross_linker_sp->getModifsCstRef().at(index);

  // Set the data of the modif to their respective widgets.
  qDebug() << "Now going to update the modif details.";

  updateModifDetails(modif_csp);
}


void
CrossLinkerDefDlg::updateCrossLinkerIdentityDetails(
  libXpertMassCore::CrossLinkerSPtr cross_link_sp)
{
  if(cross_link_sp != nullptr)
    {
      m_ui.nameLineEdit->setText(cross_link_sp->getName());
      m_ui.formulaLineEdit->setText(cross_link_sp->getFormula());
    }
  else
    {
      m_ui.nameLineEdit->setText("");
      m_ui.formulaLineEdit->setText("");
    }
}


void
CrossLinkerDefDlg::updateModifDetails(libXpertMassCore::ModifCstSPtr modif_csp)
{
  if(modif_csp != nullptr)
    {
      int index = m_ui.modifNameComboBox->findText(modif_csp->getName());

      // The name might be tr("Select modif") if the modif was just
      // created(see the function to add a modif to a
      // cross-linker), thus we cannot take for granted that we'll
      // find the modif in the comboBox !
      //	Q_ASSERT(index >=  0);
      if(index >= 0)
        m_ui.modifNameComboBox->setCurrentIndex(index);
      else
        // The first item is an empty item.
        m_ui.modifNameComboBox->setCurrentIndex(0);
    }
  else
    {
      // The first item is an empty item.
      m_ui.modifNameComboBox->setCurrentIndex(0);
    }
}


void
CrossLinkerDefDlg::clearAllDetails()
{
  m_ui.nameLineEdit->setText("");
  m_ui.formulaLineEdit->setText("");

  m_ui.modifNameComboBox->setCurrentIndex(0);
}


// VALIDATION
bool
CrossLinkerDefDlg::validate()
{
  return validatePushButtonClicked();
}

} // namespace MassXpert

} // namespace MsXpS
