/**
 * \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/>.
 *
 */

#include "transformedspectrum.h"
#include "aaposition.h"
#include "complementarypeak.h"
#include <cstdint>
#include <ctime>
#include <memory>
#include <pappsomspp/core/amino_acid/aacode.h>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/exception/exceptionnotimplemented.h>
#include <pappsomspp/core/types.h>
#include <qcontainerfwd.h>
#include <qlogging.h>
#include <qnamespace.h>
#include <vector>
#include "spomsutils.h"
#include "protein.h"
#include "alignmentbucket.h"


pappso::spoms::TransformedSpectrum::~TransformedSpectrum()
{
  if(m_columnMass != nullptr)
    delete[] m_columnMass;
  if(m_doubleColumn != nullptr)
    delete[] m_doubleColumn;
}

/**
 * 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
 */
pappso::spoms::TransformedSpectrum::TransformedSpectrum(
  const pappso::AaCode &aaCode, QualifiedMassSpectrumCstSPtr qmassspectrum_sp)
  : m_aaCode(aaCode)
{
  m_ms2accuracy      = SpomsUtils::GetInstance()->getMs2Accuracy();
  m_nbMinimumAaFound = SpomsUtils::GetInstance()->getNbMinAaFound();
  qDebug();
  int number_of_amino_acide_indices = m_aaCode.getSize() + 1;

  std::vector<std::vector<AAPosition>> amino_acids_positions_temp;
  amino_acids_positions_temp.resize(number_of_amino_acide_indices);

  std::vector<double> peaks =
    qmassspectrum_sp.get()->getMassSpectrumCstSPtr().get()->xValues();
  mcsp_qualifiedMassSpectrum = qmassspectrum_sp;
  m_nonAlignedMass           = 0.0;
  bool is_ok;
  m_precursorMass = qmassspectrum_sp.get()->getPrecursorMass(&is_ok);
  if(!is_ok)
    {
      throw pappso::PappsoException("getPrecursorMass error");
    }
  // ComplementaryPeak peak_temp;
  //
  m_peakMask.resize(peaks.size(), 0);

  qDebug();
  std::vector<bool> peak_checked(peaks.size() + 2, false);
  std::fill(peak_checked.begin(), peak_checked.end(), false);
  // Arrays.fill(TransformedSpectrum.checked[0], 0, peaks.length, false);

  qDebug() << peak_checked.size();
  generateGhostPeaks(peaks);
  int nb_complementary_peaks =
    m_allComplementaryPeaks.size(); // IMPORTANT RECENT FIX OLIVIER


  std::vector<std::uint32_t> peak_ending;
  peak_ending.resize(nb_complementary_peaks + 2, 0);
  std::fill(peak_ending.begin(), peak_ending.end(), 0);

  std::vector<bool> selected_peaks(nb_complementary_peaks, false);

  m_nbAaFound = 0;

  std::vector<int> amino_acids_positions_number; // map aa code to number
  amino_acids_positions_number.resize(number_of_amino_acide_indices, 0);


  // select amino acid positions
  m_columnCount = 1;
  double difference;
  // qDebug();
  for(int j = 1; j < nb_complementary_peaks;
      ++j) // Cette partie peut sans doute être améliorée !!
    {
      for(int i = 0; i < j; ++i)
        {
          // qDebug() << i << " " << j;
          for(uint8_t aa_code = 0; aa_code < number_of_amino_acide_indices;
              ++aa_code)
            {

              // qDebug() << aa_code;
              difference = (m_allComplementaryPeaks[j].get_mass() -
                            m_allComplementaryPeaks[i].get_mass()) -
                           m_aaCode.getMass(aa_code);
              if(std::abs(difference) <= m_ms2accuracy &&
                 (amino_acids_positions_number[aa_code] == 0 ||
                  amino_acids_positions_temp[aa_code]
                      .at(amino_acids_positions_number[aa_code] - 1)
                      .getRightColumn() < j))
                {
                  // if the mass delta match the amino acid mass
                  //  && (this is the first ocurrence of the aa ||
                  //  previous occurence right peak is before current right
                  //  peak)
                  ++m_nbAaFound;
                  // keep trace of the occurence of this aa on the right peak j
                  peak_ending[j] |= (std::uint32_t)1
                                    << ((std::uint32_t)(aa_code + 2));

                  amino_acids_positions_temp[aa_code].push_back(
                    AAPosition(j,
                               i,
                               m_allComplementaryPeaks[j].getNativePeak(),
                               m_allComplementaryPeaks[i].getNativePeak(),
                               m_allComplementaryPeaks[j].get_mass(),
                               m_allComplementaryPeaks[i].get_mass()));
                  ++amino_acids_positions_number[aa_code];
                  if(!selected_peaks[j])
                    {
                      // the right peak is selected
                      selected_peaks[j] = true;
                      ++m_columnCount;
                    }
                  // qDebug();
                }
            }
        }
      // qDebug();
    }
  // amino acid positions are OK
  // right peaks are selected

  if(m_nbAaFound >=
     m_nbMinimumAaFound) // Le spectre est cree si et seulement si
                         // suffisemment d'AAs ont ete trouve dedans
    {
      // Le contenu de "amino_acids_positions_temp" est copie dans la matrice
      // "this.amino_acids_positions" pour plus d'efficacite
      m_mapAminoAcidPositionsByAacode.resize(number_of_amino_acide_indices);
      for(std::uint8_t aa_code = 0; aa_code < number_of_amino_acide_indices;
          ++aa_code)
        {
          m_mapAminoAcidPositionsByAacode[aa_code] =
            amino_acids_positions_temp[aa_code];
          for(auto &aa_pos : m_mapAminoAcidPositionsByAacode[aa_code])
            {
              if(aa_pos.getLeftColumn() == 0)
                {
                  aa_pos.setCondition(2);
                }
              else
                {
                  aa_pos.setCondition(peak_ending[aa_pos.getLeftColumn()]);
                }
            }
        }

      // Seul les pics utiles (a droite d'un AA dans le spectre) generent une
      // colonne dans la matrice de score
      //
      //
      /*
      QStringList debug_selected_peaks;
      for(int i = 0; i < selected_peaks.size(); i++)
        {
          debug_selected_peaks
            << QString("i:%1_%2").arg(i).arg(selected_peaks[i]);
        }
      qDebug() << debug_selected_peaks.join(" ");
      qDebug() << column_count;
      */

      std::vector<int> peak_column_correspondance((peaks.size() << 1) + 2, 0);
      // Arrays.fill(TransformedSpectrum.column_correspondance[0], 0);
      // correspondance from initial all peak number to the only selected peaks
      // number (selected peak number must be consecutives)
      peak_column_correspondance[0] = 1;
      m_doubleColumn                = new bool[m_columnCount]{false};
      // "this.column_mass" sert pour le calcul des shifts
      m_columnMass     = new double[m_columnCount];
      m_columnMass[0]  = m_allComplementaryPeaks[0].get_mass();
      int column_index = 1;
      int i            = 0;
      while(column_index <= m_columnCount - 1)
        {
          if(selected_peaks[i])
            {
              if(m_allComplementaryPeaks[i].isDouble())
                {
                  m_doubleColumn[column_index] = true;
                }
              m_columnMass[column_index] =
                m_allComplementaryPeaks[i].get_mass();
              peak_column_correspondance[i] = column_index + 1;
              ++column_index;
            }
          ++i;
        }

      for(std::uint8_t k = 0; k < number_of_amino_acide_indices; ++k)
        {
          for(auto &aa_pos : m_mapAminoAcidPositionsByAacode[k])
            {
              qDebug() << "before left peak:" << aa_pos.getLeftPeak()
                       << "(mass:" << aa_pos.getLeftMass() << ")"
                       << " right peak:" << aa_pos.getRightPeak()
                       << "(mass:" << aa_pos.getRightMass() << ")";
              aa_pos.updateColumn(peak_column_correspondance);
              qDebug() << "after left peak:" << aa_pos.getLeftPeak()
                       << "(mass:" << aa_pos.getLeftMass() << ")"
                       << " right peak:" << aa_pos.getRightPeak()
                       << "(mass:" << aa_pos.getRightMass() << ")";
            }
        }
    } // if the spectrum is processed
  else
    {
      m_mapAminoAcidPositionsByAacode.resize(0);
    }
}


