/**
 * \file core/spoms/protein.cpp
 * \date 06/02/2026
 * \author Aurélien Berthier, BENOIST Emile, TESSIER Dominique
 * \brief Running specpeptidoms on one scan
 */


/*
 * 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 "protein.h"
#include "aaposition.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <exception>
#include <memory>
#include <pappsomspp/core/exception/exceptionnotimplemented.h>
#include <pappsomspp/core/exception/exceptionoutofrange.h>
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/types.h>
#include <qdebug.h>
#include <qlogging.h>
#include <QDebug>
#include <QObject>
#include <qobject.h>
#include <vector>
#include "alignmentbucket.h"
#include "spomsaa.h"
#include "spomsutils.h"
#include "interpretationbackup.h"
#include "location.h"
#include "scenario.h"
// #include "interpretationsaver.h"

namespace pappso
{
namespace spoms
{

Protein::Protein(const pappso::AaCode &aa_code, pappso::ProteinSp protein_sp)
{
  m_ACCURACY = SpomsUtils::GetInstance()->getMs2Accuracy();
  m_NB_LOCATIONS_SAVED =
    SpomsUtils::GetInstance()
      ->getParameterValue("alignment", "nb_locations_saved")
      .toInt();

  msp_protein = protein_sp;
  m_sequence.reserve(msp_protein.get()->size());

  for(auto &aa_letter : msp_protein.get()->getSequence())
    {
      pappso::Enums::AminoAcidChar amino_acid =
        (pappso::Enums::AminoAcidChar)aa_letter.toLatin1();
      std::uint8_t aa_code_number = aa_code.getAaCode(amino_acid);
      m_sequence.push_back(
        {aa_code_number, amino_acid, aa_code.getMass(aa_code_number)});
    }
  std::reverse(m_sequence.begin(), m_sequence.end());
}
Protein::Protein(const Protein &other [[maybe_unused]])
{
  throw pappso::ExceptionNotImplemented("no Protein copy constructor");
}
Protein::~Protein()
{
}
const pappso::ProteinSp &
Protein::getProteinSp() const
{
  return msp_protein;
}

const std::vector<SpomsAa> &
Protein::getSequence() const
{
  return m_sequence;
}

std::vector<SpomsAa>
Protein::getSubSequence(int start, int end) const
{
  std::vector<SpomsAa> sub_sequence;

  start = std::max(0, start);
  end   = std::min((int)msp_protein.get()->size() - 1, end);
  for(int i = start; i <= end; ++i)
    {
      sub_sequence.push_back(m_sequence[i]);
    }
  return sub_sequence;
}

const SpomsAa &
Protein::getSpomsAa(int position)
{
  return m_sequence.at(position);
}

/**
 *
 * Return the string version of the subsequence starting (resp. ending) at
 * position 'start' (resp. 'end'), both include
 * @param start: the beginning of the subsequence
 * @param end: the end of the subsequence
 * @return the string subsequence
 */
QString
Protein::subSequence(int start, int end)
{
  start = std::max(0, start);
  end   = std::min((int)msp_protein.get()->size() - 1, end);

  QString sub_sequence =
    msp_protein.get()->getSequence().mid(start, end - start);
  return sub_sequence;
}

std::vector<LocationBackup>
Protein::preliminaryTreatmentV1(const TransformedSpectrum &transformed_spectrum,
                                const pappso::AaCode &aaCode,
                                int min_max_score,
                                const QJsonObject &score_grid) const
{

  MemoryBuffer memory_buffer(transformed_spectrum.getColumnCount(),
                             m_sequence.size());

  return preliminaryTreatmentV1(
    memory_buffer, transformed_spectrum, aaCode, min_max_score, score_grid);
}

