/**
 * \file core/spoms/transformedspectrum.h
 * \date 11/02/2026
 * \author Aurélien Berthier, BENOIST Emile, TESSIER Dominique
 * \brief handle processed spectrum
 */


/*
 * PeptidOMS, Spectra to protein alignment tool
 * Copyright (C) 2026  Olivier Langella
 * <olivier.langella@cnrs.fr>
 *
 * 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/>.
 *
 */

#pragma once
#include "complementarypeak.h"
#include "aaposition.h"
#include <pappsomspp/core/amino_acid/aacode.h>
#include <pappsomspp/core/massspectrum/qualifiedmassspectrum.h>
#include <vector>
#include "scenario.h"
#include "locationbackup.h"

namespace pappso
{
namespace spoms
{
class Protein;
class Alignment;
class AlignmentBucket;
/**
 * This class represent a spectrum after its pre-treatment.
 * Some of the attributes of a TransformedSpectrum can be stored in static
 * structure because they are treated sequentially.
 * @author 	BENOIST Emile, TESSIER Dominique
 *
 */

class TransformedSpectrum
{


  public:
  static void reset();


  public:
  /**
   * The first constructor that build the first transformed version of a native
   * spectrum whose the attributes are in parameter. It correspond to the
   * pre-treatment of a spectrum
   * @param peaks: the peaks list of the native spectrum
   * @param precursor_mass_charged: the precursor mass/charge ratio
   * @param id: the native spectrum id
   * @param charge: the charge of the precursor
   */

  TransformedSpectrum(const pappso::AaCode &aaCode,
                      QualifiedMassSpectrumCstSPtr qmassspectrum_sp);

  /**
   * The second constructor that only initializes the size of certain
   * structures. The spectrum obtained will then be used to contain a
   * TransformedSpectrum with some modification (possibly ignored peaks and
   * possible non-aligned mass).
   */
  TransformedSpectrum(const pappso::AaCode &aaCode,
                      QualifiedMassSpectrumCstSPtr qmassspectrum_sp,
                      const std::vector<int> &peak_mask,
                      double non_aligned_mass);

  virtual ~TransformedSpectrum();


  /**
   * Return if the spectrum respects or not the user parameters (minimum number
   * of peaks/amino acids). If it's not the case, it return false, otherwise
   * true
   * @return true if the spectrum respects the user defined parameters,
   * otherwise false
   */
  bool isUsefull() const;

  /**
   * Return the number of column needed in the matrix for the alignment of the
   * spectrum
   * @return the number of column
   */

  int getColumnCount() const;

  // Beaucoup appelée !
  /**
   * Return if the column in parameter correspond to a double peak or not
   * @param column: the number of the column
   * @return true if the column corresponds to a double peak, otherwise false
   */

  inline bool
  isDoubleColumn(int column) const
  {
    return m_doubleColumn[column];
  }

  /**
   * Return the complete list of amino acid positions in the spectrum. This is a
   * list of list whose the first dimension correspond to the number of an amino
   * acid and the second dimension correspond the the different position of an
   * amino acid in the spectrum. Each position is characterized by an AAPosition
   * object and the list of position for a given amino acid is sorted from the
   * left to the right in the spectrum.
   * @return The list of amino acid positions in the spectrum
   */

  inline const std::vector<std::vector<AAPosition>> &
  getAminoAcidsPositions() const
  {
    return m_mapAminoAcidPositionsByAacode;
  }


  // Beaucoup appelée !
  /**
   * Compute and return the mass shift between the 'left_column' and the left
   * peak of the amino acid 'aa' given that the right peak of 'aa' correspond to
   * the 'right_column'.
   * @param left_column: beginning of the shift
   * @param right_column: end of the shift plus 'aa'
   * @param aa: the amino acid considered in the computation of the shift
   * @return the shift value
   */

  inline double
  getShift(int left_column, int right_column, double aa_mass) const
  {
    return m_columnMass[right_column] - aa_mass - m_columnMass[left_column];
  }

  /**
   * Return the mass shift between the 'last_column' and the hypothetical last
   * y-ion peak
   * @param last_column: the beginning of the shift
   * @return the shift value
   */

  inline double
  getLastShift(int last_column) const
  {
    double last_shift = m_heaviestPeakMass - m_columnMass[last_column];
    if(last_shift > m_ms2accuracy)
      {
        return last_shift;
      }
    else
      {
        return 0.0;
      }
  }

  /**
   * The middle peak correspond to the leftmost peak for which the complementary
   * peak is to its left. It is useful when a given alignment used at least one
   * peak and its complementary (see
   * algorithm.SpecGlobThread.cleanPeaksUsedSeveralTimes).
   * @return the number of the middle peak
   */
  int getMiddlePeak() const;