void
pappso::spoms::TransformedSpectrum::generateGhostPeaks(
  const std::vector<double> &peaks)
{
  m_middlePeak = 0;
  m_allComplementaryPeaks.clear();
  m_heaviestPeakMass = complementary(MHPLUS);
  // AminoAcid.getCTermMass() + AminoAcid.getNTermMass() +
  double first_mass = MASSH2O + MHPLUS;
  m_allComplementaryPeaks.reserve((peaks.size() << 1) + 2);
  m_allComplementaryPeaks.push_back({first_mass, false, 0});
  m_allComplementaryPeaks.push_back(
    {m_heaviestPeakMass, false, (int)peaks.size() + 1});
  // qDebug();


  int i             = 0;
  int j             = peaks.size() - 1;
  double total_mass = complementary(0);
  while(i <= j)
    {
      if(i == j)
        {
          m_allComplementaryPeaks.push_back({peaks[i], false, i + 1});
          m_allComplementaryPeaks.push_back(
            {complementary(peaks[i]), false, i + 1});
          if(peaks[i] < complementary(peaks[i]))
            {
              m_middlePeak = i + 1;
            }
          break;
        }
      double sum_mass = peaks[i] + peaks[j];
      if(std::abs(sum_mass - total_mass) < m_ms2accuracy)
        { // we have a complement !
          if(m_peakMask[i] >= 0)
            {
              m_allComplementaryPeaks.push_back({peaks[i], true, i + 1});
            }
          if(m_peakMask[i] <= 0)
            {
              m_allComplementaryPeaks.push_back({peaks[j], true, i + 1});
            }
          qDebug() << "complement! " << peaks[i] << " " << peaks[j];

          if(peaks[i] < complementary(peaks[i]))
            {
              m_middlePeak = i + 1;
            }
          i++;
          j--;
          continue;
        }
      else
        {
          double i_complement = complementary(peaks[i]);
          if(i_complement > peaks[j])
            {
              if(m_peakMask[i] >= 0)
                {
                  m_allComplementaryPeaks.push_back({peaks[i], false, i + 1});
                }
              if(i_complement < m_heaviestPeakMass && m_peakMask[i] <= 0)
                m_allComplementaryPeaks.push_back({i_complement, false, i + 1});

              if(peaks[i] < i_complement)
                {
                  m_middlePeak = i + 1;
                }
              i++;
              continue;
            }
          else
            {
              i_complement = complementary(peaks[j]);
              if(m_peakMask[j] >= 0)
                {
                  m_allComplementaryPeaks.push_back({peaks[j], false, j + 1});
                }
              if(i_complement > first_mass && m_peakMask[j] <= 0)
                m_allComplementaryPeaks.push_back(
                  {complementary(peaks[j]), false, j + 1});

              if(peaks[j] < i_complement)
                {
                  m_middlePeak = j + 1;
                }
              j--;
              continue;
            }
        }
      i++;
    }
  sort(m_allComplementaryPeaks.begin(), m_allComplementaryPeaks.end());
  // qDebug();
}
/**
 * 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).
 */
