/**
 * \file pappsomspp/processing/specglob/peptidemodel.h
 * \date 14/11/2023
 * \author Olivier Langella
 * \brief SpecGlobTool peptide model
 * \todo https://www.psidev.info/proforma
 *
 * C++ implementation of the SpecGlob algorithm described in :
 * 1. Prunier, G. et al. Fast alignment of mass spectra in large proteomics
 * datasets, capturing dissimilarities arising from multiple complex
 * modifications of peptides. BMC Bioinformatics 24, 421 (2023).
 *
 *     HAL Id : hal-04296170 , version 1
 *     Mot de passe : hxo20cl
 *     DOI : 10.1186/s12859-023-05555-y
 */


/*
 * SpecGlobTool, Spectra to peptide alignment tool
 * Copyright (C) 2023  Olivier Langella
 * <olivier.langella@universite-paris-saclay.fr>
 *
 * This program is free software: you can redistribute ipetide to spectrum
 * alignmentt 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/>.
 *
 */

#pragma once

#include "../../peptide/peptide.h"
#include "../../massspectrum/qualifiedmassspectrum.h"
#include "types.h"
#include "../../exportinmportconfig.h"

namespace pappso
{
namespace specglob
{

struct AminoAcidModel
{
  pappso::Aa amino_acid;
  SpectralAlignmentType alignment_type;
  bool bracket;
  double mass_difference;
  bool remove = false;
};


struct TheoreticalPeakDataPoint
{
  pappso::MzRange mz_range;
  std::vector<pappso::DataPoint>::const_iterator
    it_experimental_peak_match; // intensity of experimental matched peak
  std::size_t aa_indice; // the amino acid indice in the peptide model (from 0
                         // to size, begining by nter)
};
/**
 * @todo write docs
 */
class PMSPP_LIB_DECL PeptideModel : public std::vector<AminoAcidModel>
{
  public:
  /**
   * Default constructor
   */
  PeptideModel(const pappso::QualifiedMassSpectrum &qmass_spectrum,
               const pappso::Peptide &peptide);

  PeptideModel();
  /**
   * Copy constructor
   *
   * @param other TODO
   */
  PeptideModel(const PeptideModel &other);

  /**
   * Destructor
   */
  virtual ~PeptideModel();

  QString toString() const;

  // void setPrecursorMass(double precursor_mass);

  double getMass() const;

  /** @brief get the m/z peptide model mass
   * */
  pappso_double getMz(unsigned int charge) const;

  /** @brief mass delta between experimental and theoretical mass
   */
  double getMassDelta() const;

  std::size_t modifCount() const;

  void setBeginMassDelta(double);

  void matchExperimentalPeaks(pappso::PrecisionPtr precision);

  std::vector<double> getTheoreticalIonMassList() const;


  std::size_t getCountSharedPeaks() const;
  double getIntensitySharedPeaks() const;
  double getIntensityExperimentalPeaks() const;

  void assignResidualMass2Cter();

  /**
   * Use to delete offset when there is two opposite mass in the hit modified
   * example : [150.2]SDS[-150.2]KR --> SDSKR
   *
   * @param precision : The mass spectrometer precision
   * @return true if model is modified
   */
  bool eliminateComplementaryDelta(pappso::PrecisionPtr precision);

  /** @brief try to replace mass differences with corresponding mutations mass
   * delta
   *
   * amino acid list is tested as raw (no modification)
   *
   * @param aa_list amno acid list to test (beware of cysteine +
   * carbamidomethyl)
   * @param precision : The mass spectrometer precision
   * @return true if model is modified
   */
  bool checkForMutations(const std::vector<AminoAcidChar> &aa_list,
                         pappso::PrecisionPtr precision);


  /** @brief try to replace mass differences with corresponding mutation mass
   * delta
   *
   * amino acid candidate tested could bear some modifications
   *
   * @param amino_acid_candidate : The amino acid candidate to check
   * @param precision : The mass spectrometer precision
   * @return true if model is modified
   */
  bool checkForMutation(const pappso::Aa &amino_acid_candidate,
                        pappso::PrecisionPtr precision);


  /** @brief try to replace mass differences with the given modifications on a
   * specific amino acid
   *
   * @param aa_modified the modified amino acid
   * @param precision The mass spectrometer precision
   * @return true if model is modified
   */
  bool checkForAaModification(const pappso::Aa &aa_modified,
                              pappso::PrecisionPtr precision);

  /** @brief try to replace mass differences with the given modification
   *
   * @param modification : The proposed modification pointer
   * @param precision : The mass spectrometer precision
   * @return true if model is modified
   */
  bool checkForAaModificationP(pappso::AaModificationP modification,
                               pappso::PrecisionPtr precision);

  PeptideModel &operator=(const PeptideModel &other);

  PeptideModel &copyDeep(const PeptideModel &other);
  /**
   * Use to try to explain negative offset with deletion of amino acids
   *
   * @param precision : The mass spectrometer precision
   * @return true if model is modified
   */
  bool eliminateNegativeOffset(pappso::PrecisionPtr precision);


  /**
   * Use to remove brackets around not found Amino acids when the B or Y peak
   * corresponding to the amino acid in the sequence is in the experimental peak
   * list
   *
   * @param betterModified       : The better sequence of hit Modified
   * @param experimentalMassList : The list of mass that are in the experimental
   *                             spectrum
   * @param precision            : The precision of the mass spectrometer
   * @return the new sequence without brackets around found amino acids after
   * all post treatments
   */
  bool removeBracketsForAlignedAA();

  const std::vector<TheoreticalPeakDataPoint> &
  getTheoreticalPeakDataPointList() const;

  QString getTheoreticalPeakDataPointListToString() const;

  /** @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;

  bool hasRemoval() const;

  /** @brief try to remove left amino acid if there is a removal
   * @return true if peptide model is modified
   */
  bool ltrimOnRemoval();

  private:
  void generateTheoreticalPeaks(pappso::PrecisionPtr precision);

  private:
  std::vector<TheoreticalPeakDataPoint> m_theoreticalPeakList;

  double m_experimentalPrecursorMass;
  double m_beginMassDelta                   = 0;
  pappso::MassSpectrumCstSPtr mcsp_peakList = nullptr;

  pappso::AaModificationP m_nterModification;
  pappso::AaModificationP m_cterModification;

  std::size_t m_countSharedPeaks      = 0;
  double m_intensitySharedPeaks       = 0;
  double m_intensityExperimentalPeaks = 0;
};
} // namespace specglob
} // namespace pappso
