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

/////////////////////// Local includes
#include "PolymerModificationDlg.hpp"
#include "ui_PolymerModificationDlg.h"


namespace MsXpS
{

namespace MassXpert
{

PolymerModificationDlg::PolymerModificationDlg(
  SequenceEditorWnd *editorWnd,
  /* no polymer **/
  /* no libXpertMassCore::PolChemDef **/
  const QString &settings_file_path,
  const QString &application_name,
  const QString &description,
  libXpertMassCore::Enums::PolymerEnd end)
  : AbstractSeqEdWndDependentDlg(editorWnd,
                                 0, /*polymer **/
                                 0, /*polChemDef **/
                                 settings_file_path,
                                 "PolymerModificationDlg",
                                 application_name,
                                 description),
    m_polymerEnd(end),
    mp_ui(new Ui::PolymerModificationDlg)
{
  if(!initialize())
    qFatal() << "Programming error. Failed to initialize dialog window.";
}


PolymerModificationDlg::~PolymerModificationDlg()
{
}


bool
PolymerModificationDlg::initialize()
{
  mp_ui->setupUi(this);

  setWindowIcon(qApp->windowIcon());

  // Update the window title because the window title element in mp_ui might be
  // either erroneous or empty.
  setWindowTitle(
    QString("%1 - %2").arg(m_applicationName).arg(m_windowDescription));

  populateModificationList();

  if(mp_editorWnd)
    updateModificationLineEdits();

  if(static_cast<int>(m_polymerEnd) &
     static_cast<int>(libXpertMassCore::Enums::PolymerEnd::LEFT))
    mp_ui->leftEndCheckBox->setChecked(true);

  if(static_cast<int>(m_polymerEnd) &
     static_cast<int>(libXpertMassCore::Enums::PolymerEnd::RIGHT))
    mp_ui->rightEndCheckBox->setChecked(true);

  readSettings();

  connect(this, SIGNAL(rejected()), this, SLOT(close()));
  connect(mp_ui->modifyPushButton,
          &QPushButton::clicked,
          this,
          &PolymerModificationDlg::modify);
  connect(mp_ui->unmodifyPushButton,
          &QPushButton::clicked,
          this,
          &PolymerModificationDlg::unmodify);

  return true;
}


void
PolymerModificationDlg::updateModificationLineEdits()
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  mp_ui->leftEndLineEdit->setText(
    QString("%1 = %2")
      .arg(polymer_sp->getLeftEndModifCstRef().getFormula())
      .arg(polymer_sp->getLeftEndModifCstRef().getName()));

  mp_ui->rightEndLineEdit->setText(
    QString("%1 = %2")
      .arg(polymer_sp->getRightEndModifCstRef().getFormula())
      .arg(polymer_sp->getRightEndModifCstRef().getName()));
}


bool
PolymerModificationDlg::populateModificationList()
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    mp_editorWnd->getPolChemDefRenderingCstRPtr()->getPolChemDefCstSPtr();
  Q_ASSERT(pol_chem_def_csp);

  for(libXpertMassCore::ModifCstSPtr modif_csp :
      pol_chem_def_csp->getModifsCstRef())
    mp_ui->modifListWidget->addItem(modif_csp->getName());

  return true;
}


bool
PolymerModificationDlg::parseModifDefinition(libXpertMassCore::Modif &modif)
{
  // We do not allow maxCount to be entered in the GUI, set it to 1.
  // Likewise for the targets.
  modif.setMaxCount(1);
  modif.setTargets("*");

  QString text = mp_ui->modifNameLineEdit->text();

  modif.setName(text);

  text = mp_ui->modifFormulaLineEdit->text();

  modif.setFormula(text);

  // Attention, we have to compute the masses of the modif!

  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    mp_editorWnd->getPolChemDefRenderingCstRPtr()->getPolChemDefCstSPtr();

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

  if(!modif.calculateMasses(isotopic_data_csp))
    return false;

  // We do not actually care of the targets of the modification. This
  // member datum should be '*' by default anyway.

  //  modif->setTargets("*");

  libXpertMassCore::ErrorList error_list;
  if(!modif.validate(&error_list))
    return false;

  return true;
}


void
PolymerModificationDlg::modify()
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  // There are two ways to perform a modification: either select one
  // modification from the list of available modifications as defined
  // in the polymer chemistry definition, or perform a quick and dirty
  // modification definition in the dialog.

  libXpertMassCore::Modif modif(polymer_sp->getPolChemDefCstSPtr(), "NOT_SET");

  if(mp_ui->defineModifGroupBox->isChecked())
    {
      // The user wants to use a self-defined modification.

      if(!parseModifDefinition(modif))
        {
          return;
        }
    }
  else
    {
      // The user should have selected one item from the list of
      // available modifications. Let's get to the modification in
      // question.

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

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

      QString name = selectedList.at(0)->text();
      if(name.isEmpty())
        qFatal() << "Programming error. The Modif name cannot be empty.";

      // Find the proper modification in the list of modifs in the
      // polymer chemistry definition and update all the data from the
      // reference one in the polymer chemistry definition.
      libXpertMassCore::ModifCstSPtr modif_csp =
        polymer_sp->getPolChemDefCstSPtr()->getModifCstSPtrByName(name);

      if(modif_csp == nullptr)
        {
          QMessageBox::warning(this,
                               tr("MassXpert3 - Polymer modification"),
                               tr("Failed to find modification (%1) in the "
                                  "polymer chemistry definition.")
                                 .arg(name),
                               QMessageBox::Ok);

          return;
        }

      modif = *modif_csp;
    }

  // At this point, whatever the way the modification was created, we
  // have it full and working. Use it to perform the modification
  // according to the user's requests.

  if(mp_ui->leftEndCheckBox->checkState() == Qt::Checked)
    {
      polymer_sp->setLeftEndModif(modif);
      mp_editorWnd->setWindowModified(true);
    }

  if(mp_ui->rightEndCheckBox->checkState() == Qt::Checked)
    {
      polymer_sp->setRightEndModif(modif);
      mp_editorWnd->setWindowModified(true);
    }

  updateModificationLineEdits();

  mp_editorWnd->updatePolymerEndsModifs();

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

  return;
}


void
PolymerModificationDlg::unmodify()
{
  libXpertMassCore::PolymerQSPtr polymer_sp = mp_editorWnd->getPolymerSPtr();

  if(mp_ui->leftEndCheckBox->checkState() == Qt::Checked)
    polymer_sp->unmodify(libXpertMassCore::Enums::PolymerEnd::LEFT);

  if(mp_ui->rightEndCheckBox->checkState() == Qt::Checked)
    polymer_sp->unmodify(libXpertMassCore::Enums::PolymerEnd::RIGHT);

  mp_editorWnd->setWindowModified(true);

  updateModificationLineEdits();

  // Also update the labels of the buttons in the sequence editor
  // window.
  mp_editorWnd->updatePolymerEndsModifs();

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

} // namespace MassXpert

} // namespace MsXpS