pappso::spoms::TransformedSpectrum::TransformedSpectrum(
  const pappso::AaCode &aaCode,
  QualifiedMassSpectrumCstSPtr qmassspectrum_sp,
  const std::vector<int> &peak_mask,
  double non_aligned_mass)
  : m_aaCode(aaCode)
{
  m_ms2accuracy      = SpomsUtils::GetInstance()->getMs2Accuracy();
  m_nbMinimumAaFound = SpomsUtils::GetInstance()->getNbMinAaFound();
  m_maximumCallToTryAllRedundantGroups =
    SpomsUtils::GetInstance()->getParameterValueOrDefault(
      "post_treatment", "maximum_tries_on_recursive_correction", 5000);

  // qDebug();
  int number_of_amino_acide_indices = m_aaCode.getSize() + 1;

  std::vector<std::vector<AAPosition>> amino_acids_positions_temp;
  amino_acids_positions_temp.resize(number_of_amino_acide_indices);

  std::vector<double> peaks =
    qmassspectrum_sp.get()->getMassSpectrumCstSPtr().get()->xValues();
  mcsp_qualifiedMassSpectrum = qmassspectrum_sp;
  m_nonAlignedMass           = non_aligned_mass;
  m_peakMask                 = peak_mask;
  bool is_ok;
  m_precursorMass =
    qmassspectrum_sp.get()->getPrecursorMass(&is_ok) - non_aligned_mass;
  if(!is_ok)
    {
      throw pappso::PappsoException("getPrecursorMass error");
    }
  // ComplementaryPeak peak_temp;

  // qDebug();
  std::vector<bool> peak_checked(peaks.size() + 2, false);
  std::fill(peak_checked.begin(), peak_checked.end(), false);
  // Arrays.fill(TransformedSpectrum.checked[0], 0, peaks.length, false);

  // qDebug() << peak_checked.size();
  generateGhostPeaks(peaks);
  int nb_complementary_peaks =
    m_allComplementaryPeaks.size(); // IMPORTANT RECENT FIX OLIVIER


  std::vector<std::uint32_t> peak_ending;
  peak_ending.resize(nb_complementary_peaks + 2, 0);
  std::fill(peak_ending.begin(), peak_ending.end(), 0);

  std::vector<bool> selected_peaks(nb_complementary_peaks, false);

  m_nbAaFound = 0;

  std::vector<int> amino_acids_positions_number; // map aa code to number
  amino_acids_positions_number.resize(number_of_amino_acide_indices, 0);


  // select amino acid positions
  m_columnCount = 1;
  double difference;
  // qDebug();
  for(int j = 1; j < nb_complementary_peaks;
      ++j) // Cette partie peut sans doute être améliorée !!
    {
      for(int i = 0; i < j; ++i)
        {
          // qDebug() << i << " " << j;
          for(uint8_t aa_code = 0; aa_code < number_of_amino_acide_indices;
              ++aa_code)
            {

              // qDebug() << aa_code;
              difference = (m_allComplementaryPeaks[j].get_mass() -
                            m_allComplementaryPeaks[i].get_mass()) -
                           m_aaCode.getMass(aa_code);
              if(std::abs(difference) <= m_ms2accuracy &&
                 (amino_acids_positions_number[aa_code] == 0 ||
                  amino_acids_positions_temp[aa_code]
                      .at(amino_acids_positions_number[aa_code] - 1)
                      .getRightColumn() < j))
                {
                  // if the mass delta match the amino acid mass
                  //  && (this is the first ocurrence of the aa ||
                  //  previous occurence right peak is before current right
                  //  peak)
                  ++m_nbAaFound;
                  // keep trace of the occurence of this aa on the right peak j
                  peak_ending[j] |= (std::uint32_t)1
                                    << ((std::uint32_t)(aa_code + 2));

                  amino_acids_positions_temp[aa_code].push_back(
                    AAPosition(j,
                               i,
                               m_allComplementaryPeaks[j].getNativePeak(),
                               m_allComplementaryPeaks[i].getNativePeak(),
                               m_allComplementaryPeaks[j].get_mass(),
                               m_allComplementaryPeaks[i].get_mass()));
                  ++amino_acids_positions_number[aa_code];
                  if(!selected_peaks[j])
                    {
                      // the right peak is selected
                      selected_peaks[j] = true;
                      ++m_columnCount;
                    }
                  // qDebug();
                }
            }
        }
      // qDebug();
    }
  // amino acid positions are OK
  // right peaks are selected

  if(m_nbAaFound >=
     m_nbMinimumAaFound) // Le spectre est cree si et seulement si
                         // suffisemment d'AAs ont ete trouve dedans
    {
      // Le contenu de "amino_acids_positions_temp" est copie dans la matrice
      // "this.amino_acids_positions" pour plus d'efficacite
      m_mapAminoAcidPositionsByAacode.resize(number_of_amino_acide_indices);
      for(std::uint8_t aa_code = 0; aa_code < number_of_amino_acide_indices;
          ++aa_code)
        {
          m_mapAminoAcidPositionsByAacode[aa_code] =
            amino_acids_positions_temp[aa_code];
          for(auto &aa_pos : m_mapAminoAcidPositionsByAacode[aa_code])
            {
              if(aa_pos.getLeftColumn() == 0)
                {
                  aa_pos.setCondition(2);
                }
              else
                {
                  aa_pos.setCondition(peak_ending[aa_pos.getLeftColumn()]);
                }
            }
        }

      // Seul les pics utiles (a droite d'un AA dans le spectre) generent une
      // colonne dans la matrice de score
      //
      //
      /*
      QStringList debug_selected_peaks;
      for(int i = 0; i < selected_peaks.size(); i++)
        {
          debug_selected_peaks
            << QString("i:%1_%2").arg(i).arg(selected_peaks[i]);
        }
      qDebug() << debug_selected_peaks.join(" ");
      qDebug() << column_count;
      */

      std::vector<int> peak_column_correspondance((peaks.size() << 1) + 2, 0);
      // Arrays.fill(TransformedSpectrum.column_correspondance[0], 0);
      // correspondance from initial all peak number to the only selected peaks
      // number (selected peak number must be consecutives)
      peak_column_correspondance[0] = 1;
      m_doubleColumn                = new bool[m_columnCount]{false};
      // "this.column_mass" sert pour le calcul des shifts
      m_columnMass     = new double[m_columnCount];
      m_columnMass[0]  = m_allComplementaryPeaks[0].get_mass();
      int column_index = 1;
      int i            = 0;
      while(column_index <= m_columnCount - 1)
        {
          if(selected_peaks[i])
            {
              if(m_allComplementaryPeaks[i].isDouble())
                {
                  m_doubleColumn[column_index] = true;
                }
              m_columnMass[column_index] =
                m_allComplementaryPeaks[i].get_mass();
              peak_column_correspondance[i] = column_index + 1;
              ++column_index;
            }
          ++i;
        }

      for(std::uint8_t k = 0; k < number_of_amino_acide_indices; ++k)
        {
          for(auto &aa_pos : m_mapAminoAcidPositionsByAacode[k])
            {
              // qDebug() << "before left peak:" << aa_pos.getLeftPeak()
              //          << "(mass:" << aa_pos.getLeftMass() << ")"
              //          << " right peak:" << aa_pos.getRightPeak()
              //          << "(mass:" << aa_pos.getRightMass() << ")";
              aa_pos.updateColumn(peak_column_correspondance);
              // qDebug() << "after left peak:" << aa_pos.getLeftPeak()
              //          << "(mass:" << aa_pos.getLeftMass() << ")"
              //          << " right peak:" << aa_pos.getRightPeak()
              //          << "(mass:" << aa_pos.getRightMass() << ")";
            }
        }
    } // if the spectrum is processed
}


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

