/* 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 <QFileDialog>
#include <QCloseEvent>


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


/////////////////////// Local includes
#include "PolChemDefWnd.hpp"
#include "ProgramWindow.hpp"
#include "Application.hpp"
// #include "AtomDefDlg.hpp"
#include "IsotopeDefDlg.hpp"
#include "MonomerDefDlg.hpp"
#include "ModifDefDlg.hpp"
#include "CleavageAgentDefDlg.hpp"
#include "FragmentationPathwayDefDlg.hpp"
#include "CrossLinkerDefDlg.hpp"


namespace MsXpS
{

namespace MassXpert
{


PolChemDefWnd::PolChemDefWnd(ProgramWindow *parent,
                             const QString &polChemDefFilePath,
                             const QString &application_name,
                             const QString &description)
  : AbstractMainTaskWindow(
      parent, "PolChemDefWnd", application_name, description)
{
  // Start by creating the polymer chemistry definition.
  // Note that it is perfectly possible to create a window with not polymer
  // chemistry definition file path if the user asks to create a brand new
  // polymer chemistry definition.

  // Allocate a brand new empty polymer chemistry definition.
  msp_polChemDef = std::make_shared<libXpertMassCore::PolChemDef>();

  if(!polChemDefFilePath.isEmpty())
    msp_polChemDef->setXmlDataFilePath(polChemDefFilePath);

  if(!initialize())
    {
      QMessageBox::warning(0,
                           QString("MassXpert - %1").arg(m_wndTypeName),
                           "Failed to initialize the polymer "
                           "chemistry definition window.",
                           QMessageBox::Ok);
    }

  readSettings();

  show();
}


PolChemDefWnd::~PolChemDefWnd()
{
  // These values must have been set to 0 in the constructor !!!
  // delete mpa_atomDefDlg;
  delete mpa_monomerDefDlg;
  delete mpa_isotopeDefDlg;
  delete mpa_modifDefDlg;
  delete mpa_cleaveSpecDefDlg;
  delete mpa_fragSpecDefDlg;
  delete mpa_crossLinkerDefDlg;
}


void
PolChemDefWnd::closeEvent(QCloseEvent *event)
{
  // We are asked to close the window even if it has unsaved data.
  if(m_forciblyClose || maybeSave())
    {
      writeSettings();
      event->accept();
      return;
    }
  else
    {
      event->ignore();
    }
}


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

  setWindowIcon(qApp->windowIcon());

  setAttribute(Qt::WA_DeleteOnClose);
  statusBar()->setSizeGripEnabled(true);

  m_ui.ionizeChargeSpinBox->setRange(-1000000000, 1000000000);
  m_ui.ionizeLevelSpinBox->setRange(0, 1000000000);


  ////// Connection of the SIGNALS and SLOTS //////

  // SINGULAR ENTITIES
  connect(m_ui.nameLineEdit,
          SIGNAL(editingFinished()),
          this,
          SLOT(nameEditFinished()));

  connect(m_ui.leftCapLineEdit,
          SIGNAL(editingFinished()),
          this,
          SLOT(leftCapEditFinished()));

  connect(m_ui.rightCapLineEdit,
          SIGNAL(editingFinished()),
          this,
          SLOT(rightCapEditFinished()));

  connect(m_ui.ionizeFormulaLineEdit,
          SIGNAL(editingFinished()),
          this,
          SLOT(ionizeFormulaEditFinished()));

  connect(m_ui.ionizeChargeSpinBox,
          SIGNAL(valueChanged(int)),
          this,
          SLOT(ionizeChargeChanged()));

  connect(m_ui.ionizeLevelSpinBox,
          SIGNAL(valueChanged(int)),
          this,
          SLOT(ionizeLevelChanged()));


  // PLURAL ENTITIES
  // connect(m_ui.atomPushButton,
  // SIGNAL(clicked()),
  // this,
  // SLOT(atomPushButtonClicked()));

  connect(m_ui.isotopePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(isotopePushButtonClicked()));

  connect(m_ui.monomerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(monomerPushButtonClicked()));

  connect(m_ui.modifPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(modifPushButtonClicked()));

  connect(m_ui.crossLinkerPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(crossLinkerPushButtonClicked()));

  connect(m_ui.cleavagePushButton,
          SIGNAL(clicked()),
          this,
          SLOT(cleavagePushButtonClicked()));

  connect(m_ui.fragmentationPushButton,
          SIGNAL(clicked()),
          this,
          SLOT(fragmentationPushButtonClicked()));


  // SAVE/SAVE AS
  connect(
    m_ui.savePushButton, &QPushButton::clicked, this, &PolChemDefWnd::save);
  connect(
    m_ui.saveAsPushButton, &QPushButton::clicked, this, &PolChemDefWnd::saveAs);


  // VALIDATION
  connect(m_ui.validatePushButton, &QPushButton::clicked, this, [&]() {
    libXpertMassCore::ErrorList error_list;
    validate(&error_list);
    if(error_list.size())
      {
        QMessageBox::warning(
          this,
          QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription),
          tr("%1@%2\n"
             "Failed to validate the polymer chemistry definition.")
            .arg(__FILE__)
            .arg(__LINE__),
          QMessageBox::Ok);
      }
  });


  // We have to set the name with the [*] placeholder right now
  // because the functions below complain, otherwise.

  updateWindowTitle();

  // If there was a path available in the constructor, then the polymer
  // chemistry definition has it. Load the file!
  if(!msp_polChemDef->getXmlDataFilePath().isEmpty())
    {
      // We first need to load the isotopic data.

      if(!libXpertMassCore::PolChemDef::renderXmlPolChemDefFile(msp_polChemDef))
        {
          QMessageBox::warning(
            this,
            QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription),
            tr("%1@%2\n"
               "Failed to render the polymer chemistry definition file.")
              .arg(__FILE__)
              .arg(__LINE__),
            QMessageBox::Ok);

          return false;
        }

      m_ui.nameLineEdit->setText(msp_polChemDef->getName());
    }
  else
    {
      // No, we really opened an EMPTY polymer chemistry definition window,
      // nothing chemical is set.
      qDebug() << "The polymer chemistry definition window was opened with no "
                  "definition.";
    }

  if(!setSingularData())
    return false;

  if(!msp_polChemDef->getName().isEmpty())
    statusBar()->showMessage("Correctly opened file.");

  setWindowModified(false);

  return true;
}


void
PolChemDefWnd::nameEditFinished()
{
  if(m_ui.nameLineEdit->text().isEmpty())
    statusBar()->showMessage(
      "Name of the polymer chemistry "
      "definition cannot be empty.");
  else
    {
      if(m_ui.nameLineEdit->isModified())
        {
          msp_polChemDef->setName(m_ui.nameLineEdit->text());

          m_ui.nameLineEdit->setModified(true);

          setWindowModified(true);
        }
    }
}


void
PolChemDefWnd::leftCapEditFinished()
{
  if(!m_ui.leftCapLineEdit->isModified())
    return;

  if(!checkIsotopicData())
    {
      return;
    }

  libXpertMassCore::IsotopicDataCstSPtr isotopic_data_csp =
    msp_polChemDef->getIsotopicDataCstSPtr();

  libXpertMassCore::Formula formula =
    libXpertMassCore::Formula(m_ui.leftCapLineEdit->text());

  libXpertMassCore::ErrorList error_list;

  if(!formula.validate(isotopic_data_csp, &error_list))
    {
      statusBar()->showMessage(
        tr("Formula(%1) is invalid.").arg(formula.getActionFormula()));

      m_ui.leftCapLineEdit->setText(
        msp_polChemDef->getLeftCap().getActionFormula());
    }
  else
    {
      msp_polChemDef->setLeftCap(formula);

      m_ui.leftCapLineEdit->setModified(false);

      setWindowModified(true);
    }

  return;
}


void
PolChemDefWnd::rightCapEditFinished()
{
  if(!m_ui.rightCapLineEdit->isModified())
    return;

  if(!checkIsotopicData())
    {
      return;
    }

  libXpertMassCore::IsotopicDataCstSPtr isotopic_data_csp =
    msp_polChemDef->getIsotopicDataCstSPtr();

  libXpertMassCore::Formula formula =
    libXpertMassCore::Formula(m_ui.rightCapLineEdit->text());

  libXpertMassCore::ErrorList error_list;

  if(!formula.validate(isotopic_data_csp, &error_list))
    {
      statusBar()->showMessage(
        tr("Formula(%1) is invalid.").arg(formula.getActionFormula()));

      m_ui.rightCapLineEdit->setText(
        msp_polChemDef->getRightCap().getActionFormula());
    }
  else
    {
      msp_polChemDef->setRightCap(formula);

      m_ui.rightCapLineEdit->setModified(false);

      setWindowModified(true);
    }

  return;
}


void
PolChemDefWnd::ionizeFormulaEditFinished()
{
  if(!m_ui.ionizeFormulaLineEdit->isModified())
    return;

  if(!checkIsotopicData())
    return;

  libXpertMassCore::IsotopicDataCstSPtr isotopic_data_csp =
    msp_polChemDef->getIsotopicDataCstSPtr();

  libXpertMassCore::Formula formula =
    libXpertMassCore::Formula(m_ui.ionizeFormulaLineEdit->text());

  libXpertMassCore::ErrorList error_list;

  if(!formula.validate(isotopic_data_csp, &error_list))
    {
      statusBar()->showMessage(
        tr("Formula(%1) is invalid.").arg(formula.getActionFormula()));

      m_ui.ionizeFormulaLineEdit->setText(msp_polChemDef->getIonizerCstRef()
                                            .getFormulaCstRef()
                                            .getActionFormula());
    }
  else
    {
      msp_polChemDef->getIonizerRef().setFormula(formula.getActionFormula());

      m_ui.ionizeFormulaLineEdit->setModified(false);

      setWindowModified(true);
    }

  return;
}


void
PolChemDefWnd::ionizeChargeChanged()
{
  int charge = m_ui.ionizeChargeSpinBox->value();

  msp_polChemDef->getIonizerRef().setNominalCharge(charge);

  setWindowModified(true);

  return;
}


void
PolChemDefWnd::ionizeLevelChanged()
{
  int level = m_ui.ionizeLevelSpinBox->value();

  msp_polChemDef->getIonizerRef().setLevel(level);

  setWindowModified(true);

  return;
}


bool
PolChemDefWnd::setSingularData()
{
  m_ui.leftCapLineEdit->setText(
    msp_polChemDef->getLeftCap().getActionFormula());
  m_ui.rightCapLineEdit->setText(
    msp_polChemDef->getRightCap().getActionFormula());

  m_ui.ionizeFormulaLineEdit->setText(
    msp_polChemDef->getIonizerCstRef().getFormulaCstRef().getActionFormula());

  m_ui.ionizeChargeSpinBox->setValue(
    msp_polChemDef->getIonizerCstRef().getNominalCharge());
  m_ui.ionizeLevelSpinBox->setValue(
    msp_polChemDef->getIonizerCstRef().getLevel());

  return true;
}


bool
PolChemDefWnd::checkIsotopicData()
{
  libXpertMassCore::IsotopicDataCstSPtr isotopic_data_csp =
    msp_polChemDef->getIsotopicDataCstSPtr();

  if(isotopic_data_csp->size() < 1)
    {
      QMessageBox::warning(
        this,
        QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription),
        tr("Edit the atom definition first."),
        QMessageBox::Ok);

      return false;
    }

  return true;
}


void
PolChemDefWnd::updateWindowTitle()
{
  QString shownName = tr("Untitled");

  if(!msp_polChemDef->getXmlDataFilePath().isEmpty())
    {
      shownName = QFileInfo(msp_polChemDef->getXmlDataDirPath()).fileName();
    }

  // The [*] is a place holder that is updated to nothing if the window is not
  // modified and to * if the window is modified (call setWindowModified()).
  setWindowTitle(tr("%1 - %2 - %3[*]")
                   .arg(m_applicationName)
                   .arg(m_windowDescription)
                   .arg(shownName));
}


void
PolChemDefWnd::isotopePushButtonClicked()
{
  // Check if the atom definition window was constructed already. If
  // not we construct it.

  if(!mpa_isotopeDefDlg)
    {
      mpa_isotopeDefDlg =
        new IsotopeDefDlg(msp_polChemDef,
                          this,
                          dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
                          m_applicationName,
                          "Isotope definitions");

      mpa_isotopeDefDlg->show();
      m_ui.isotopePushButton->setChecked(true);

      return;
    }

  if(mpa_isotopeDefDlg->isVisible())
    {
      mpa_isotopeDefDlg->hide();
      m_ui.isotopePushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_isotopeDefDlg->show();
      m_ui.isotopePushButton->setChecked(true);

      return;
    }
}


void
PolChemDefWnd::monomerPushButtonClicked()
{
  // Check if the monomer definition window was constructed
  // already. If not we construct it.

  if(!mpa_monomerDefDlg)
    {
      mpa_monomerDefDlg =
        new MonomerDefDlg(msp_polChemDef,
                          this,
                          dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
                          m_applicationName,
                          "Monomer definitions");
      mpa_monomerDefDlg->show();
      m_ui.monomerPushButton->setChecked(true);

      return;
    }

  if(mpa_monomerDefDlg->isVisible())
    {
      mpa_monomerDefDlg->hide();
      m_ui.monomerPushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_monomerDefDlg->show();
      m_ui.monomerPushButton->setChecked(true);

      return;
    }
}


void
PolChemDefWnd::modifPushButtonClicked()
{
  // Check if the modif definition window was constructed
  // already. If not we construct it.

  if(!mpa_modifDefDlg)
    {
      mpa_modifDefDlg =
        new ModifDefDlg(msp_polChemDef,
                        this,
                        dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
                        m_applicationName,
                        "Modification definitions");
      mpa_modifDefDlg->show();
      m_ui.modifPushButton->setChecked(true);

      return;
    }

  if(mpa_modifDefDlg->isVisible())
    {
      mpa_modifDefDlg->hide();
      m_ui.modifPushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_modifDefDlg->show();
      m_ui.monomerPushButton->setChecked(true);

      return;
    }
}


void
PolChemDefWnd::crossLinkerPushButtonClicked()
{
  // Check if the crossLinker definition window was constructed
  // already. If not we construct it.

  if(!mpa_crossLinkerDefDlg)
    {
      mpa_crossLinkerDefDlg =
        new CrossLinkerDefDlg(msp_polChemDef,
                              this,
                              dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
                              m_applicationName,
                              "Cross-linker definitions");
      mpa_crossLinkerDefDlg->show();
      m_ui.crossLinkerPushButton->setChecked(true);

      return;
    }

  if(mpa_crossLinkerDefDlg->isVisible())
    {
      mpa_crossLinkerDefDlg->hide();
      m_ui.crossLinkerPushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_crossLinkerDefDlg->show();
      m_ui.crossLinkerPushButton->setChecked(true);

      return;
    }
}


void
PolChemDefWnd::cleavagePushButtonClicked()
{
  // Check if the cleaveSpec definition window was constructed already. If
  // not we construct it.

  if(!mpa_cleaveSpecDefDlg)
    {
      mpa_cleaveSpecDefDlg =
        new CleavageAgentDefDlg(msp_polChemDef,
                                this,
                                dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
                                m_applicationName,
                                "Cleavage specification definitions");
      mpa_cleaveSpecDefDlg->show();
      m_ui.cleavagePushButton->setChecked(true);

      return;
    }

  if(mpa_cleaveSpecDefDlg->isVisible())
    {
      mpa_cleaveSpecDefDlg->hide();
      m_ui.cleavagePushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_cleaveSpecDefDlg->show();
      m_ui.cleavagePushButton->setChecked(true);

      return;
    }
}


void
PolChemDefWnd::fragmentationPushButtonClicked()
{
  // Check if the fragSpec definition window was constructed already. If
  // not we construct it.

  if(!mpa_fragSpecDefDlg)
    {
      mpa_fragSpecDefDlg = new FragmentationPathwayDefDlg(
        msp_polChemDef,
        this,
        dynamic_cast<Application *>(qApp)->getUserConfigSettingsFilePath(),
        m_applicationName,
        "Fragmentation specification definitions");
      mpa_fragSpecDefDlg->show();
      m_ui.fragmentationPushButton->setChecked(true);

      return;
    }

  if(mpa_fragSpecDefDlg->isVisible())
    {
      mpa_fragSpecDefDlg->hide();
      m_ui.fragmentationPushButton->setChecked(false);

      return;
    }
  else
    {
      mpa_fragSpecDefDlg->show();
      m_ui.fragmentationPushButton->setChecked(true);

      return;
    }
}


bool
PolChemDefWnd::validate(libXpertMassCore::ErrorList *error_list_p)
{
  if(!checkIsotopicData())
    {
      error_list_p->push_back(
        "The isotopic data contain not a single isotope.");

      // We cannot even think of going on with the validation.

      return false;
    }

  // We know we have isotopic data, check them before anything else because
  // we'll need them for almost all the other validations below.

  libXpertMassCore::IsotopicDataCstSPtr isotopic_data_csp =
    msp_polChemDef->getIsotopicDataCstSPtr();

  // The validation of the isotopic data takes a vector of strings to store all
  // the errors.

  if(!isotopic_data_csp->validate(error_list_p))
    {
      error_list_p->push_back("The isotopic data failed to validate.");

      // We cannot even think of going on with the validation.

      return false;
    }

  QString name = m_ui.nameLineEdit->text();

  if(name.isEmpty())
    {
      error_list_p->push_back("Name cannot be empty.");
      return false;
    }

  const libXpertMassCore::Formula &lc_formula(msp_polChemDef->getLeftCap());

  if(!lc_formula.validate(isotopic_data_csp, error_list_p))
    {
      error_list_p->push_back("Validation of left cap failed.");
      return false;
    }

  const libXpertMassCore::Formula &rc_formula(msp_polChemDef->getLeftCap());
  if(!rc_formula.validate(isotopic_data_csp, error_list_p))
    {
      error_list_p->push_back("Validation of right cap failed.");
      return false;
    }

  libXpertMassCore::Ionizer ionizer = msp_polChemDef->getIonizerCstRef();
  if(!ionizer.validate(error_list_p))
    {
      error_list_p->push_back("Validation of ionizer failed.");
      return false;
    }

  for(const libXpertMassCore::MonomerCstSPtr monomer_csp :
      msp_polChemDef->getMonomersRef())
    {
      if(!monomer_csp->validate(error_list_p))
        {
          error_list_p->push_back("Validation of the monomers failed.");
          return false;
        }
    }

  for(libXpertMassCore::ModifCstSPtr modif_csp : msp_polChemDef->getModifsCstRef())
    {
      if(!modif_csp->validate(error_list_p))
        {
          error_list_p->push_back("Validation of the modifications failed.");
          return false;
        }
    }

  for(libXpertMassCore::CrossLinkerCstSPtr cross_linker_csp :
      msp_polChemDef->getCrossLinkersCstRef())
    {
      if(!cross_linker_csp->validate(error_list_p))
        {
          error_list_p->push_back("Validation of the cross-linkers failed.");
          return false;
        }
    }

  for(libXpertMassCore::CleavageAgentCstSPtr cleavage_agent_csp :
      msp_polChemDef->getCleavageAgentsCstRef())
    {
      if(!cleavage_agent_csp->validate(error_list_p))
        {
          error_list_p->push_back(
            "Validation of the cleavage specifications failed.");
          return false;
        }
    }

  for(libXpertMassCore::FragmentationPathwayCstSPtr fragmentation_pathway_csp :
      msp_polChemDef->getFragmentationPathwaysCstRef())
    {
      if(!fragmentation_pathway_csp->validate(error_list_p))
        {
          error_list_p->push_back(
            "Validation of the fragmentation pathways failed.");
          return false;
        }
    }

  statusBar()->showMessage(
    "Polymer chemistry definition "
    "data validation succeeded.");

  return true;
}


bool
PolChemDefWnd::maybeSave()
{
  // Returns true if we can continue(either saved ok or discard). If
  // save failed or cancel we return false to indicate to the caller
  // that something is wrong.

  if(isWindowModified())
    {
      QMessageBox::StandardButton ret;
      ret = QMessageBox::warning(
        this,
        QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription),
        tr("The document has been modified.\n"
           "Do you want to save your changes?"),
        QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);

      if(ret == QMessageBox::Save)
        {
          // We want to save the file. If the file has no existing
          // file associate the save as function will be called
          // automatically.
          return save();
        }
      else if(ret == QMessageBox::Discard)
        return true;
      else if(ret == QMessageBox::Cancel)
        return false;
    }

  return true;
}


bool
PolChemDefWnd::save()
{

  // Saving a polymer chemistry definition means saving data to two files:
  //
  // 1. First save the polymer chemistry data to an XML file, typically by the
  // name of the polymer chemistry definition. Once that file has been saved, it
  // is possible to use the directory in which the file was saved to craft the
  // isotopic data file path in which to save the isotopic data.
  //
  // 2. The isotopic data goes to the isotopic-data.dat file inside the polymer
  // chemistry data directory.
  //
  // It might be that the definition is totally new, in which case the
  // filePath() call will return something invalid as a QFile object. In that
  // case we ask the saveAs() to do the job.

  // We only save if the validation is ok.
  libXpertMassCore::ErrorList error_list;
  bool ok = validate(&error_list);

  if(!ok)
    {
      // Errors encountered.

      QMessageBox msgBox;

      msgBox.setText("Failed to save the document.\n\n");
      msgBox.setInformativeText(
        QString("The encountered errors:\n%1")
          .arg(libXpertMassCore::Utils::joinErrorList(error_list)));
      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  // Saving a polymer chemistry definition means first saving the isotopic data,
  // then the polymer chemistry definition remaining items.

  if(!QFile::exists(msp_polChemDef->getXmlDataFilePath()))
    return saveAs();

  if(!msp_polChemDef->writeXmlFile())
    {
      statusBar()->showMessage(tr("File save failed."), 3000);

      QMessageBox msgBox;

      msgBox.setText("Failed to save the polymer chemistry definition.");

      msgBox.setInformativeText(
        "Please, check that you have "
        "write permissions on the file.");

      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  // At this point we have succeded saving the xml file, we need to also save
  // the isotopic data. Use the crafted filename because we are saving the
  // isotopic data in the context of the saving of the whole polymer chemistry
  // definition (the IsotopeDefDlg has a save button where the user can save the
  // data in any other place).

  QString isotopic_data_file_path = msp_polChemDef->getXmlDataDirPath();

  QFile file_info(isotopic_data_file_path);

  if(!file_info.exists())
    {
      statusBar()->showMessage(tr("Isotopic data saving failed."), 5000);

      QMessageBox msgBox;

      msgBox.setText("Failed to save the isotopic data.");
      msgBox.setInformativeText(
        "Please, check that the polymer chemistry definition directory "
        "exists and is writable.");
      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  // Great, the polchemdef data directory exists, we can craft the isotopic data
  // file path.

  isotopic_data_file_path += QDir::separator();
  isotopic_data_file_path += "isotopic-data.dat";
  msp_polChemDef->setIsotopicDataFilePath(isotopic_data_file_path);

  // Now use the polymer chemistry definition save function.

  std::size_t isotope_count =
    libXpertMassCore::PolChemDef::writeIsotopicData(msp_polChemDef);

  if(!isotope_count)
    {
      statusBar()->showMessage(tr("Isotopic data saving failed."), 5000);

      QMessageBox msgBox;

      msgBox.setText("Failed to save the isotopic data.");
      msgBox.setInformativeText(
        "Please, check that the polymer chemistry definition directory "
        "exists and is writable and that the isotope data validate fine.");

      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  // Now save the isotopic data using the right isotopic data handler, that is,
  // the user config handler that mimicks the library handler.

  libXpertMassCore::IsotopicDataUserConfigHandler isotopic_data_handler(
    msp_polChemDef->getIsotopicDataSPtr());

  isotopic_data_handler.setFileName(isotopic_data_file_path);
  bool res = isotopic_data_handler.writeData();

  if(!res)
    {
      statusBar()->showMessage(tr("Isotopic data saving failed."), 5000);

      QMessageBox msgBox;

      msgBox.setText("Failed to save the isotopic data.");
      msgBox.setInformativeText(
        "Please, check that the polymer chemistry definition directory "
        "exists and is writable.");
      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  statusBar()->showMessage(tr("File save succeeded."), 3000);

  updateWindowTitle();

  setWindowModified(false);

  return true;
}


bool
PolChemDefWnd::saveAs()
{
  libXpertMassCore::ErrorList error_list;
  bool ok = validate(&error_list);

  if(!ok)
    {
      // Errors encountered.

      QMessageBox msgBox;

      msgBox.setText("Failed to save the document.\n\n");
      msgBox.setInformativeText(
        QString("The encountered errors:\n%1")
          .arg(libXpertMassCore::Utils::joinErrorList(error_list)));
      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  QString filePath = QFileDialog::getSaveFileName(this,
                                                  tr("Save Polymer chemistry "
                                                     "definition file"),
                                                  QDir::homePath(),
                                                  tr("XML files(*.xml *.XML)"));

  if(filePath.isEmpty())
    {
      statusBar()->showMessage(tr("File path is empty"), 3000);
      return false;
    }

  msp_polChemDef->setXmlDataFilePath(filePath);

  if(!msp_polChemDef->writeXmlFile())
    {
      statusBar()->showMessage(tr("File save failed."), 3000);

      QMessageBox msgBox;

      msgBox.setText("Failed to save the document.");
      msgBox.setInformativeText(
        "Please, check that you have "
        "write permissions on the file.");
      msgBox.setStandardButtons(QMessageBox::Ok);

      msgBox.exec();

      return false;
    }

  statusBar()->showMessage(tr("File save succeeded."), 3000);

  updateWindowTitle();

  setWindowModified(false);

  return true;
}

} // namespace MassXpert

} // namespace MsXpS