std::vector<LocationBackup>
Protein::preliminaryTreatmentV1(MemoryBuffer &memory_buffer,
                                const TransformedSpectrum &transformed_spectrum,
                                const pappso::AaCode &aaCode,
                                int min_max_score,
                                const QJsonObject &score_grid) const
{
  qDebug();
  bool SHUT_DOWN_3_PEAKS_VERSION =
    score_grid.value("shut_down_3_peaks_version").toBool();

  int INITIALISATION_SCORE_MAIN =
    score_grid.value("found_with_shift_main").toInt() -
    score_grid.value("certainly_found_main").toInt() - 1;

  int CERTAINLY_FOUND_WITH_SHIFT_MAIN =
    score_grid.value("certainly_found_with_shift_main").toInt();
  int CERTAINLY_FOUND_MAIN  = score_grid.value("certainly_found_main").toInt();
  int FOUND_WITH_SHIFT_MAIN = score_grid.value("found_with_shift_main").toInt();
  int FOUND_MAIN            = score_grid.value("found_main").toInt();
  int NOT_FOUND_MAIN        = score_grid.value("not_found_main").toInt();
  int MAX_REALIGNMENT_SIZE_FIRST_COLUMN =
    score_grid.value("max_realignment_size_first_column").toInt();
  int MAX_REALIGNMENT_SIZE = score_grid.value("max_realignment_size").toInt();
  int SURPLUS              = score_grid.value("surplus").toInt();

  InterpretationsSaver interpretation_saver =
    InterpretationsSaver(this, m_NB_LOCATIONS_SAVED);
  double SMALLEST_MASS = aaCode.getMass((uint8_t)1);
  // int INITIALISATION_SCORE_MAIN  = -3;

  qDebug();


  int column_count = transformed_spectrum.getColumnCount();


  InterestCell *interest_cells  = memory_buffer.interest_cells;  // column_count
  InterestCell *scenario_buffer = memory_buffer.scenario_buffer; // column_count
  double *current_masses = memory_buffer.current_masses; // sequence_length
  int *trees_position =
    memory_buffer.trees_position; //(sequence_length + 1) * (column_count + 1)

  qDebug() << column_count;
  if(column_count == 0)
    return std::vector<LocationBackup>();
  // this.interpretations_saver.updateProtein(protein_number);
  // protein = proteins[protein_number];
  //
  //
  // int sequence_length = m_sequence.size();


  int tree_id             = 0;
  interest_cells[0].score = 0;

  qDebug();
  /*
   * Initialization of the first row
   */
  for(int j = 1; j < column_count; j++)
    {
      interest_cells[j].row_number = 0;
      interest_cells[j].score      = INITIALISATION_SCORE_MAIN;

      // qDebug() << interest_cells[j].score;
    }
  std::uint32_t current_condition = 3;
  int current_temp_scenario;
  int last_max_shift_score;
  int last_max_column;
  int last_column;
  int realignment_length;
  double current_shift;
  int current_score_shift;
  int current_score_found;
  int best_column;
  int best_score;
  int current_score;

  qDebug();

  /*
   *  For each amino acid in the current protein sequence
   */
  // for(int row_number = 0; row_number < sequence_length;)
  int row_number = 0;
  for(const SpomsAa &current_aa : m_sequence)
    {
      try
        {
          interest_cells[0].row_number = row_number;

          // qDebug() << "*********** row:" << row_number << " "
          //          << (char)m_sequence[row_number].aa << " "
          //          << m_sequence[row_number].code;

          // for(int j = 0; j < column_count; j++)
          //   {
          //     qDebug() << "i:" << j << "row:" << interest_cells[j].row_number
          //              << " score:" << interest_cells[j].score
          //              << " tree_id:" << interest_cells[j].tree_id
          //              << " beginning:" << interest_cells[j].beginning;
          //   }


          ++row_number;
          current_temp_scenario = 0;
          last_max_shift_score  = -1;
          last_max_column       = -1;
          last_column           = -1;
          const std::vector<AAPosition> &aa_positions =
            transformed_spectrum.getAminoAcidsPositions().at(
              current_aa.code); // all_aa_positions[aa_number];
          // aa_positions_length = all_aa_positions_number[aa_number];
          //

          // qDebug() << " aacode:" << aa_number << " letter:" << (char)aa;

          /*
           * For each position where the current amino acid is found in the
           * current spectrum
           */
          for(const AAPosition &aa_position : aa_positions)
            {

              qDebug() << aa_position.toString()
                       << " left:" << aa_position.getLeftColumn();
              /*
               * (184.129 ,271.176)1"  left: 6
               * (255.165 ,342.202)*10000"  left: 10
               * (427.213 ,514.246)*1001000"  left: 20
               * (428.217 ,515.248)*1000"  left: 21
               * (742.481 ,829.511)*1010000000"  left: 33
               * (743.482 ,830.515)*1000000000"  left: 34
               * (915.526 ,1002.56)1"  left: 42
               */

              // position           = aa_positions[aa_position_number];
              // uint32_t position_condition  = aa_position.getCondition();
              int aa_position_left_column  = aa_position.left_column;
              int aa_position_right_column = aa_position.right_column;

              // qDebug() << "before aa:" << row_number << " condition"
              //          << QString::number(position_condition, 2)
              //          << " left:" << aa_position.getLeftColumn();
              /*
               * This condition correspond to the restriction do to the 3 peaks
               * version
               */

              qDebug() << "preliminary treatment pa1a1";
              if((aa_position.condition & current_condition) != 0 ||
                 SHUT_DOWN_3_PEAKS_VERSION)
                {

                  qDebug() << "preliminary treatment pa1a2";
                  qDebug() << "condition OK aa:" << row_number << " condition:"
                           << QString::number(aa_position.condition, 2)
                           << " current condition"
                           << QString::number(current_condition, 2)
                           << " left:" << aa_position.left_column;
                  realignment_length = 0;
                  // aa_position.getLeftColumn()  = aa_position.getLeftColumn();
                  // aa_position.getRightColumn() =
                  // aa_position.getRightColumn();
                  current_shift = 0.0;

                  /*
                   * The score for a shift or a found depend on the peak of the
                   * current right column. This peak can be double or not
                   */
                  if(transformed_spectrum.isDoubleColumn(
                       aa_position_right_column))
                    {
                      current_score_shift = CERTAINLY_FOUND_WITH_SHIFT_MAIN;
                      current_score_found = CERTAINLY_FOUND_MAIN;
                    }
                  else
                    {
                      current_score_shift = FOUND_WITH_SHIFT_MAIN;
                      current_score_found = FOUND_MAIN;
                    }

                  /*
                   * First possibility :
                   * not found case
                   */
                  best_column = aa_position_right_column;
                  best_score =
                    interest_cells[best_column].score +
                    NOT_FOUND_MAIN *
                      (row_number - interest_cells[best_column].row_number);

                  /*
                   * Second possibility :
                   * found case
                   *
                   * The condition check that the left peak of the current
                   * AAPosition has a column
                   */
                  if((aa_position.condition & 1) == 0)
                    {
                      /*
                       * If it relies on a cell corresponding to a not found
                       * case, the score for the found case corresponds to the
                       * score of a shift
                       */
                      InterestCell *interest_cell_left_column_p =
                        &interest_cells[(aa_position_left_column + 1)];
                      if(interest_cell_left_column_p->row_number <
                         row_number - 1)
                        {
                          current_score =
                            interest_cell_left_column_p->score +
                            NOT_FOUND_MAIN *
                              ((row_number - 1) -
                               interest_cell_left_column_p->row_number) +
                            current_score_shift;
                        }
                      else
                        {
                          current_score = interest_cell_left_column_p->score +
                                          current_score_found;
                        }
                      /*
                       * Saving the new score if it's the new best one
                       */
                      if(current_score >= best_score)
                        {
                          best_score  = current_score;
                          best_column = aa_position_left_column + 1;
                        }
                    }

                  /*
                   * Found the new best position for a shift
                   */
                  InterestCell *cell_p = &interest_cells[(last_column + 1)];
                  for(int j = (last_column + 1); j <= aa_position_left_column;
                      ++j, cell_p++)
                    {
                      current_score = cell_p->score +
                                      NOT_FOUND_MAIN *
                                        ((row_number - 1) - cell_p->row_number);
                      if(current_score > last_max_shift_score)
                        {
                          last_max_shift_score = current_score;
                          last_max_column      = j;
                        }
                    }
                  last_column = aa_position_left_column;

                  /*
                   * If a shift is possible, i.e. the current left column is
                   * greater than -1
                   */
                  if(aa_position_left_column >= 0)
                    {
                      /*
                       * Trying a realignment from the first column if it is
                       * allowed
                       */
                      if(MAX_REALIGNMENT_SIZE_FIRST_COLUMN >= 2)
                        {
                          current_shift = transformed_spectrum.getShift(
                            0, aa_position_right_column, current_aa.mass);
                          if(current_score_found > best_score)
                            {
                              while(realignment_length <
                                      MAX_REALIGNMENT_SIZE_FIRST_COLUMN &&
                                    row_number - realignment_length >= 2 &&
                                    current_shift - m_ACCURACY >
                                      current_masses[realignment_length])
                                {
                                  ++realignment_length;
                                }
                              /*
                               * If the realignment works, saving the new score
                               * if it's the new best one
                               */
                              if(realignment_length <
                                   MAX_REALIGNMENT_SIZE_FIRST_COLUMN &&
                                 row_number - realignment_length >= 2 &&
                                 std::abs(current_shift -
                                          current_masses[realignment_length]) <=
                                   m_ACCURACY)
                                {
                                  best_score  = current_score_found;
                                  best_column = 0;
                                  ++realignment_length;
                                }
                            }
                        }
                      /*
                       * Trying a realignment from the best shift position if it
                       * is allowed, ...
                       */
                      if(MAX_REALIGNMENT_SIZE >= 2)
                        {
                          /*
                           * If the best shift position is the first column,
                           * there is no need to try a realignment because it
                           * has already been tried just before
                           */
                          if(last_max_column != 0)
                            {
                              current_shift = transformed_spectrum.getShift(
                                last_max_column,
                                aa_position_right_column,
                                current_aa.mass);
                              realignment_length =
                                row_number -
                                interest_cells[last_max_column].row_number - 2;
                              if(realignment_length >= 0 &&
                                 realignment_length < MAX_REALIGNMENT_SIZE &&
                                 std::abs(current_masses[realignment_length] -
                                          current_shift) <= m_ACCURACY)
                                {
                                  /*
                                   * The realignment works
                                   */
                                  current_score =
                                    interest_cells[last_max_column].score +
                                    current_score_found;
                                }
                              else
                                {
                                  /*
                                   * The realignment doesn't works
                                   */
                                  current_score =
                                    last_max_shift_score + current_score_shift;
                                }
                              /*
                               * Saving the new score if it's the new best one
                               */
                              if(current_score > best_score)
                                {
                                  best_score  = current_score;
                                  best_column = last_max_column;
                                }
                            }
                          /*
                           * Trying a shift from the first column
                           */
                          else if(current_score_shift > best_score)
                            {
                              best_score         = current_score_shift;
                              best_column        = 0;
                              realignment_length = 0;
                            }
                        }
                      /*
                       * ... if not, just try a shift from the best shift
                       * position
                       */
                      else if(last_max_shift_score + current_score_shift >
                              best_score)
                        {
                          best_score =
                            last_max_shift_score + current_score_shift;
                          best_column = last_max_column;
                        }
                    }
                  // qDebug() << "best_column != aa_position.getRightColumn()";
                  /*
                   * If the best case is not a not found case, it is stored in
                   * the differents structures
                   */
                  if(best_column != aa_position_right_column)
                    {
                      InterestCell *p_current_scenario =
                        &scenario_buffer[current_temp_scenario];
                      p_current_scenario->row_number = aa_position_right_column;
                      p_current_scenario->score      = best_score;
                      if(best_column == 0)
                        {
                          trees_position[tree_id]     = -1;
                          p_current_scenario->tree_id = tree_id;
                          p_current_scenario->beginning =
                            row_number - realignment_length - SURPLUS -
                            (int)(current_shift / SMALLEST_MASS);
                          ++tree_id;
                        }
                      else
                        {
                          p_current_scenario->tree_id =
                            interest_cells[best_column].tree_id;
                          p_current_scenario->beginning =
                            interest_cells[best_column].beginning;
                        }
                      current_temp_scenario++;
                      // serious problem found 22/02/2026  current_temp_scenario
                      // +=
                      // 4
                    }
                }

              // qDebug() << "preliminary treatment p1";
            }

          qDebug() << "preliminary treatment p2";
          current_condition = (1 << (current_aa.code + 2)) + 3;
          /*
           * Saving all the scenarios for the current row
           */
          // serious problem found 22/02/2026     for(int i = 0; i <
          // current_temp_scenario; i += 4)
          InterestCell *p_scenario_buffer = &scenario_buffer[0];
          for(int i = 0; i < current_temp_scenario; i++, p_scenario_buffer++)
            {
              //   qDebug() << "preliminary treatment pa3a1 " << i
              //            << " best column:" << best_column
              //            << " column count:" << column_count;
              best_column = p_scenario_buffer->row_number;
              InterestCell *p_interest_cells_best_column =
                &interest_cells[best_column];
              p_interest_cells_best_column->row_number = row_number;
              p_interest_cells_best_column->score = p_scenario_buffer->score;
              p_interest_cells_best_column->tree_id =
                p_scenario_buffer->tree_id;
              p_interest_cells_best_column->beginning =
                p_scenario_buffer->beginning;


              // qDebug() << "scenario_buffer " << (i + 1) << " row" <<
              // row_number
              //          << " score:" << scenario_buffer[i].score;

              /*
               * Saving the alignment if it is qualitative
               */
              if(p_scenario_buffer->score > min_max_score)
                {
                  min_max_score = interpretation_saver.tryAddLocation(
                    p_scenario_buffer->beginning,
                    row_number + SURPLUS +
                      (int)(transformed_spectrum.getLastShift(best_column) /
                            SMALLEST_MASS),
                    p_scenario_buffer->score,
                    p_scenario_buffer->tree_id);

                  // qDebug() << "row " << row_number
                  // //          << " min_max_score:" << min_max_score;
                  // for(auto &location : interpretation_saver.getLocations())
                  //   {
                  //     qDebug() << location.toString();
                  //   }
                }

              // qDebug() << "preliminary treatment pa3a2";
            }

          qDebug() << "preliminary treatment p3";
          if(MAX_REALIGNMENT_SIZE >= 2)
            {
              /*
               * Updating current_masses for the next row
               */
              double row_number_mass = current_aa.mass;
              for(int i = MAX_REALIGNMENT_SIZE - 1; i >= 1; --i)
                {
                  current_masses[i] = current_masses[i - 1] + row_number_mass;
                }
              current_masses[0] = row_number_mass;
            }
        }
      catch(const std::exception &std_err)
        {
          throw pappso::PappsoException(
            QObject::tr("preliminaryTreatmentV1 failed at row number %1 with "
                        "std error:\n%2")
              .arg(row_number)
              .arg(std_err.what()));
        }
    } // iterate on protein sequence

  qDebug() << "end premilimary treatment";
  return interpretation_saver.getLocations();
}