int
pappso::spoms::TransformedSpectrum::getColumnCount() const
{
  return m_columnCount;
}


/**
 * 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
pappso::spoms::TransformedSpectrum::getMiddlePeak() const
{
  return m_middlePeak;
}

/**
 * Return the peak_mask list
 * @param peak_mask list
 */

const std::vector<int> &
pappso::spoms::TransformedSpectrum::getPeakMask() const
{
  return m_peakMask;
}

/**
 * Setter of the attribute peak_mask
 * @param peak_mask
 */

void
pappso::spoms::TransformedSpectrum::setPeakMask(
  const std::vector<int> &peak_mask_in)
{
  m_peakMask = peak_mask_in;
}

/**
 * Return the hypothetical error in the precursor mass.
 * @return non_aligned_mass value
 */

double
pappso::spoms::TransformedSpectrum::getNonAlignedMass() const
{
  return m_nonAlignedMass;
}


/**
 * 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
pappso::spoms::TransformedSpectrum::massCount(double mass) const
{
  std::vector<double> peaks =
    mcsp_qualifiedMassSpectrum.get()->getMassSpectrumSPtr().get()->xValues();
  int left    = -1;
  int right   = peaks.size();
  int counter = 0;
  bool found  = false;
  while(!found && left + 1 < right)
    {
      if(mass > peaks[(left + right) / 2] + m_ms2accuracy)
        {
          left = (left + right) / 2;
        }
      else if(mass < peaks[(left + right) / 2] - m_ms2accuracy)
        {
          right = (left + right) / 2;
        }
      else
        {
          counter = 1;
          found   = true;
        }
    }
  left  = -1;
  right = peaks.size();
  found = false;
  mass  = complementary(mass);
  while(!found && left + 1 < right)
    {
      if(mass > peaks[(left + right) / 2] + m_ms2accuracy)
        {
          left = (left + right) / 2;
        }
      else if(mass < peaks[(left + right) / 2] - m_ms2accuracy)
        {
          right = (left + right) / 2;
        }
      else
        {
          ++counter;
          found = true;
        }
    }
  return counter;
}

/**
 * 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
pappso::spoms::TransformedSpectrum::complementary(double mass) const
{
  return m_precursorMass - mass + 2 * MHPLUS;
}

double
pappso::spoms::TransformedSpectrum::getLastMass() const
{
  return m_heaviestPeakMass;
}


const std::vector<pappso::spoms::ComplementaryPeak> &
pappso::spoms::TransformedSpectrum::getComplementaryPeaks() const
{
  return m_allComplementaryPeaks;
}


/*
 * 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<pappso::spoms::Alignment>
pappso::spoms::TransformedSpectrum::tryAllRedundantGroups(
  int *pdepth,
  std::vector<TransformedSpectrum::peak_group> &peaks_groups,
  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
{
  (*pdepth)++;

  qDebug() << "tryAllRedundantGroups depth:" << *pdepth;
  std::shared_ptr<Alignment> new_alignment_sp;

  if(*pdepth > m_maximumCallToTryAllRedundantGroups)
    return new_alignment_sp;

  if(group_number == number_of_groups)
    {
      /*
       * For a given combination, a new spectrum is built and a new alignment is
       * performed on it and the current LssSM
       */
      /*
      this.current_transformed_spectrum =
        this.transformed_spectra_bucket[this.current_position_in_bucket + 2];
      this.current_native_spectrum.transform(this.current_transformed_spectrum,
                                             this.current_peak_mask,
                                             this.current_non_aligned_mass);
*/
      std::shared_ptr<TransformedSpectrum> new_transformed_spectrum_sp =
        std::make_shared<TransformedSpectrum>(m_aaCode,
                                              mcsp_qualifiedMassSpectrum,
                                              current_peak_mask,
                                              non_aligned_mass);
      try
        {
          // qWarning() << "ts 1068";
          new_alignment_sp = protein.finalTreatment(
            new_transformed_spectrum_sp, location_backup, true);
          qDebug() << "ts 1071";
          alignments_bucket.tryToAdd(new_alignment_sp);
        }
      catch(const pappso::PappsoException &pappso_error)
        {
          throw pappso::PappsoException(
            QObject::tr("tryAllRedundantGroups failed with error :\n%1")
              .arg(pappso_error.qwhat()));
        }
    }
  else
    {
      std::vector<int> &group1 = peaks_groups[group_number].peak1_list;
      std::vector<int> &group2 = peaks_groups[group_number].peak2_list;
      for(int peak_number : group1)
        {
          current_peak_mask[peak_number - 1] = 1;
        }
      for(int peak_number : group2)
        {
          current_peak_mask[peak_number - 1] = -1;
        }
      new_alignment_sp = tryAllRedundantGroups(pdepth,
                                               peaks_groups,
                                               current_peak_mask,
                                               alignments_bucket,
                                               protein,
                                               location_backup,
                                               group_number + 1,
                                               number_of_groups,
                                               0);

      alignments_bucket.tryToAdd(new_alignment_sp);
      for(int peak_number : group1)
        {
          current_peak_mask[peak_number - 1] = -1;
        }

      for(int peak_number : group2)
        {
          current_peak_mask[peak_number - 1] = 1;
        }
      new_alignment_sp = tryAllRedundantGroups(pdepth,
                                               peaks_groups,
                                               current_peak_mask,
                                               alignments_bucket,
                                               protein,
                                               location_backup,
                                               group_number + 1,
                                               number_of_groups,
                                               0);

      alignments_bucket.tryToAdd(new_alignment_sp);
      for(int peak_number : group1)
        {
          current_peak_mask[peak_number - 1] = 0;
        }
      for(int peak_number : group2)
        {
          current_peak_mask[peak_number - 1] = 0;
        }
      peaks_groups[group_number].peak1_list.clear();
      peaks_groups[group_number].peak2_list.clear();
    }
  new_alignment_sp = alignments_bucket.getBestAlignment();
  return new_alignment_sp;
}


