/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 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 <massxpert/gui/MonomerCodeEvaluator.hpp>
#include <massxpert/nongui/PolChemDef.hpp>
#include <massxpert/nongui/Monomer.hpp>
#include <massxpert/nongui/Polymer.hpp>


namespace msXpSmassXpert
{

MonomerCodeEvaluator::MonomerCodeEvaluator(Polymer *polymer,
                                           SequenceEditorWnd *wnd,
                                           QLineEdit *elab,
                                           QLineEdit *error)
  : mp_polymer(polymer),
    mp_editorWnd(wnd),
    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)
{
  const PolChemDef *polChemDef   = mp_polymer->polChemDef();
  const QList<Monomer *> refList = polChemDef->monomerList();

  Monomer *monomer = new Monomer(polChemDef, "NOT_SET");

  int ret = -1;
  ret     = Monomer::isCodeInList(code, refList, monomer);
  if(ret == -1)
    qFatal("Fatal error at %s@%d. Aborting.", __FILE__, __LINE__);

  mp_editorWnd->mpa_editorGraphicsView->removeSelectedOligomer();

  mp_editorWnd->mpa_editorGraphicsView->insertMonomerAtPoint(monomer);

  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 iter = 0; iter < m_completionsList.size(); ++iter)
    {
      int index = m_completionsList.at(iter);

      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)
{
  const PolChemDef *polChemDef = mp_polymer->polChemDef();
  QList<Monomer *> refList     = polChemDef->monomerList();

  // Start of a new code. There should be nothing in m_elabCode.

  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);
      Monomer *monomer = refList.at(index);
      QString code     = monomer->code();

      // 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() >= polChemDef->codeLength())
        {
          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)
{
  const PolChemDef *polChemDef = mp_polymer->polChemDef();
  QList<Monomer *> refList     = polChemDef->monomerList();

  // 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);
      Monomer *monomer = refList.at(index);
      QString code     = monomer->code();

      // 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() >= polChemDef->codeLength())
        {
          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)
{
  const PolChemDef *polChemDef = mp_polymer->polChemDef();
  QList<Monomer *> refList     = polChemDef->monomerList();

  m_completionsList.clear();

  for(int iter = 0; iter < refList.size(); ++iter)
    {
      Monomer *monomer = refList.at(iter);

      if(monomer->code().startsWith(code, Qt::CaseSensitive))
        m_completionsList.append(iter);
    }

  return m_completionsList.size();
}


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


void
MonomerCodeEvaluator::debugCompletionsPutStdErr()
{
  const PolChemDef *polChemDef = mp_polymer->polChemDef();
  QList<Monomer *> refList     = polChemDef->monomerList();

  for(int iter = 0; iter < m_completionsList.size(); ++iter)
    {
      int index = m_completionsList.at(iter);

      Monomer *monomer = refList.at(index);
      qDebug() << "Completion:" << monomer->code();
    }
}

} // namespace msXpSmassXpert