std::shared_ptr<Alignment>
Protein::finalTreatment(
  std::shared_ptr<const TransformedSpectrum> transformed_spectrum_sp,
  const LocationBackup &location_backup,
  bool force_no_cleaning) const
{

  std::shared_ptr<Alignment> alignment_sp = nullptr;
  if(!transformed_spectrum_sp.get()->isUsefull())
    {
      return alignment_sp;
    }
  if(transformed_spectrum_sp.get()->getAminoAcidsPositions().size() < 18)
    {
      throw pappso::PappsoException(
        QObject::tr("ERROR: "
                    "transformed_spectrum_sp.get()->getAminoAcidsPositions()."
                    "size()=%1 isUsefull:%2")
          .arg(transformed_spectrum_sp.get()->getAminoAcidsPositions().size())
          .arg(transformed_spectrum_sp.get()->isUsefull()));
    }

  bool b_SHUT_DOWN_PEAKS_CLEANING(true);
  if(!force_no_cleaning)
    {
      b_SHUT_DOWN_PEAKS_CLEANING =
        pappso::spoms::SpomsUtils::GetInstance()
          ->getParameterValue("post_treatment", "shut_down_peaks_cleaning")
          .toBool();
    }
  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();
  bool b_SHUT_DOWN_NON_ALIGNED_MASS(true);
  // this.current_peak_mask            = first_peak_mask;
  qDebug() << "location " << location_backup.toString();
  Location location(
    m_sequence, location_backup.start_line - 1, location_backup.end_line - 1);
  // this.expandMatriceHeight(this.current_location.getLength() + 1);

  qDebug() << location.toString();
  // qWarning() << "p 665";
  /*
   * The final treatment correspond to the alignment on the current
   * location with the second system of score. The result is store in
   * this.best_interpretation_backup and in this.scenarios_matrice
   */
  std::vector<std::vector<Scenario>> scenarios_matrice;
  InterpretationBackup best_interpretation_backup = preciseAlignment(
    *transformed_spectrum_sp.get(), location, scenarios_matrice);


  //[Thread-0] INFO main.Main - 15 52 73
  // 16   1   -8
  qDebug() << best_interpretation_backup.getColumnNumber() << " "
           << best_interpretation_backup.getLineNumber() << " "
           << best_interpretation_backup.getScore();
  /*
   * The verification that the alignment has a high enough score.
   */
  std::shared_ptr<Alignment> post_processed_alignment_sp = nullptr;
  if(best_interpretation_backup.getScore() > MIN_SCENARIO_SCORE &&
     best_interpretation_backup.getLineNumber() > 0)
    {

      // qWarning() << "p 690";
      /*
    this.current_alignment =
      this.alignments_bucket[NB_INTERPRETATIONS_SAVED + 1];
    this.current_alignment.overwrite(this.best_interpretation_backup,
                                     this.current_transformed_spectrum,
                                     this.scenarios_matrice,
                                     this.current_location);
*/
      alignment_sp = std::make_shared<Alignment>(best_interpretation_backup,
                                                 transformed_spectrum_sp,
                                                 scenarios_matrice,
                                                 location_backup);

      qDebug() << alignment_sp.get()->computeHitModifiedAndRealSequence().join(
        " ");
      if(!b_SHUT_DOWN_PEAKS_CLEANING)
        {
          /*
           * A correction can be apply to the alignment if it rely several times
           * on the same peak in the spectrum.
           */
          std::pair<std::shared_ptr<Alignment>, bool> clean_peaks_return =
            transformed_spectrum_sp.get()->cleanPeaksUsedSeveralTimes(
              *this, location_backup, alignment_sp.get()->getScenarios());
          if(clean_peaks_return.second)
            {
              alignment_sp = clean_peaks_return.first;
            }
        }
      qDebug() << "if(!b_SHUT_DOWN_NON_ALIGNED_MASS)";
      if(!b_SHUT_DOWN_NON_ALIGNED_MASS)
        {
          if(alignment_sp != nullptr)
            {
              /*
               * We generate all the masses that can possibly correspond to a
               * non-aligned mass and for each of them, a new transformed
               * spectrum is constructed. A new alignment is then performed on
               * them and the current location and the best one is keep.
               */
              std::vector<double> possible_non_aligned_masses =
                alignment_sp.get()->computePossibleNonAlignedMasses();
              AlignmentBucket alignments_bucket(NB_INTERPRETATIONS_SAVED);

              if(possible_non_aligned_masses.size() > 0)
                {
                  for(double non_aligned_mass : possible_non_aligned_masses)
                    {
                      transformed_spectrum_sp.get()->postProcessOneMass(
                        *this,
                        location_backup,
                        transformed_spectrum_sp.get()->getPeakMask(),
                        non_aligned_mass,
                        alignments_bucket);
                    }

                  /*
                   * If the new alignment is better than the original, the new
                   * one is also saved in this.interpretation_saver.
                   */
                  if(alignments_bucket.getBestAlignmentScore() >
                     MIN_SCENARIO_SCORE)
                    {
                      if(alignment_sp->compareToPostProcessing(
                           *(alignments_bucket.getBestAlignment().get())))
                        {
                          post_processed_alignment_sp =
                            alignments_bucket.getBestAlignment();
                        }
                    }

                  // current_non_aligned_mass = 0.0;
                }
            }
        }

      /*
       * Only the best alignment is kept. The best is the one with the best
       * score and with the most peaks in common in case of a tie.
       */
      /*
     if(this.current_alignment.compareTo(
          this.alignments_bucket[NB_INTERPRETATIONS_SAVED]) == -1)
       {
         this.alignments_bucket[NB_INTERPRETATIONS_SAVED + 1] =
           this.alignments_bucket[NB_INTERPRETATIONS_SAVED];
         this.alignments_bucket[NB_INTERPRETATIONS_SAVED] =
           this.current_alignment;
         this.transformed_spectra_bucket[NB_INTERPRETATIONS_SAVED + 1] =
           this.transformed_spectra_bucket[NB_INTERPRETATIONS_SAVED];
         this.transformed_spectra_bucket[NB_INTERPRETATIONS_SAVED] =
           this.current_transformed_spectrum;
       }
       */
    }
  qDebug() << " end final treatment";

  // qWarning() << "p 798";
  return alignment_sp;
}