std::pair<std::shared_ptr<pappso::spoms::Alignment>, bool>
pappso::spoms::TransformedSpectrum::cleanPeaksUsedSeveralTimes(
  const pappso::spoms::Protein &protein,
  const pappso::spoms::LocationBackup &location_backup,
  const std::vector<Scenario> &scenario_list) const
{
  int MIN_SCENARIO_SCORE =
    pappso::spoms::SpomsUtils::GetInstance()
      ->getParameterValue("alignment", "min_scenario_score")
      .toInt();
  int NB_INTERPRETATIONS_SAVED =
    pappso::spoms::SpomsUtils::GetInstance()
      ->getParameterValue("post_treatment", "nb_interpretations_saved")
      .toInt();
  int current_group_number;
  AAPosition aaPosition;

  std::vector<int> used_peaks(getColumnCount() * 2);

  std::vector<double> peaks =
    mcsp_qualifiedMassSpectrum.get()->getMassSpectrumCstSPtr().get()->xValues();


  std::vector<peak_group> peaks_group_list(peaks.size());

  std::vector<int> current_peak_mask(peaks.size(), 0);


  AlignmentBucket alignments_bucket(NB_INTERPRETATIONS_SAVED);

  bool tried_correction(false);


  int nb_redundant_calls = 0;

  do
    {
      /*
       * Store the peaks used several times in the current alignment and divided
       * them in several groups. A group contains peaks that are consecutive in
       * the current alignment
       */
      bool has_redundant_peaks = false;
      std::vector<int> counter_used_peaks(peaks.size() + 2, 0);
      // Arrays.fill(this.counter_used_peaks, 0);
      int peak_index = 0;
      Scenario current_scenario;
      int scenario_number = 0;
      do
        {
          ++scenario_number;
          current_scenario = scenario_list.at(scenario_number);
          //  this.current_alignment.getScenarios().get(scenario_number);
          aaPosition = current_scenario.getPosition();
          ++counter_used_peaks[aaPosition.getRightPeak()];
          if(counter_used_peaks[aaPosition.getRightPeak()] == 2)
            {
              has_redundant_peaks = true;
            }
          used_peaks[peak_index] = aaPosition.getRightPeak();
          ++peak_index;
          // if ((aaPosition.getCondition() & 1) == 1) // si la version 3 pics
          // n'est plus appliquée, alors cette condition n'est plus valide ! A
          // remplacer par ça (current_scenario.getPreviousColumnNumber() - 1 ==
          // current_scenario.getPosition().getLeftColumn()) si jamais
          if(current_scenario.getPreviousColumnNumber() !=
             aaPosition.getLeftColumn() + 1)
            {
              ++counter_used_peaks[aaPosition.getLeftPeak()];
              if(counter_used_peaks[aaPosition.getLeftPeak()] == 2)
                {
                  has_redundant_peaks = true;
                }
              used_peaks[peak_index] = aaPosition.getLeftPeak();
              ++peak_index;
            }
        }
      while(current_scenario.getPreviousColumnNumber() > 0);

      current_group_number = 0;
      if(has_redundant_peaks)
        {
          int middle_peak = getMiddlePeak();
          int left_index = 0, right_index = peak_index - 1;
          while(left_index < right_index)
            {
              while(counter_used_peaks[used_peaks[left_index]] == 1)
                {
                  ++left_index;
                }
              if(left_index < right_index)
                {
                  while(used_peaks[left_index] != used_peaks[right_index])
                    {
                      --right_index;
                    }
                  do
                    {
                      if(used_peaks[left_index] <= middle_peak)
                        {
                          peaks_group_list[current_group_number]
                            .peak1_list.push_back(used_peaks[left_index]);
                        }
                      else
                        {
                          peaks_group_list[current_group_number]
                            .peak2_list.push_back(used_peaks[left_index]);
                        }
                      ++left_index;
                      --right_index;
                    }
                  while(left_index < right_index &&
                        counter_used_peaks[used_peaks[left_index]] == 2 &&
                        used_peaks[left_index] == used_peaks[right_index]);
                  ++current_group_number;
                }
            }
        }

      /*
       * If there is at least one peak used several times, computing a new
       * alignment on a new version of the current spectrum
       */

      /*
            std::vector<std::shared_ptr<TransformedSpectrum>>
              transformed_spectra_bucket;
              */

      if(current_group_number > 0)
        {
          // qWarning() << "ts 1255 " << current_group_number;
          //  this.alignments_bucket[this.current_position_in_bucket].clear();
          //  this.alignments_bucket[this.current_position_in_bucket +
          //  1].clear();
          tried_correction = true;
          std::shared_ptr<Alignment> alignment =
            tryAllRedundantGroups(&nb_redundant_calls,
                                  peaks_group_list,
                                  current_peak_mask,
                                  alignments_bucket,
                                  protein,
                                  location_backup,
                                  0,
                                  current_group_number,
                                  0);

          if((alignment.get() != nullptr) &&
             (alignment.get()->getScore() > MIN_SCENARIO_SCORE))
            {
              /*
            this.alignments_bucket[this.current_position_in_bucket + 1] =
              this.alignments_bucket[this.current_position_in_bucket];
            this.alignments_bucket[this.current_position_in_bucket] =
              this.current_alignment;
            this.current_transformed_spectrum =
              this
                .transformed_spectra_bucket[this.current_position_in_bucket +
                                            1];
            this.transformed_spectra_bucket[this.current_position_in_bucket +
                                            1] =
              this
                .transformed_spectra_bucket[this.current_position_in_bucket];
            this.transformed_spectra_bucket[this.current_position_in_bucket] =
              this.current_transformed_spectrum;
              */
              current_peak_mask =
                alignment.get()->getTransformedSpectrum().get()->getPeakMask();
              // this.current_transformed_spectrum.getPeakMask();
            }
          else
            {
              current_group_number = 0;
            }
        }
    }
  while(current_group_number > 0);

  // qWarning() << "ts 1301 " << current_group_number;
  return {alignments_bucket.getBestAlignment(), tried_correction};
}

