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


/////////////////////// Local includes
#include "MonomerCodeEvaluator.hpp"
#include <MsXpS/libXpertMassCore/PolChemDef.hpp>
#include <MsXpS/libXpertMassCore/Monomer.hpp>
#include <MsXpS/libXpertMassCore/Polymer.hpp>


namespace MsXpS
{

namespace MassXpert
{


MonomerCodeEvaluator::MonomerCodeEvaluator(
  libXpertMassCore::PolymerQSPtr polymer_sp,
  SequenceEditorWnd *seq_editor_wnd_p,
  QLineEdit *elab,
  QLineEdit *error)
  : msp_polymer(polymer_sp),
    mp_editorWnd(seq_editor_wnd_p),
    m_elabCodeLineEdit(elab),
    m_errorCodeLineEdit(error)
{
}


MonomerCodeEvaluator::~MonomerCodeEvaluator()
{
}


void
MonomerCodeEvaluator::setSequenceEditorWnd(SequenceEditorWnd *wnd)
{
  Q_ASSERT(wnd);

  mp_editorWnd = wnd;
}


void
MonomerCodeEvaluator::setEdits(QLineEdit *elab, QLineEdit *error)
{
  Q_ASSERT(elab && error);

  m_elabCodeLineEdit  = elab;
  m_errorCodeLineEdit = error;
}


bool
MonomerCodeEvaluator::evaluateCode(const QString &code)
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    msp_polymer->getPolChemDefCstSPtr();

  //  We just want to make sure that the code is known to the polymer chemistry
  //  definition.
  libXpertMassCore::MonomerCstSPtr monomer_csp =
    pol_chem_def_csp->getMonomerCstSPtrByCode(code);

  if(monomer_csp == nullptr)
    qFatal() << "Programming error. Monomer code is not known.";

  mp_editorWnd->mpa_editorGraphicsView->removeSelectedOligomer();

  //  The monomer will be allocated by libXpertMassCore::Sequence anew as a
  //  MonomerSPtr on the basis of the reference to the Monomer.
  mp_editorWnd->mpa_editorGraphicsView->insertMonomerAtPoint(*monomer_csp);

  mp_editorWnd->clearCompletionsListSelection();

  return true;
}


void
MonomerCodeEvaluator::escapeKey()
{
  if(!m_elabCode.isEmpty())
    {
      m_elabCode.remove(m_elabCode.size() - 1, 1);
      m_elabCodeLineEdit->setText(m_elabCode);
    }

  m_errorCodeLineEdit->clear();
}


bool
MonomerCodeEvaluator::reportCompletions()
{
  if(m_elabCode.isEmpty())
    {
      mp_editorWnd->completionsListSelectAt(-1);
      return true;
    }

  mp_editorWnd->clearCompletionsListSelection();

  int ret = autoComplete(m_elabCode);

  if(!ret)
    return true;

  for(int index : m_completionsList)
    mp_editorWnd->completionsListSelectAt(index);


  return true;
}


bool
MonomerCodeEvaluator::newKey(QString &key)
{
  QChar::Category category = key.at(0).category();

  if(category == QChar::Letter_Uppercase)
    upperCaseChar(key.at(0));
  else if(category == QChar::Letter_Lowercase)
    lowerCaseChar(key.at(0));
  else
    return false;

  return true;
}


bool
MonomerCodeEvaluator::upperCaseChar(QChar qchar)
{
  //  An uppercase character was entered. By definition,  that means that
  //  a new monomer code is being started. If there is the elaborating code
  //  is not empty,  then this is an error.

  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    msp_polymer->getPolChemDefCstSPtr();

  const std::vector<libXpertMassCore::MonomerSPtr> &reference_monomers =
    pol_chem_def_csp->getMonomersCstRef();

  if(!m_elabCode.isEmpty())
    {
      QString msg = QString("Bad char: %1.").arg(qchar);
      reportError(msg);

      return false;
    }

  m_elabCode += qchar;

  int autoCompletions = autoComplete(m_elabCode);

  if(!autoCompletions)
    {
      QString msg = QString(tr("Bad char: %1.")).arg(qchar);
      reportError(msg);

      m_elabCode.clear();

      return false;
    }
  else if(autoCompletions == 1)
    {
      int index                            = m_completionsList.at(0);
      libXpertMassCore::MonomerSPtr monomer_sp = reference_monomers.at(index);
      QString code                         = monomer_sp->getCode();

      // The code was from completion monomer_sps, so they come right
      // from the polymer chemistry defintion: impossible that
      // this code does not evaluate.
      bool ret = false;
      ret      = evaluateCode(code);
      if(ret == 0)
        qFatal() << "Programming error.";

      m_evalCode.clear();
      m_elabCode.clear();

      m_elabCodeLineEdit->setText("");
      m_errorCodeLineEdit->setText("");
    }
  else if(autoCompletions > 1)
    {
      if(m_elabCode.size() >= pol_chem_def_csp->getCodeLength())
        {
          QString msg = QString(tr("Bad char: %1.")).arg(qchar);
          reportError(msg);

          m_elabCode.remove(m_elabCode.size() - 1, 1);

          debugCompletionsPutStdErr();

          return false;
        }

      m_elabCodeLineEdit->setText(m_elabCode);
      // If there was a bad character in the error line edit widget,
      // clear it.
      m_errorCodeLineEdit->setText("");
    }

  return true;
}


bool
MonomerCodeEvaluator::lowerCaseChar(QChar qchar)
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    msp_polymer->getPolChemDefCstSPtr();

