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


/*
 * SpecGlobTool, Spectra to peptide alignment tool
 * Copyright (C) 2023  Olivier Langella
 * <olivier.langella@universite-paris-saclay.fr>
 *
 * This program is free software: you can redistribute 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 <boost/numeric/ublas/matrix.hpp>
#include "experimentalspectrum.h"
#include "peptidespectrum.h"
#include "scorevalues.h"
#include "peptidemodel.h"
#include "../../exportinmportconfig.h"

using namespace boost::numeric::ublas;


namespace pappso
{
namespace specglob
{
struct SpectralAlignmentDataPoint
{
  double mass_difference;
  int score;
  std::size_t origin_column_indices;
  SpectralAlignmentType alignment_type;
};

/**
 * @todo write docs
 */
class PMSPP_LIB_DECL SpectralAlignment
{
  public:
  /**
   * Default constructor
   */
  SpectralAlignment(ScoreValues score_values,
                    pappso::PrecisionPtr precision_ptr);

  /**
   * Destructor
   */
  ~SpectralAlignment();


  /** @brief build the alignment matrix between a peptide sequence and an
   * experimental spectrum
   *
   * @param peptide_spectrum the peptide sequence
   * @param experimental_spectrum the experimental spectrum
   */
  void align(PeptideSpectraCsp peptide_spectrum,
             ExperimentalSpectrumCsp experimental_spectrum);

  /** @brief trim the current peptide to get a minimal alignment score
   */
  PeptideModel rtrim(PrecisionPtr precision_ptr);

  const matrix<SpectralAlignmentDataPoint> &getMatrix() const;
  std::vector<int> getScoreRow(std::size_t row_indice) const;

  int getMaxScore() const;

  boost::numeric::ublas::matrix<SpectralAlignmentDataPoint>::iterator2
  getMaxPosIterator() const;

  double getPrecursorMzDelta() const;


  /**
   * Backtrack is here to get the HitModified Sequence during the backtrack of
   * score calculation. It start from best score align case in matrix and use
   * origin matrix to go back to the start of the alignment
   *
   * @param row       : the row indices of the best score
   * @param column    : the column indices of the best score
   * @param precision : the precision of measures
   */

  QString backTrack() const;


  PeptideModel buildPeptideModel() const;

  ExperimentalSpectrumCsp getExperimentalSpectrumCsp() const;

  PeptideSpectraCsp getPeptideSpectraCsp() const;

  private:
  const ExperimentalSpectrumDataPoint &getExperimentalSpectrumDataPoint(
    const boost::numeric::ublas::matrix<SpectralAlignmentDataPoint>::iterator2
      &itpos) const;

  void fillMassDelta(const PeptideSpectrum &peptide_spectrum,
                     const ExperimentalSpectrum &experimental_spectrum);


  /**
   * This method do the alignment of the 2 Spectra and fill matrices at actual
   * coordinates i(row - theoretical) and j(column - experimental)
   *
   * @param theoIndiceI : The actual theoretical peak indices
   * @param expeIndiceJ : The actual experimental peak indices
   *
   */
  void fillMatricesWithScores(
    const boost::numeric::ublas::matrix<SpectralAlignmentDataPoint>::iterator2
      &it_pos,
    const PeptideSpectrum &peptide_spectrum,
    const ExperimentalSpectrum &experimental_spectrum);


  /**
   * Method to get a realigned j value of where come from the realignment and
   * get the associated realigned score
   *
   * @param theoIndicesI    : The row where we are actually
   * @param expeIndicesK    : The founded k indices
   * @param expeIndicesJ    : The column where we are actually
   * @param reAlignScore    : Score to add if it is needed to add offset to
   *                        realign
   * @param alignScoreToAdd : Score to add if there is an alignment with peak k
   * @param precision       : Precision of measures
   * @return an int[] where int[0] is the origin column (m) where come from the
   *         re-alignment and int[1] is the calculated re-aligned score and
   * int[3] is the alignment type code
   *
   */
  SpectralAlignmentDataPoint getBestRealignScore(
    const boost::numeric::ublas::matrix<SpectralAlignmentDataPoint>::iterator2
      &it_pos,
    std::size_t expeIndicesK,
    int reAlignScore,
    int alignScoreToAdd);


  private:
  ExperimentalSpectrumCsp mcsp_experimentalSpectrum;
  PeptideSpectraCsp mcsp_peptideSpectrum;
  matrix<SpectralAlignmentDataPoint> m_matrix; // x <= peptide, y <= spectrum
  pappso::PrecisionPtr m_precisionPtr;
  ScoreValues m_scoreValues;
  int m_maxScore;
  boost::numeric::ublas::matrix<SpectralAlignmentDataPoint>::iterator2
    m_itPosMax;

  double m_precursorMassDelta =
    0; // diffference between precursor ion and theoretical peptide ion

  /**
   * modifies the score on the last amino acid alignment (RA score
   * rather than NA
   * Not documented anyMore in the interface
   */

  bool m_BETTER_END_RA = false;
};
} // namespace specglob
} // namespace pappso
