/* massXpert - the true massist's program.
   --------------------------------------
   Copyright(C) 2006,2007 Filippo Rusconi

   http://www.massxpert.org/massXpert

   This file is part of the massXpert project.

   The massxpert project is the successor to the "GNU polyxmass"
   project that is an official GNU project package(see
   www.gnu.org). The massXpert project is not endorsed by the GNU
   project, although it is released ---in its entirety--- under the
   GNU General Public License. A huge part of the code in massXpert
   is actually a C++ rewrite of code in GNU polyxmass. As such
   massXpert was started at the Centre National de la Recherche
   Scientifique(FRANCE), that granted me the formal authorization to
   publish it under this Free Software License.

   This software is free software; you can redistribute it and/or
   modify it under the terms of the GNU  General Public
   License version 3, as published by the Free Software Foundation.
   

   This software 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 software; if not, write to the

   Free Software Foundation, Inc.,

   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/


/////////////////////// Local includes
#include "monomerCodeEvaluator.hpp"
#include "polChemDef.hpp"
#include "monomer.hpp"
#include "polymer.hpp"


namespace massXpert
{

  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 massXpert
