/* 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 "CleaveMotif.hpp"
#include "Polymer.hpp"


namespace msxps
{

	namespace massxpert
	{



//! Constructs a cleavage motif.
/*!

  \param polChemDef Polymer chemistry definition. Cannot be 0.

  \param name Name. Cannot be empty.

  \param str Motif in the form of "Lys/;Arg/;-Lys/Pro".

  \param offset Offset of the cleavage.

  \param isForCleave Tells if motif is for cleavage.
*/
CleaveMotif::CleaveMotif(const PolChemDef *polChemDef,
                         QString name,
                         const QString &str,
                         int offset,
                         bool isForCleave)
  : PolChemDefEntity(polChemDef, name),
    m_motif(str),
    m_offset(offset),
    m_isForCleave(isForCleave)
{
}


//! Constructs a copy of \p other.
/*!

  \param other cleavage motif to be used as a mold.
*/
CleaveMotif::CleaveMotif(const CleaveMotif &other)
  : PolChemDefEntity(other),
    m_motif(other.m_motif),
    m_codeList(other.m_codeList),
    m_offset(other.m_offset),
    m_isForCleave(other.m_isForCleave)
{
}

//! Destroys the cleavage motif.
CleaveMotif::~CleaveMotif()
{
}


//! Modifies \p other to be identical to \p this.
/*!  \param other cleavage motif.
 */
void
CleaveMotif::clone(CleaveMotif *other) const
{
  Q_ASSERT(other);

  PolChemDefEntity::clone(other);

  other->m_motif = m_motif;

  other->m_codeList.clear();
  other->m_codeList = m_codeList;

  other->m_offset      = m_offset;
  other->m_isForCleave = m_isForCleave;
}


//! Modifies \p this  to be identical to \p other.
/*!  \param other cleavage motif to be used as a mold.
 */
void
CleaveMotif::mold(const CleaveMotif &other)
{
  if(&other == this)
    return;

  PolChemDefEntity::mold(other);

  m_motif       = other.m_motif;
  m_codeList    = other.m_codeList;
  m_offset      = other.m_offset;
  m_isForCleave = other.m_isForCleave;
}


//! Assigns other to \p this cleavage motif.
/*! \param other cleavage motif used as the mold to set values to \p
  this instance.

  \return a reference to \p this cleavage motif.
*/
CleaveMotif &
CleaveMotif::operator=(const CleaveMotif &other)
{
  if(&other != this)
    mold(other);

  return *this;
}


//! Sets the motif.
/*!  \param str new motif.
 */
void
CleaveMotif::setMotif(const QString &str)
{
  m_motif = str;
}


//! Returns the motif.
/*!
  \return the motif.
*/
const QString &
CleaveMotif::motif()
{
  return m_motif;
}


//! Returns the string list of codes in the motif.
/*!
  \return the string list of codes in the motif.
*/
const QStringList &
CleaveMotif::codeList() const
{
  return m_codeList;
}


//! Sets the offset.
/*!  \param value new offset.
 */
void
CleaveMotif::setOffset(int value)
{
  m_offset = value;
}


//! Returns the offset.
/*!
  \return the offset.
*/
int
CleaveMotif::offset()
{
  return m_offset;
}


//! Sets if motif is for cleavage.
/*!  \param value if motif is for cleavage.
 */
void
CleaveMotif::setForCleave(bool value)
{
  m_isForCleave = value;
}


//! Returns if motif is for cleavage.
/*!  \return if motif is for cleavage.
 */
bool
CleaveMotif::isForCleave()
{
  return m_isForCleave;
}


//! Parses a cleavage site.
/*! Upon parsing of a cleavage site, this cleavage motif is filled
  with data.

  \param site Cleavage site in the form "Lys/Pro" or "KKGK/RRGK".

  \return the number of monomer codes in the motif, -1 upon error.
*/
int
CleaveMotif::parse(const QString &site)
{
  const QList<Monomer *> &refList = mp_polChemDef->monomerList();

  QString code;
  QString error;
  QString local = site;

  int index = 0;
  int count = 0;

  // We get something like "Lys/Pro" or something like "KKGK/RRGK" and
  // we have to make three things:
  //
  // 1. change the site "KKGK/RRGK" to a motif string(KKGKRRGK).
  //
  // 2. set the offset member to the index of '/' in the initial site
  // string.
  //
  // 3. make an array of codes with the motif.

  Sequence sequence(local);

  while(1)
    {
      code.clear();

      if(sequence.nextCode(
           &code, &index, &error, mp_polChemDef->codeLength()) == -1)
        {
          if(error == "/")
            {
              m_offset = count;

              // qDebug() << __FILE__ << __LINE__
              // << "Found / at code position" << m_offset;

              // Increment index so that we iterate in the next code
              // at next round.
              ++index;

              continue;
            }

          // There was an error parsing the site sequence. If the err
          // string contains a space, that is not serious, we could
          // skip that.

          qDebug() << __FILE__ << __LINE__ << "Failed to parse cleavage site"
                   << site;

          return -1;
        }

      // There might have been no error, but that does not means that
      // we necessarily got a code.
      if(code.isEmpty())
        {
          // We arrived at the end of a code parsing step(either
          // because the current 'local' cleavage site(that is
          // 'sequence') was finished parsing or because we finished
          // parsing one of its monomer codes.
          break;
        }

      // At this point we actually had a code.

      //       qDebug() << __FILE__ << __LINE__
      // 		<< "Got next code:" << code.toAscii();

      // At this point, 'code' contains something that looks like a
      // valid code, but we still have to make sure that this code
      // actually is in our list of monomer codes...

      if(Monomer::isCodeInList(code, refList) == -1)
        {
          qDebug() << __FILE__ << __LINE__ << "Monomer code" << code
                   << " is not in the monomer list.";

          return -1;
        }

      // At the end m_motif will contain KKGKRRGK, while site was
      // "KKGK/RRGK" KKGKRRGK
      m_motif += code;

      // Add the newly parsed code to the code list.
      m_codeList << code;

      ++count;

      // Increment index so that we iterate in the next code at next
      // round.
      ++index;
    }

  // Return the number of successfully parsed monomer codes for the
  // 'site' string.
  Q_ASSERT(count == m_codeList.size());

  return count;
}


bool
CleaveMotif::validate()
{
  Q_ASSERT(0);
  return true;
}

} // namespace massxpert

} // namespace msxps