/**
 * 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
pappso::spoms::TransformedSpectrum::isUsefull() const
{
  if(m_nbAaFound < m_nbMinimumAaFound)
    return false;
  return true;
}

void
pappso::spoms::TransformedSpectrum::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
{
  try
    {
      // qWarning() << "ts 1327";
      //  current_transformed_spectrum =
      //    this.transformed_spectra_bucket[NB_INTERPRETATIONS_SAVED + 1];
      //  current_native_spectrum.transform(
      //    this.transformed_spectra_bucket[NB_INTERPRETATIONS_SAVED + 1],
      //    current_peak_mask,
      //    current_non_aligned_mass);
      std::shared_ptr<TransformedSpectrum> new_transformed_spectrum_sp =
        std::make_shared<TransformedSpectrum>(
          m_aaCode, mcsp_qualifiedMassSpectrum, peak_mask, non_aligned_mass);

      std::shared_ptr<Alignment> new_alignment_sp;
      /*
       * We use the second system of scores to perform the
       * alignment.
       */
      // qWarning() << "ts 1346";
      new_alignment_sp = protein.finalTreatment(
        new_transformed_spectrum_sp, location_backup, false);
      alignments_bucket.tryToAdd(new_alignment_sp);
      qDebug() << "ts 1346";
    }
  catch(const pappso::PappsoException &pappso_error)
    {
      throw pappso::PappsoException(
        QObject::tr("postProcessOneMass failed with error :\n%1")
          .arg(pappso_error.qwhat()));
    }
}

double
pappso::spoms::TransformedSpectrum::getPrecursorMass(bool *ok_p) const
{
  return mcsp_qualifiedMassSpectrum.get()->getPrecursorMass(ok_p);
}

std::size_t
pappso::spoms::TransformedSpectrum::getNbAaFound() const
{
  return m_nbAaFound;
}
