/*******************************************************************************
 * Copyright (c) 2025 Olivier Langella
 *<Olivier.Langella@universite-paris-saclay.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/


// ./tests/peptidoms/catch2-only-peptidoms [peptidoms] -s


#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>

#include <QString>
#include "../common.h"
#include "tests/tests-config.h"
#include <pappsomspp/core/amino_acid/aastringcodemassmatching.h>
#include <pappsomspp/core/fasta/fastareader.h>
#include <pappsomspp/core/fasta/fastafileindexer.h>
#include <pappsomspp/core/massspectrum/massspectrum.h>
#include <pappsomspp/core/processing/filters/filterresample.h>
#include <pappsomspp/core/processing/filters/filterpass.h>
#include <pappsomspp/core/processing/filters/filterchargedeconvolution.h>
#include <pappsomspp/core/processing/specpeptidoms/correctiontree.h>
#include <pappsomspp/core/processing/specpeptidoms/locationsaver.h>
#include <pappsomspp/core/processing/specpeptidoms/semiglobalalignment.h>
#include <pappsomspp/core/processing/specpeptidoms/spomsspectrum.h>
#include <pappsomspp/core/processing/specpeptidoms/types.h>
#include <pappsomspp/core/protein/protein.h>

class FastaSeq : public pappso::FastaHandlerInterface
{
  public:
  const QString &
  getDescription() const
  {
    return description;
  };
  const QString &
  getSequence() const
  {
    return sequence;
  };
  void
  setSequence(const QString &description_in, const QString &sequence_in) override
  {
    std::cout << std::endl << "begin description=" << description_in.toStdString();
    std::cout << std::endl << "sequence=" << sequence_in.toStdString();
    std::cout << std::endl << "end" << std::endl;
    description = description_in;
    sequence    = sequence_in;
  };

  private:
  QString description;
  QString sequence;
};

TEST_CASE("test for peptidoms alignment.", "[peptidoms]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  SECTION(
    "..:: test sequence diversity "
    "::..",
    "[diversity]")
  {
    REQUIRE(pappso::specpeptidoms::SemiGlobalAlignment::checkSequenceDiversity(
              "EEKKEEVAVERSTAEAAPEEKPEEKPIFPAVKDLI", 5, 2) == true);

    REQUIRE(pappso::specpeptidoms::SemiGlobalAlignment::checkSequenceDiversity("EEK", 5, 2) ==
            false);
    REQUIRE(pappso::specpeptidoms::SemiGlobalAlignment::checkSequenceDiversity("EEKKE", 5, 2) ==
            true);
    REQUIRE(pappso::specpeptidoms::SemiGlobalAlignment::checkSequenceDiversity("AEEEEEK", 5, 2) ==
            false);
  }


  SECTION(
    "..:: BASIC sequence alignment scan_3340 spectrum easy met on fasta file "
    "GKQQVM[MOD:00425]VGYSDSGK "
    "::..",
    "[peptidoms]")
  {

    AaCode aa_code;
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));

    pappso::specpeptidoms::SpOMSProtein protein(
      "protein_id",
      "VERLFSVDWYMDRIKGKQQVMVGYSDSGKDAGRLSAAWQLYRAQEEMAQVAKRYGVKLTLFHGRGGTVG",
      aa_code);

    pappso::PrecisionPtr precision_ptr = pappso::PrecisionFactory::getDaltonInstance(0.02);

    pappso::QualifiedMassSpectrum spectrum_simple = readQualifiedMassSpectrumMgf(
      QString(CMAKE_SOURCE_DIR)
        .append("/tests/data/scans/20120906_balliau_extract_1_A05_urnb-2_scan_3340.mgf"));
    pappso::FilterChargeDeconvolution(precision_ptr)
      .filter(*(spectrum_simple.getMassSpectrumSPtr().get()));
    pappso::FilterResampleKeepGreater(150).filter(*(spectrum_simple.getMassSpectrumSPtr().get()));
    pappso::FilterGreatestY(120).filter(*(spectrum_simple.getMassSpectrumSPtr().get()));


    pappso::specpeptidoms::SpOMSSpectrum experimental_spectrum(
      spectrum_simple, precision_ptr, aa_code);


    pappso::specpeptidoms::ScoreValues score_values;

    pappso::specpeptidoms::SemiGlobalAlignment semi_global_alignment(
      score_values, precision_ptr, aa_code);

    semi_global_alignment.fastAlign(experimental_spectrum, &protein); // 1er alignement
    std::vector<pappso::specpeptidoms::Location> locations =
      semi_global_alignment.getLocationSaver()
        .getLocations(); // On récupère les sous-séquences intéressantes pour un alignement plus
                         // précis.
    REQUIRE(locations.size() == 1);
    REQUIRE(locations.at(0).score == 27);
    REQUIRE(locations.at(0).beginning == 35);
    REQUIRE(locations.at(0).length == 32);
    REQUIRE(locations.at(0).getPeptideString().toStdString() == "RLFSVDWYMDRIKGKQQVMVGYSDSGKDAGRL");
    for(auto loc : locations)
      {
        semi_global_alignment.preciseAlign(experimental_spectrum,
                                           &protein,
                                           loc.beginning,
                                           loc.length); // 2e alignement
        pappso::specpeptidoms::Alignment best_alignment =
          semi_global_alignment.getBestAlignment(); // On récupère le meilleur alignement.

        REQUIRE(best_alignment.getNonAlignedMass() == Catch::Approx(872.358934433));
        REQUIRE(best_alignment.getPeptideString(protein.getSequence()).toStdString() == "VGYSD");
        REQUIRE(best_alignment.m_peptideModel.toInterpretation().toStdString() ==
                "[687.359]VGYSDSGK");

        REQUIRE(best_alignment.m_peptideModel.toProForma().toStdString() == "[+687.3589]-VGYSDSGK");
        REQUIRE(best_alignment.SPC == 6);
        REQUIRE_THAT(
          best_alignment.peaks,
          Catch::Matchers::Approx(std::vector<std::size_t>({0, 20, 31, 40, 49, 55, 64})));
        REQUIRE_THAT(best_alignment.shifts,
                     Catch::Matchers::Approx(std::vector<double>({})).margin(0.001));
        REQUIRE(best_alignment.score == 34);
        REQUIRE(best_alignment.beginning == 21);
        REQUIRE(best_alignment.end == 28);
        REQUIRE(protein.getSequence()
                  .mid(best_alignment.beginning, best_alignment.end - best_alignment.beginning)
                  .toStdString() == "VGYSDSGK");
        REQUIRE(best_alignment.begin_shift == 0);
        REQUIRE(best_alignment.end_shift == Catch::Approx(687.3589344331));
        REQUIRE((best_alignment.begin_shift > 0 || best_alignment.end_shift > 0 ||
                 best_alignment.shifts.size() > 0) == true);

        // JAVA result nbSelectedPeaks= 120
        // 20120906_balliau_extract_1_A05_urnb-2.3340.3340.3_GKQQVM[MOD:00719]VGYSDSGK;-1;0;YGVTGGP;[782,788];[492,22]YGVT[357,17]GGP;34;9;;;;;;;

        if(best_alignment.begin_shift > 0 || best_alignment.end_shift > 0 ||
           best_alignment.shifts.size() > 0) // Si il y a des potentielles erreurs de masse parente,
                                             // on effectue un post-processing.
          {
            std::vector<double> potential_mass_errors =
              semi_global_alignment.getPotentialMassErrors(
                aa_code, best_alignment, protein.getSequence());

            REQUIRE_THAT(potential_mass_errors,
                         Catch::Matchers::Approx(std::vector<double>({687.3589344331,
                                                                      556.3184493445,
                                                                      457.25003543,
                                                                      329.1914579233,
                                                                      201.1328804167,
                                                                      73.0379174006,
                                                                      16.0164536795})));
            semi_global_alignment.postProcessingAlign(
              experimental_spectrum, &protein, loc.beginning, loc.length, potential_mass_errors);
            pappso::specpeptidoms::Alignment best_post_processed_alignment =
              semi_global_alignment
                .getBestAlignment(); // /!\ L'alignement en post-processing écrase le meilleur
                                     // alignement enregistré dans SemiGlobalAlignment

            REQUIRE_THAT(best_post_processed_alignment.shifts,
                         Catch::Matchers::Approx(std::vector<double>({})));
            REQUIRE(best_post_processed_alignment.m_peptideModel.toInterpretation().toStdString() ==
                    "[G][K][Q][Q][V]MVGYSDSGK");

            REQUIRE(best_post_processed_alignment.getNonAlignedMass() == Catch::Approx(0));

            potential_mass_errors = semi_global_alignment.getPotentialMassErrors(
              aa_code, best_post_processed_alignment, protein.getSequence());

            REQUIRE_THAT(potential_mass_errors, Catch::Matchers::Approx(std::vector<double>({})));
          }
      }
  }

  SECTION(
    "..:: BASIC sequence alignment scan_15780 spectrum easy met on fasta file AIADGSLLDLLR "
    "::..",
    "[peptidoms]")
  {

    AaCode aa_code;
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));

    pappso::specpeptidoms::SpOMSProtein protein(
      "protein_id",
      "SGVSEISAESSFTSIEEFLEPLELCYKSLCDCGDKAIADGSLLDLLRQVFTFGLSLVKLDIRQESERH",
      aa_code);

    pappso::PrecisionPtr precision_ptr = pappso::PrecisionFactory::getDaltonInstance(0.02);

    pappso::QualifiedMassSpectrum spectrum_simple = readQualifiedMassSpectrumMgf(
      QString(CMAKE_SOURCE_DIR)
        .append("/tests/data/scans/20120906_balliau_extract_1_A01_urnb-1_15780.mgf"));
    pappso::FilterChargeDeconvolution(precision_ptr)
      .filter(*(spectrum_simple.getMassSpectrumSPtr().get()));
    pappso::FilterResampleKeepGreater(150).filter(*(spectrum_simple.getMassSpectrumSPtr().get()));
    pappso::FilterGreatestY(120).filter(*(spectrum_simple.getMassSpectrumSPtr().get()));


    pappso::specpeptidoms::SpOMSSpectrum experimental_spectrum(
      spectrum_simple, precision_ptr, aa_code);


    pappso::specpeptidoms::ScoreValues score_values;

    pappso::specpeptidoms::SemiGlobalAlignment semi_global_alignment(
      score_values, precision_ptr, aa_code);

    semi_global_alignment.fastAlign(experimental_spectrum, &protein); // 1er alignement
    std::vector<pappso::specpeptidoms::Location> locations =
      semi_global_alignment.getLocationSaver()
        .getLocations(); // On récupère les sous-séquences intéressantes pour un alignement plus
                         // précis.
    REQUIRE(locations.size() == 1);
    REQUIRE(locations.at(0).score == 33);
    REQUIRE(locations.at(0).beginning == 13);
    REQUIRE(locations.at(0).length == 28);
    for(auto loc : locations)
      {
        semi_global_alignment.preciseAlign(experimental_spectrum,
                                           &protein,
                                           loc.beginning,
                                           loc.length); // 2e alignement
        pappso::specpeptidoms::Alignment best_alignment =
          semi_global_alignment.getBestAlignment(); // On récupère le meilleur alignement.

        REQUIRE(best_alignment.m_peptideModel.toInterpretation().toStdString() ==
                "[A][I]ADGSLLD[382.27]");
        REQUIRE(best_alignment.SPC == 9);
        REQUIRE_THAT(
          best_alignment.peaks,
          Catch::Matchers::Approx(std::vector<std::size_t>({0, 35, 37, 40, 41, 43, 47, 71})));
        REQUIRE_THAT(best_alignment.shifts,
                     Catch::Matchers::Approx(std::vector<double>({382.27})).margin(0.001));
        REQUIRE(best_alignment.score == 40);
        REQUIRE(best_alignment.beginning == 35);
        REQUIRE(best_alignment.end == 43);
        REQUIRE(best_alignment.begin_shift == 0.0);
        REQUIRE(best_alignment.end_shift == 0);
        qDebug() << best_alignment.begin_shift << best_alignment.end_shift
                 << best_alignment.shifts.size();
        REQUIRE((best_alignment.begin_shift > 0 || best_alignment.end_shift > 0
                 //  || best_alignment.shifts.size() > 0
                 ) == false);

        REQUIRE(best_alignment.getNonAlignedMass() == Catch::Approx(382.0));
      }
  }

  SECTION("..:: ::..", "[peptidoms]")
  {

    AaCode aa_code;

    REQUIRE(aa_code.getAa('C').getMass() == Catch::Approx(103.0091849597));
    REQUIRE(aa_code.getAaCode('C') == 7);

    REQUIRE(aa_code.getAa((uint8_t)6).getMass() < aa_code.getAa((uint8_t)7).getMass());
    aa_code.addAaModification('C', AaModification::getInstance("MOD:00397"));


    pappso::AaStringCodeMassMatching aaMatching(
      aa_code, 1, pappso::PrecisionFactory::getDaltonInstance(0.25));
    auto code_list = aaMatching.getAaCodeFromMass(150);


    REQUIRE(aa_code.getAaCode('M') == 13);
    REQUIRE(aa_code.getAaCode('A') == 2);
    REQUIRE(aa_code.getAaCode('I') == 7);

    pappso::QualifiedMassSpectrum spectrum_simple = readQualifiedMassSpectrumMgf(
      QString(CMAKE_SOURCE_DIR)
        .append("/tests/data/scans/"
                "20120906_balliau_extract_1_A08_teal-2_scan_3302.mgf"));

    pappso::FilterGreatestY(60).filter(*(spectrum_simple.getMassSpectrumSPtr().get()));

    pappso::PrecisionPtr precision_ptr     = pappso::PrecisionFactory::getDaltonInstance(0.02);
    std::vector<pappso_double> masses      = spectrum_simple.getMassSpectrumSPtr().get()->xValues();
    std::vector<pappso_double> intensities = spectrum_simple.getMassSpectrumSPtr().get()->yValues();
    REQUIRE(masses.size() == intensities.size());

    REQUIRE(spectrum_simple.size() == 60);

    pappso::specpeptidoms::SpOMSSpectrum experimental_spectrum(
      spectrum_simple, precision_ptr, aa_code);

    REQUIRE(spectrum_simple.size() == 60);

    pappso::specpeptidoms::SpOMSSpectrumCsp spectrum_copy =
      std::make_shared<pappso::specpeptidoms::SpOMSSpectrum>(experimental_spectrum);

    REQUIRE(spectrum_simple.size() == 60);

    pappso::specpeptidoms::SpOMSSpectrumCsp spectrum_pp_copy =
      std::make_shared<pappso::specpeptidoms::SpOMSSpectrum>(experimental_spectrum, 50);

    REQUIRE(spectrum_simple.size() == 60);

    pappso::specpeptidoms::LocationSaver location_saver;

    REQUIRE(location_saver.getMinScore(0) == 15);

    REQUIRE(location_saver.getNextTree() == 0);

    location_saver.addLocation(4, 20, 0, 20, nullptr);

    REQUIRE(location_saver.getMinScore(0) == 20);
    REQUIRE(location_saver.getNextTree() == 1);
    REQUIRE(location_saver.getMinScore(1) == 15);

    pappso::specpeptidoms::CorrectionTree correction_tree;
    correction_tree.addPeaks(1, 60);
    correction_tree.addPeaks(3, 57);
    correction_tree.addPeaks(5, 55);
    REQUIRE(correction_tree.getPeaks().size() == 8);

    pappso::specpeptidoms::CorrectionTree empty_correction_tree;
    REQUIRE(empty_correction_tree.getPeaks().size() == 0);

    pappso::specpeptidoms::ScoreValues score_values;

    pappso::specpeptidoms::SemiGlobalAlignment semi_global_alignment(
      score_values, precision_ptr, aa_code);

    FastaSeq seq;

    REQUIRE(1 == 1);
  }
}