/**
 * The method performing the second alignment of the current spectrum and all
 * the bests LssSM. At the end of the call of this method, the
 * best_interpretation_backup contains the best alignment for the current LssSM.
 */
InterpretationBackup
Protein::preciseAlignment(
  const TransformedSpectrum &transformed_spectrum,
  const Location &location,
  std::vector<std::vector<Scenario>> &scenarios_matrice) const
{
  qDebug() << location.toString();

  int sequence_length;
  int row_number;
  SpomsAa aa;
  int current_temp_scenario;
  /*
  AAPosition[][] all_aa_positions =
    this.current_transformed_spectrum.getAminoAcidsPositions();
  int[] all_aa_positions_number =
    this.current_transformed_spectrum.getAminoAcidsPositionsNumber();
  AAPosition[] aa_positions;
  */
  // int aa_positions_length;
  // int aa_position_number;
  AAPosition position;
  int current_score_shift, current_score_found;
  int realignment_length;
  double current_shift;
  int best_column;
  int best_score;
  int current_score;
  int column_count = transformed_spectrum.getColumnCount();


  std::vector<InterestCell> interest_cells(column_count);

  std::vector<InterestCell> scenario_buffer(column_count);


  interest_cells[0].score = 0;

  InterpretationBackup best_interpretation_backup(
    SpomsUtils::GetInstance()
      ->getParameterValue("alignment", "min_scenario_score")
      .toInt(10));

  int initialisation_score_post =
    SpomsUtils::GetInstance()->getInitialisationScorePost();
  QJsonObject score_post =
    SpomsUtils::GetInstance()->getPostTreatmentScoreGrid();
  int CERTAINLY_FOUND_WITH_SHIFT_POST =
    score_post.value("certainly_found_with_shift_post").toInt();
  int CERTAINLY_FOUND_POST  = score_post.value("certainly_found_post").toInt();
  int NOT_FOUND_POST        = score_post.value("not_found_post").toInt();
  int FOUND_WITH_SHIFT_POST = score_post.value("found_with_shift_post").toInt();
  int FOUND_POST            = score_post.value("found_post").toInt();
  int MAX_REALIGNMENT_SIZE  = score_post.value("max_realignment_size").toInt();

  try
    {
      /*
       * Initialization of the first row
       */
      for(int j = 1; j < column_count; j++)
        {
          interest_cells[j].row_number = 0;
          interest_cells[j].score      = initialisation_score_post;
        }
      sequence_length = location.getLength();

      std::vector<double> current_masses(sequence_length, 0.0);
      scenarios_matrice.resize(sequence_length + 1);

      qDebug() << sequence_length;
      // assert(scenarios_matrice.size() == 24);
      for(auto &it_reset_scenario : scenarios_matrice)
        {
          it_reset_scenario.resize(column_count + 1);
        }

      qDebug();
      /*
       *  For each amino acid in the current protein sequence
       */
      try
        {
          for(row_number = 0; row_number < sequence_length;)
            {

              interest_cells[0].row_number = row_number;
              aa                           = location.get(row_number);
              // qDebug() << (char)aa.aa;
              ++row_number;
              current_temp_scenario = 0;
              // aa_positions          = all_aa_positions[aa_number];
              // aa_positions_length   = all_aa_positions_number[aa_number];

              // qWarning() << "pr 892 "
              //            <<
              //            transformed_spectrum.getAminoAcidsPositions().size()
              //            << " " << aa.code;
              /*
               * For each position of the current amino acid in the current
               * spectrum.
               */
              for(auto &position :
                  transformed_spectrum.getAminoAcidsPositions().at(aa.code))
                {
                  try
                    {
                      /*
                       * The score of a shift and a found depend on the fact
                       * that the corresponding peak in the spectrum is double
                       * or not.
                       */
                      if(transformed_spectrum.isDoubleColumn(
                           position.getRightColumn()))
                        {
                          current_score_shift = CERTAINLY_FOUND_WITH_SHIFT_POST;
                          current_score_found = CERTAINLY_FOUND_POST;
                        }
                      else
                        {
                          current_score_shift = FOUND_WITH_SHIFT_POST;
                          current_score_found = FOUND_POST;
                        }

                      realignment_length = 0;

                      /*
                       * First possibility :
                       * not found case
                       */
                      best_column =
                        position.getRightColumn(); // current_right_column;
                      best_score =
                        interest_cells[position.getRightColumn()].score +
                        NOT_FOUND_POST *
                          (row_number -
                           interest_cells[position.getRightColumn()]
                             .row_number);

                      // qDebug() << " 1bestscore" << best_score;

                      /*
                       * Second possibility :
                       * found case
                       *
                       * The condition check that the left peak of the current
                       * AAPosition has a column
                       */
                      if((position.getCondition() & 1) == 0)
                        {
                          /*
                           * If it rely on a cell corresponding to a not found
                           * case, the score for the found case correspond to
                           * the score of a shift
                           */
                          if(interest_cells[position.getLeftColumn() + 1]
                               .row_number < row_number - 1)
                            {
                              /*
                               * If the found case follow a shift in the
                               * protein.
                               */
                              current_score =
                                interest_cells[(position.getLeftColumn() + 1)]
                                  .score +
                                NOT_FOUND_POST *
                                  ((row_number - 1) -
                                   interest_cells[(position.getLeftColumn() +
                                                   1)]
                                     .row_number) +
                                current_score_shift;
                              // qDebug() << "cc" << current_score;
                            }
                          else
                            {
                              current_score =
                                interest_cells[(position.getLeftColumn() + 1)]
                                  .score +
                                current_score_found;
                              // qDebug() << "c2c" << current_score;
                            }
                          /*
                           * Saving the new score if it's the new best one
                           */
                          if(current_score >= best_score)
                            {
                              best_score  = current_score;
                              best_column = position.getLeftColumn() + 1;
                            }
                        }
                      /*
                      [Thread-0] INFO main.Main -  row:0 best_column:16 best
                      score:-23 [Thread-0] INFO main.Main -  row:0
                      best_column:43 best score:-12 qDebug() << " row:" <<
                      interest_cells[0].row_number
                               << " best_column:" << best_column
                               << " best score:" << best_score
                               << " position:" << position.toString();
                      */

                      /*
                       * If a shift is possible, i.e. the current left column is
                       * greater than -1
                       */
                      if(position.getLeftColumn() >= 0)
                        {
                          /*
                           * Trying a realignment for each possible shift,
                           * except for the first column
                           */
                          for(int j = 1; j <= position.getLeftColumn(); ++j)
                            {
                              current_shift = transformed_spectrum.getShift(
                                j, position.getRightColumn(), aa.mass);
                              realignment_length =
                                row_number - interest_cells[j].row_number - 2;
                              if(realignment_length >= 0 &&
                                 std::abs(current_masses[realignment_length] -
                                          current_shift) <= m_ACCURACY)
                                {
                                  /*
                                   * The realignment works
                                   */
                                  current_score = interest_cells[j].score +
                                                  current_score_found;
                                }
                              else
                                {
                                  /*
                                   * The realignment doesn't works
                                   */
                                  current_score =
                                    interest_cells[j].score +
                                    NOT_FOUND_POST *
                                      ((row_number - 1) -
                                       interest_cells[j].row_number) +
                                    current_score_shift;
                                }

                              /*
                               * Saving the new score if it's the new best one
                               */
                              if(current_score > best_score)
                                {
                                  best_score  = current_score;
                                  best_column = j;
                                }
                            }

                          /*
                           * Trying the realignment for the column 0
                           */
                          if(current_score_found > best_score)
                            {
                              current_shift = transformed_spectrum.getShift(
                                0, position.getRightColumn(), aa.mass);
                              realignment_length = 0;
                              while(row_number - realignment_length >= 2 &&
                                    current_shift - m_ACCURACY >
                                      current_masses[realignment_length])
                                {
                                  ++realignment_length;
                                }
                              /*
                               * If the realignment works, saving the new score
                               * if it's the new best one
                               */
                              if(row_number - realignment_length >= 2 &&
                                 std::abs(current_shift -
                                          current_masses[realignment_length]) <=
                                   m_ACCURACY)
                                {
                                  ++realignment_length;
                                  best_score  = current_score_found;
                                  best_column = 0;
                                }
                              /*
                               * If the realignment doesn't works, trying a
                               * shift and saving the corresponding score if its
                               * the new best one
                               */
                              else if(current_score_shift > best_score)
                                {
                                  realignment_length = 0;
                                  best_score         = current_score_shift;
                                  best_column        = 0;
                                }
                            }
                        }
                    }
                  catch(const std::exception &std_exception)
                    {
                      throw pappso::PappsoException(
                        QObject::tr("checking positions failed with std "
                                    "exception:\n%1")
                          .arg(std_exception.what()));
                    }
                  // qDebug() << "best_score:" << best_score;

                  // if(interest_cells[0].row_number > 0)
                  //   assert(false);
                  /*
                   * If the best case is not a not found case, it is stored
                   * in the different structures
                   */
                  try
                    {
                      if(best_column != position.getRightColumn())
                        {
                          // qDebug() << "current_temp_scenario:" <<
                          // current_temp_scenario;
                          scenario_buffer[current_temp_scenario].row_number =
                            position.getRightColumn();
                          scenario_buffer[current_temp_scenario].score =
                            best_score;
                          if(best_column == 0)
                            {
                              scenarios_matrice.at(row_number)
                                .at(position.getRightColumn())
                                .update(row_number - 1 - realignment_length,
                                        0,
                                        position);
                            }
                          else
                            {
                              scenarios_matrice.at(row_number)
                                .at(position.getRightColumn())
                                .update(interest_cells[best_column].row_number,
                                        best_column,
                                        position);
                            }
                          current_temp_scenario++;
                        }
                    }
                  catch(const std::exception &std_exception)
                    {
                      throw pappso::PappsoException(
                        QObject::tr("stash scenario failed with std "
                                    "exception:\n%1")
                          .arg(std_exception.what()));
                    }
                }
              // qWarning() << " current_temp_scenario:" <<
              // current_temp_scenario;
              try
                {
                  /*
                   * Saving all the scenarios for the current row
                   */
                  for(int i = 0; i < current_temp_scenario; i++)
                    {
                      best_column = scenario_buffer[i].row_number;
                      interest_cells[best_column].row_number = row_number;
                      interest_cells[best_column].score =
                        scenario_buffer[i].score;

                      /*
                       * Saving the alignment if it the new best one
                       */
                      if(row_number > (int)m_sequence.size())
                        {
                          throw pappso::ExceptionOutOfRange(
                            QObject::tr("row number %1 out of range")
                              .arg(row_number));
                        }
                      best_interpretation_backup.tryImprove(
                        interest_cells[best_column].score,
                        row_number,
                        best_column);
                    }

                  if(MAX_REALIGNMENT_SIZE >= 2)
                    {
                      /*
                       * Updating current_masses for the next row
                       */
                      for(int i = row_number - 1; i >= 1; --i)
                        {
                          current_masses[i] = current_masses[i - 1] + aa.mass;
                        }
                      current_masses[0] = aa.mass;
                    }
                }
              catch(const std::exception &std_exception)
                {
                  throw pappso::PappsoException(
                    QObject::tr("saving scenario failed with std "
                                "exception:\n%1")
                      .arg(std_exception.what()));
                }
              // qDebug() << " at end best column:" << best_column;
            }
        }
      catch(const std::exception &std_exception)
        {
          throw pappso::PappsoException(
            QObject::tr("for each amino acid failed with std "
                        "exception:\n%1")
              .arg(std_exception.what()));
        }
    }
  catch(const std::exception &std_exception)
    {
      throw pappso::PappsoException(
        QObject::tr(
          "precise alignment failed for protein %1 with std exception:\n%2")
          .arg(msp_protein.get()->getAccession())
          .arg(std_exception.what()));
    }
  return best_interpretation_backup;
}


} // namespace spoms
} // namespace pappso