  /**
   * Return the peak_mask list
   * @param peak_mask list
   */
  const std::vector<int> &getPeakMask() const;


  std::size_t getNbAaFound() const;

  /**
   * Setter of the attribute peak_mask
   * @param peak_mask
   */
  void setPeakMask(const std::vector<int> &peak_mask);

  /**
   * Return the hypothetical error in the precursor mass.
   * @return non_aligned_mass value
   */
  double getNonAlignedMass() const;

  /**
   * Return the mass of the column in parameter
   * @param column: the number of the column
   * @return the mass value
   */
  inline double
  getColumnMass(int column) const
  {
    return m_columnMass[column];
  }

  /**
   * Compute the number of peaks (complementary included) that shared the mass
   * in parameter
   * @param mass: the researched mass
   * @return the number of peak with the mass in parameter
   */
  int massCount(double mass) const;

  double getLastMass() const;

  const std::vector<ComplementaryPeak> &getComplementaryPeaks() const;


  /**
   * This method is used to check if the current alignment has at least one peak
   * used several times. If it's the case, new alignments are computed until no
   * peak is used several times
   * @return if second (bool) is true, a correction has been tried. The
   * correction result is stored in first (alignment). The alignment is nullptr
   * if no correction has been tried or if no corrected alignment has sufficient
   * score.
   */
  std::pair<std::shared_ptr<Alignment>, bool> cleanPeaksUsedSeveralTimes(
    const pappso::spoms::Protein &protein,
    const pappso::spoms::LocationBackup &location_backup,
    const std::vector<Scenario> &scenario_list) const;

  void postProcessOneMass(const pappso::spoms::Protein &protein,
                          const pappso::spoms::LocationBackup &location_backup,
                          const std::vector<int> &peak_mask,
                          double non_aligned_mass,
                          AlignmentBucket &alignments_bucket) const;

  double getPrecursorMass(bool *ok_p = nullptr) const;

  private:
  /** @brief generate ghost peaks
   * complete the real peak list with complementary peaks when they are missing
   *
   */
  void generateGhostPeaks(const std::vector<double> &peaks);

  /**
   * A method that compute the mass of the complementary peak of a peak whose
   * the mass is in parameter
   * @param mass: the mass of the peak of which we wish to obtain the
   * complementary peak
   * @return the mass of the complementary peak of the peak in parameter
   */
  double complementary(double mass) const;


  private:
  struct peak_group
  {
    std::vector<int> peak1_list;
    std::vector<int> peak2_list;
  };

  private:
  /*
   * A recursive method that try all possible combinations of peaks used several
   * times. Each combination lead to a new spectrum with some removed peaks and
   * a new alignment. At the end, the best alignment is store in the
   * current_alignment variable
   */
  std::shared_ptr<Alignment> tryAllRedundantGroups(
    int *depth,
    std::vector<TransformedSpectrum::peak_group> &peaks_group_list,
    std::vector<int> &current_peak_mask,
    AlignmentBucket &alignments_bucket,
    const pappso::spoms::Protein &protein,
    const pappso::spoms::LocationBackup &location_backup,
    int group_number,
    int number_of_groups,
    double non_aligned_mass) const;

  private:
  const pappso::AaCode &m_aaCode;
  QualifiedMassSpectrumCstSPtr mcsp_qualifiedMassSpectrum;
  std::vector<int> m_peakMask; // size is number of peaks
  double m_nonAlignedMass = 0;

  // array indice to the original spectrum, replaced by
  // mcsp_qualifiedMassSpectrum
  // int id;
  double m_precursorMass;
  int m_middlePeak;
  std::vector<std::vector<AAPosition>>
    m_mapAminoAcidPositionsByAacode; // map aa code to aapositions
  bool *m_doubleColumn =
    nullptr; // true if the column is a pair of complement ions
  double *m_columnMass = nullptr;

  /** @brief heaviest possible mass in the peak list given the precursor mass
   * (PRECURSOR_MASS + MHPLUS)
   */
  double m_heaviestPeakMass;
  // int column_count;

  std::vector<ComplementaryPeak> m_allComplementaryPeaks;

  // private static int[][] peak_masks;
  // static std::vector<std::vector<ComplementaryPeak>> rts_all_peaks;
  double m_ms2accuracy   = 0.02;
  int m_nbMinimumAaFound = 10;
  int m_columnCount;
  int m_nbAaFound = 0;

  int m_maximumCallToTryAllRedundantGroups = 5000;
};


} // namespace spoms
} // namespace pappso