  const std::vector<libXpertMassCore::MonomerSPtr> &reference_monomers =
    pol_chem_def_csp->getMonomersCstRef();

  // There should be something in m_elabCode.

  if(m_elabCode.isEmpty())
    {
      QString msg = QString(tr("Bad char: %1.")).arg(qchar);
      reportError(msg);

      return false;
    }

  m_elabCode += qchar;

  int autoCompletions = autoComplete(m_elabCode);

  if(!autoCompletions)
    {
      QString msg = QString(tr("Bad char: %1.")).arg(qchar);
      reportError(msg);

      m_elabCode.remove(m_elabCode.size() - 1, 1);

      return false;
    }
  else if(autoCompletions == 1)
    {
      int index                            = m_completionsList.at(0);
      libXpertMassCore::MonomerSPtr monomer_sp = reference_monomers.at(index);
      QString code                         = monomer_sp->getCode();

      // The code was from completion monomers, so they come right
      // from the polymer chemistry defintion: impossible that
      // this code does not evaluate.

      bool ret = false;
      ret      = evaluateCode(code);
      if(ret == 0)
        qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

      m_evalCode.clear();
      m_elabCode.clear();

      m_elabCodeLineEdit->setText("");
      m_errorCodeLineEdit->setText("");
    }
  else if(autoCompletions > 1)
    {
      if(m_elabCode.size() >= pol_chem_def_csp->getCodeLength())
        {
          QString msg = QString(tr("Bad char: %1.")).arg(qchar);
          reportError(msg);

          m_elabCode.remove(m_elabCode.size() - 1, 1);

          debugCompletionsPutStdErr();

          return false;
        }

      m_elabCodeLineEdit->setText(m_elabCode);
    }

  return true;
}


int
MonomerCodeEvaluator::autoComplete(QString &code)
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    msp_polymer->getPolChemDefCstSPtr();

  const std::vector<libXpertMassCore::MonomerSPtr> &reference_monomers =
    pol_chem_def_csp->getMonomersCstRef();

  m_completionsList.clear();

  for(std::size_t iter = 0; iter < reference_monomers.size(); ++iter)
    {
      libXpertMassCore::MonomerSPtr monomer_sp = reference_monomers.at(iter);

      if(monomer_sp->getCode().startsWith(code, Qt::CaseSensitive))
        m_completionsList.push_back(iter);
    }

  return m_completionsList.size();
}


void
MonomerCodeEvaluator::reportError(QString &msg)
{
  m_errorCodeLineEdit->setText(msg);
}


void
MonomerCodeEvaluator::debugCompletionsPutStdErr()
{
  libXpertMassCore::PolChemDefCstSPtr pol_chem_def_csp =
    msp_polymer->getPolChemDefCstSPtr();

  const std::vector<libXpertMassCore::MonomerSPtr> &reference_monomers =
    pol_chem_def_csp->getMonomersCstRef();

  for(int index : m_completionsList)
    libXpertMassCore::MonomerSPtr monomer_sp = reference_monomers.at(index);
}

} // namespace MassXpert

} // namespace MsXpS
