/**
 * \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 "pappsomspp/core/types.h"
#include "peptideinterface.h"
#include <cstdint>
#include "pappsomspp/export-import-config.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
 */
PMSPP_LIB_DECL bool peptideIonIsNter(Enums::PeptideIon ion_type);

/** \brief tells if an ion type is the complement ion of the other
 * \param ion_type_ref the ion type reference
 * \param ion_type the ion to test
 */
PMSPP_LIB_DECL bool peptideIonTypeIsComplement(Enums::PeptideIon ion_type_ref,
                                               Enums::PeptideIon ion_type);


/** \brief get the direction of a peptide ion
 * \param ion_type the ion to test
 * \return the peptide direction
 */
PMSPP_LIB_DECL PeptideDirection getPeptideIonDirection(Enums::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
{
  friend class PeptideProFormaParser;
  friend bool operator<(const Peptide &l, const Peptide &r);
  friend bool operator==(const Peptide &l, const Peptide &r);


  public:
  Peptide(const QString &pepstr);
  virtual ~Peptide();
  Peptide(const Peptide &peptide);

  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);

  /** @brief adds a modification to all amino acid of the sequence
   * @param aaModification pointer on modification to add
   * @param Enums::AminoAcidChar amino_acid to apply the modification
   * */
  void addAaModificationOnAllAminoAcid(AaModificationP aaModification,
                                       Enums::AminoAcidChar amino_acid);

  std::vector<Aa>::iterator begin();

  std::vector<Aa>::iterator end();

  std::vector<Aa>::const_iterator begin() const;

  std::vector<Aa>::const_iterator end() const;

  std::vector<Aa>::const_reverse_iterator rbegin() const;

  std::vector<Aa>::const_reverse_iterator rend() const;

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


  /** @brief get raw mass array of the amino acid sequence
   */
  std::vector<double> getRawMassArray() const;

  /** @brief get complete serie of product masses for a given charge and ion type
   */
  std::vector<double> getMassIonSerie(unsigned int charge,
                                      pappso::Enums::PeptideIon ion_type) const;

  pappso_double getMass();
  pappso_double getMass() const override;

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

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

  /** @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 replaces all occurences of a modification by a new one
   * on a specific amino acid
   * @param aa the amino acid to modify
   * @param oldmod modification to change
   * @param newmod new modification
   */
  void replaceAaModificationOnAminoAcid(Enums::AminoAcidChar aa,
                                        pappso::AaModificationP oldmod,
                                        pappso::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;

  void setCleavageNterModification(AaModificationP mod);
  void setCleavageCterModification(AaModificationP mod);
  AaModificationP getCleavageNterModification() const;
  AaModificationP getCleavageCterModification() const;
  void setNterModification(AaModificationP mod);
  void setCterModification(AaModificationP mod);
  AaModificationP getNterModification() const;
  AaModificationP getCterModification() const;


  /** @brief apply 100% isotope replacement
   * @todo
   */
  void setGlobalModification(Enums::Isotope isotope_kind);


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


  /** @brief get the peptide model in ProForma notation
   * https://github.com/HUPO-PSI/ProForma/blob/master/README.md
   * @return QString as described in ProForma
   */
  QString toProForma() const;


  virtual const ChemicalFormula getChemicalFormula() const override;
  virtual const ChemicalFormula getChemicalFormulaCharge(unsigned int charge) const override;

  protected:
  std::vector<Aa> m_aaVec;
  bool m_fullC13     = false;
  bool m_fullN15     = false;
  bool m_fullH2      = false;
  double m_proxyMass = -1;
  // moiety added to peptide Nter on cleavage
  AaModificationP m_cleavageNterMod = nullptr;
  // moiety added to peptide Cter on cleavage
  AaModificationP m_cleavageCterMod = nullptr;
  AaModificationP m_NterMod         = nullptr;
  AaModificationP m_CterMod         = nullptr;
};

} // namespace pappso
