/**
 * \file pappsomspp/peptide/peptide.h
 * \date 7/3/2015
 * \author Olivier Langella
 * \brief peptide model
 */

/*******************************************************************************
 * Copyright (c) 2015 Olivier Langella <Olivier.Langella@moulon.inra.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@moulon.inra.fr> - initial API and
 *implementation
 ******************************************************************************/

#pragma once


#include <vector>
#include <memory>
#include <QString>
#include "../amino_acid/aa.h"
#include "peptideinterface.h"
#include <cstdint>
#include "../exportinmportconfig.h"

namespace pappso
{

enum class PeptideDirection : std::int8_t
{
  Nter = 0,
  Cter = 1
};


/** \brief tells if an ion is Nter
 * \param ion_type the ion to test
 */
bool peptideIonIsNter(PeptideIon ion_type);


/** \brief get the direction of a peptide ion
 * \param ion_type the ion to test
 * \return the peptide direction
 */
PeptideDirection getPeptideIonDirection(PeptideIon ion_type);

enum class PeptideIonNter
{
  b,
  bstar,
  bo,
  a,
  astar,
  ao,
  bp,
  c
};


enum class PeptideIonCter
{
  y,
  ystar,
  yo,
  z,
  yp,
  x
};

class Peptide;

typedef std::shared_ptr<const Peptide> PeptideSp;
typedef std::shared_ptr<Peptide> NoConstPeptideSp;

class PMSPP_LIB_DECL Peptide : public PeptideInterface
{
  protected:
  std::vector<Aa> m_aaVec;
  pappso_double m_proxyMass = -1;

  public:
  Peptide(const QString &pepstr);
  virtual ~Peptide();
  Peptide(const Peptide &peptide);
  friend bool
  operator<(const Peptide &l, const Peptide &r)
  {
    return (l.m_aaVec < r.m_aaVec);
  }
  friend bool
  operator==(const Peptide &l, const Peptide &r)
  {
    return (l.m_aaVec == r.m_aaVec);
  }


  Peptide(Peptide &&toCopy);

  PeptideSp makePeptideSp() const;
  NoConstPeptideSp makeNoConstPeptideSp() const;

  /** @brief adds a modification to amino acid sequence
   * @param aaModification pointer on modification to add
   * @param position position in the amino acid sequence (starts at 0)
   * */
  void addAaModification(AaModificationP aaModification, unsigned int position);

  std::vector<Aa>::iterator
  begin()
  {
    return m_aaVec.begin();
  }

  std::vector<Aa>::iterator
  end()
  {
    return m_aaVec.end();
  }

  std::vector<Aa>::const_iterator
  begin() const
  {
    return m_aaVec.begin();
  }

  std::vector<Aa>::const_iterator
  end() const
  {
    return m_aaVec.end();
  }

  std::vector<Aa>::const_reverse_iterator
  rbegin() const
  {
    return m_aaVec.rbegin();
  }

  std::vector<Aa>::const_reverse_iterator
  rend() const
  {
    return m_aaVec.rend();
  }

  Aa &getAa(unsigned int position);
  const Aa &getConstAa(unsigned int position) const;


  pappso_double getMass();
  pappso_double
  getMass() const override
  {
    return m_proxyMass;
  };

  virtual int getNumberOfAtom(AtomIsotopeSurvey atom) const override;
  virtual int getNumberOfIsotope(Isotope isotope) const override;

  /** \brief print amino acid sequence without modifications */
  const QString getSequence() const override;
  unsigned int
  size() const override
  {
    return m_aaVec.size();
  };

  /** @brief count modification occurence
   * @param mod modification to look for
   * @result number of occurences
   */
  unsigned int getNumberOfModification(AaModificationP mod) const;

  /** @brief count modification occurence
   * @param mod modification to look for
   * @param aa_list amino acid list targets (one letter code)
   * @result number of occurences
   */
  unsigned int countModificationOnAa(AaModificationP mod,
                                     const std::vector<char> &aa_list) const;

  /** @brief replaces all occurences of a modification by a new one
   * @param oldmod modification to change
   * @param newmod new modification
   */
  void replaceAaModification(AaModificationP oldmod, AaModificationP newmod);

  /** @brief removes all occurences of a modification
   * @param mod modification to remove
   */
  void removeAaModification(AaModificationP mod);

  /** @brief get modification positions
   * @param mod modification to look for
   * @result vector containing positions (from 0 to size-1)
   */
  std::vector<unsigned int>
  getModificationPositionList(AaModificationP mod) const;

  /** @brief get modification positions
   * @param mod modification to look for
   * @param aa_list amino acid list targets (one letter code)
   * @result vector containing positions (from 0 to size-1)
   */
  std::vector<unsigned int>
  getModificationPositionList(AaModificationP mod,
                              const std::vector<char> &aa_list) const;

  /** @brief get positions of one amino acid in peptide
   * @param aa the one letter code of the amino acid
   * @result vector containing positions (from 0 to size-1) */
  std::vector<unsigned int> getAaPositionList(char aa) const;
  std::vector<unsigned int> getAaPositionList(std::list<char> list_aa) const;

  /** \brief print modification except internal modifications */
  const QString toString() const;
  /** \brief print all modifications */
  const QString toAbsoluteString() const;
  /** \brief get all sequence string with modifications and converting Leucine
   * to Isoleucine */
  const QString getLiAbsoluteString() const;

  AaModificationP getInternalNterModification() const;
  AaModificationP getInternalCterModification() const;
  void removeInternalNterModification();
  void removeInternalCterModification();

  void setInternalNterModification(AaModificationP mod);
  void setInternalCterModification(AaModificationP mod);


  void rotate();
  void reverse();
  /** @brief tells if the peptide sequence is a palindrome
   */
  virtual bool isPalindrome() const override;
  void replaceLeucineIsoleucine();
  void removeNterAminoAcid();
  void removeCterAminoAcid();
};


} // namespace pappso
