//
// File: test_psm.cpp
// Created by: Olivier Langella
// Created on: 22/1/2018
//
/*******************************************************************************
 * Copyright (c) 2018 Olivier Langella <Olivier.Langella@u-psud.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 warrantyo f
 *     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/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

// make test ARGS="-V -I 18,18"
// ./tests/catch2-only-tests [psm] -s
// ./tests/catch2-only-tests [psmfeature] -s

#include <algorithm>


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

#include <pappsomspp/core/mzrange.h>
#include <pappsomspp/core/psm/peptideisotopespectrummatch.h>
#include <pappsomspp/core/psm/peptidespectrummatch.h>
#include <pappsomspp/core/psm/features/psmfeatures.h>
#include <pappsomspp/core/processing/filters/filterpeakdelta.h>
#include <pappsomspp/core/peptide/peptideproformaparser.h>

#include <iostream>
#include <iomanip>
#include <set>
#include <QDebug>
#include <QString>
#include "common.h"
#include "tests/tests-config.h"

using namespace pappso;
using namespace std;
// using namespace pwiz::msdata;


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

  SECTION("..:: readMgf ::.. GKQQVM[MOD:00425]VGYSDSGK", "[psm]")
  {
    std::cout << std::endl << "..:: readMgf ::.. GKQQVM[MOD:00425]VGYSDSGK" << std::endl;
    // bool refine_spectrum_synthesis = false;

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


    PeptideSp peptide_sp = PeptideProFormaParser::parseString("GKQQVM[MOD:00425]VGYSDSGK");
    // peptide_sp.get()->addAaModification(AaModification::getInstance("MOD:00397"),
    // 0);

    PrecisionPtr precision         = PrecisionFactory::getDaltonInstance(0.02);
    std::list<Enums::PeptideIon> ion_list = pappso::PeptideFragmentIonListBase::getCIDionList();
    std::cout << "spectrum_simple size  " << spectrum_simple.size() << std::endl;
    // REQUIRE(spectrum_simple.size() == 100);

    PeptideIsotopeSpectrumMatch psm(
      *(spectrum_simple.getMassSpectrumSPtr().get()), peptide_sp, 2, precision, ion_list, 2, 0);

    const std::list<PeakIonIsotopeMatch> peak_ion_list = psm.getPeakIonIsotopeMatchList();

    std::vector<int> ion_ms2;
    for(const PeakIonIsotopeMatch &ion_match : peak_ion_list)
      {
        ion_ms2.push_back((int)ion_match.getPeptideIonType());
      }

    REQUIRE_THAT(ion_ms2,
                 Catch::Matchers::Approx(std::vector<int>(
                   {0, 0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  8,  8,  8,  8,  8, 8, 8, 8, 8, 8, 8, 8,
                    8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 3, 3, 3,
                    3, 3, 3, 3, 3, 3, 3, 3,  3,  2,  2,  2,  2,  2,  2,  2,  2, 1, 1, 1, 1})));
  }

  SECTION("..:: readMgf ::..", "[psm]")
  {
    std::cout << std::endl << "..:: readMgf ::.." << std::endl;
    // bool refine_spectrum_synthesis = false;

    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/peaklist_15046_simple_xt.mgf"));
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);


    Peptide peptide("AIADGSLLDLLR");
    PeptideSp peptide_sp(peptide.makePeptideSp());
    // peptide_sp.get()->addAaModification(AaModification::getInstance("MOD:00397"),
    // 0);

    PrecisionPtr precision = PrecisionFactory::getDaltonInstance(0.02);
    std::list<Enums::PeptideIon> ion_list;
    ion_list.push_back(Enums::PeptideIon::y);
    ion_list.push_back(Enums::PeptideIon::b);
    std::cout << "spectrum_simple size  " << spectrum_simple.size() << std::endl;
    REQUIRE(spectrum_simple.size() == 100);

    PeptideSpectrumMatch psm(spectrum_simple, peptide_sp, 2, precision, ion_list);

    REQUIRE(psm.countTotalMatchedIons() == 11);

    writeJsonPsm("psm.json", spectrum_simple, psm);

    PeptideSpectrumMatch psm2(
      spectrum_simple, peptide_sp, 2, precision, PeptideFragmentIonListBase::getCIDionList());

    writeJsonPsm("psm2.json", spectrum_simple, psm2);
    REQUIRE(psm2.countTotalMatchedIons() == 21);

    unsigned int test_count = psm.countTotalMatchedIons();
    REQUIRE(test_count == 11);
    // hyperscore="33.5"
    precision = PrecisionFactory::getDaltonInstance(0.0);
    PeptideSpectrumMatch psmb(spectrum_simple, peptide_sp, 2, precision, ion_list);
    test_count = psmb.countTotalMatchedIons();
    REQUIRE(test_count == 0);


    precision = PrecisionFactory::getDaltonInstance(0.02);
    PeptideIsotopeSpectrumMatch psm_iso(spectrum_simple, peptide_sp, 2, precision, ion_list, 2, 0);
    REQUIRE(psm_iso.getPeakIonIsotopeMatchList().size() == 19);


    writeJsonPsmIsotope("psm_isotope.json", spectrum_simple, psm_iso);


    PeptideIsotopeSpectrumMatch psm_iso2(spectrum_simple, peptide_sp, 2, precision, ion_list, 2, 4);
    REQUIRE(psm_iso2.getPeakIonIsotopeMatchList().size() == 0);

    std::list<PeakIonIsotopeMatch> peak_ion_isotope_match_list =
      psm_iso.getPeakIonIsotopeMatchList();

    for(auto peak : peak_ion_isotope_match_list)
      {
        qDebug() << peak.toString() << " " << peak.getPeptideNaturalIsotopeAverageSp().get();
      }
    REQUIRE(peak_ion_isotope_match_list.size() == 19);

    psm_iso.dropPeaksLackingMonoisotope();
    peak_ion_isotope_match_list = psm_iso.getPeakIonIsotopeMatchList();
    for(auto peak : peak_ion_isotope_match_list)
      {
        qDebug() << peak.toString() << " " << peak.getPeptideNaturalIsotopeAverageSp().get();
      }
    REQUIRE(peak_ion_isotope_match_list.size() == 19);

    qDebug() << peak_ion_isotope_match_list.size();
    peak_ion_isotope_match_list.sort(
      [](const PeakIonIsotopeMatch &a, const PeakIonIsotopeMatch &b) {
        qDebug() << a.getPeak().y << " > ";
        qDebug() << b.getPeak().y << " . ";
        return (a.getPeak().y > b.getPeak().y);
      });


    spectrum_simple = readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/peaklist_15046.mgf"));

    writeJsonTrace("peaklist_15046.json", spectrum_simple);
    peptide_sp = Peptide("AIADGSLLDLLR").makePeptideSp();


    precision = PrecisionFactory::getDaltonInstance(0.02);
    qDebug();
    PeptideIsotopeSpectrumMatch psm_iso_check(
      spectrum_simple, peptide_sp, 2, precision, ion_list, 2, 0);

    qDebug();
    peak_ion_isotope_match_list = psm_iso_check.getPeakIonIsotopeMatchList();

    REQUIRE(peak_ion_isotope_match_list.size() == 39);
    qDebug();

    for(auto &peak : peak_ion_isotope_match_list)
      {
        qDebug() << peak.toString() << " " << peak.getPeptideNaturalIsotopeAverageSp().get();
      }


    peak_ion_isotope_match_list.sort(
      [](const PeakIonIsotopeMatch &a, const PeakIonIsotopeMatch &b) {
        if(a.getPeptideIonType() < b.getPeptideIonType())
          return true;
        if(a.getPeptideFragmentIonSp().get()->size() < b.getPeptideFragmentIonSp().get()->size())
          return true;
        if(a.getCharge() < b.getCharge())
          return true;
        if(a.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeNumber() <
           b.getPeptideNaturalIsotopeAverageSp().get()->getIsotopeNumber())
          return true;
        return false;
      });


    auto it = peak_ion_isotope_match_list.begin();
    REQUIRE(it->toString().toStdString() == "y1+isotope0r1mz175.119");
    it++;
    REQUIRE(it->toString().toStdString() == "y1++isotope2r1mz89.0601");

    for(auto peak : peak_ion_isotope_match_list)
      {
        qDebug() << peak.toString() << " " << peak.getPeptideNaturalIsotopeAverageSp().get();
      }

    psm_iso_check.dropPeaksLackingMonoisotope();
    peak_ion_isotope_match_list = psm_iso_check.getPeakIonIsotopeMatchList();

    for(auto peak : peak_ion_isotope_match_list)
      {
        qDebug() << peak.toString() << " " << peak.getPeptideNaturalIsotopeAverageSp().get();
      }
    REQUIRE(peak_ion_isotope_match_list.size() == 39);
  }
}


TEST_CASE("psm features test suite.", "[psmfeature]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  SECTION("..:: readMgf ::..", "[psmfeature]")
  {
    std::cout << std::endl << "..:: readMgf ::.." << std::endl;
    // bool refine_spectrum_synthesis = false;

    MassSpectrum spectrum_simple =
      readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/scan_2016.mgf"));
    //.applyCutOff(150).takeNmostIntense(100).applyDynamicRange(100);


    Peptide peptide("EDKPQPPPEGR");
    PeptideSp peptide_sp(peptide.makePeptideSp());
    // peptide_sp.get()->addAaModification(AaModification::getInstance("MOD:00397"),
    // 0);

    PsmFeatures features(PrecisionFactory::getDaltonInstance(0.02), 150);

    features.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 3, 1);


    LinearRegression lr = features.getIonIsotopeLinearRegression();
    REQUIRE(lr.getCoefficientOfDetermination() == 0.0);
    REQUIRE(features.getTotalIntensity() == Catch::Approx(248641.8659667977));
    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::b) ==
            Catch::Approx(1751.5864257813).epsilon(0.001));
    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::y) ==
            Catch::Approx(57112.2947998047).epsilon(0.001));


    REQUIRE(features.countMatchedIonComplementPairs() == 0);


    /*
    <group id="15968" mh="1275.731419" z="2" rt="PT2843.58S" expect="1.7e-04"
  label="GRMZM5G815453_P01 P00874 Ribulose bisphosphate carboxylase large chain
  Precursor..." type="model" sumI="6.08" maxI="111642" fI="1116.42" act="0" >
  <protein expect="-191.8" id="15968.1" uid="49237" label="GRMZM5G815453_P01
  P00874 Ribulose bisphosphate carboxylase large chain Precursor..." sumI="8.97"
  > <note label="description">GRMZM5G815453_P01 P00874 Ribulose bisphosphate
  carboxylase large chain Precursor (RuBisCO large subunit)(EC 4.1.1.39)
  seq=translation; coord=Pt:56824..58323:1; parent_transcript=GRMZM5G815453_T01;
  parent_gene=GRMZM5G815453</note> <file type="peptide"
  URL="/gorgone/pappso/formation/TD/Database/Genome_Z_mays_5a.fasta"/> <peptide
  start="1" end="483"> <domain id="15968.1.1" start="347" end="357"
  expect="1.7e-04" mh="1275.7311" delta="0.0003" hyperscore="29.0"
  nextscore="10.8" y_score="10.5" y_ions="8" b_score="3.8" b_ions="1" pre="EGER"
  post="DDFI" seq="EITLGFVDLLR" missed_cleavages="0">
  </domain>
  </peptide>
  */
    spectrum_simple = readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/scan_15968.mgf"));

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

    peptide_sp = Peptide("EITLGFVDLLR").makePeptideSp();

    PsmFeatures features2(PrecisionFactory::getDaltonInstance(0.02), 150);

    features2.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 2, 1);

    REQUIRE(features2.getIonIsotopeLinearRegression().getCoefficientOfDetermination() ==
            Catch::Approx(0.6323680888).epsilon(0.01));

    features.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 2, 1);


    REQUIRE(features.getIonIsotopeLinearRegression().getCoefficientOfDetermination() ==
            Catch::Approx(0.6323680888).epsilon(0.01));
    REQUIRE(features.getMaxIntensityMatchedIonComplementPairPrecursorMassDelta() ==
            Catch::Approx(-0.0036778069).epsilon(0.00001));
    REQUIRE(features.countMatchedIonComplementPairs() == 3);

    REQUIRE(features.getPeakIonPairs().size() == 3);

    REQUIRE(features.getPeakIonPairs()[0].first.toString().toStdString() ==
            "b2+isotope0r1mz243.134");
    REQUIRE(features.getPeakIonPairs()[0].second.toString().toStdString() ==
            "y9+isotope0r1mz1033.61");

    REQUIRE(features.getPeakIonPairs()[1].first.toString().toStdString() ==
            "b6+isotope0r1mz661.367");
    REQUIRE(features.getPeakIonPairs()[1].second.toString().toStdString() ==
            "y5+isotope0r1mz615.383");

    REQUIRE(features.getPeakIonPairs()[2].first.toString().toStdString() ==
            "b7+isotope0r1mz760.435");
    REQUIRE(features.getPeakIonPairs()[2].second.toString().toStdString() ==
            "y4+isotope0r1mz516.315");


    REQUIRE(features.getIonPairPrecursorMassDelta(features.getPeakIonPairs()[0]) ==
            Catch::Approx(-0.0036778069).epsilon(0.001));
    REQUIRE(features.getIonPairPrecursorMassDelta(features.getPeakIonPairs()[1]) ==
            Catch::Approx(-0.0118262069).epsilon(0.001));
    REQUIRE(features.getIonPairPrecursorMassDelta(features.getPeakIonPairs()[2]) ==
            Catch::Approx(-0.0114600069).epsilon(0.001));


    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::y) == 9);
    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::b) == 2);
    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::a) == 0);


    spectrum_simple = readMgf(QString(CMAKE_SOURCE_DIR).append("/tests/data/peaklist_15046.mgf"));

    peptide_sp = Peptide("AIADGSLLDLLR").makePeptideSp();


    features.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 2, 0);


    REQUIRE(features.getIonIsotopeLinearRegression().getCoefficientOfDetermination() ==
            Catch::Approx(0.0).epsilon(0.01));

    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::b) == 3);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::b) == 4);


    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::y) == Catch::Approx(654880.6992189999));
    REQUIRE(features.getMaxIntensityPeakIonMatch(Enums::PeptideIon::y) == Catch::Approx(114514.0625));

    features.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 2, 1);


    REQUIRE(features.getIonIsotopeLinearRegression().getCoefficientOfDetermination() ==
            Catch::Approx(0.4555707503).epsilon(0.04));

    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::b) == 3);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::b) == 4);


    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::y) == Catch::Approx(863068.6967769999));
    REQUIRE(features.getMaxIntensityPeakIonMatch(Enums::PeptideIon::y) == Catch::Approx(114514.0625));

    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::b) == Catch::Approx(254791.342651));
    REQUIRE(features.getMaxIntensityPeakIonMatch(Enums::PeptideIon::b) == Catch::Approx(161016.984375));


    // SeqCoverComplementPeaks
    REQUIRE(features.getComplementPairsAaSequenceCoverage() == 5);
    REQUIRE(((double)features.getComplementPairsAaSequenceCoverage() /
             (double)peptide_sp.get()->size()) == Catch::Approx(5.0 / 12.0));
    REQUIRE(((double)0 / (double)peptide_sp.get()->size()) == Catch::Approx(0));


    REQUIRE((features.getTotalIntensityOfMatchedIonComplementPairs() /
             features.getTotalIntensity()) == Catch::Approx(0.1727291768));

    REQUIRE(
      (features.getTotalIntensityOfMatchedIonComplementPairs() < features.getTotalIntensity()));

    REQUIRE(features.getMaxIntensityMatchedIonComplementPairPrecursorMassDelta() ==
            Catch::Approx(0.0008592852).epsilon(0.00001));


    features.setPeptideSpectrumCharge(peptide_sp, &spectrum_simple, 2, 2);


    REQUIRE(features.getIonIsotopeLinearRegression().getCoefficientOfDetermination() ==
            Catch::Approx(0.4555707503).epsilon(0.04));

    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getMaxConsecutiveIon(Enums::PeptideIon::b) == 3);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::y) == 10);
    REQUIRE(features.getAaSequenceCoverage(Enums::PeptideIon::b) == 4);


    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::y) == Catch::Approx(911305.6467289998));
    REQUIRE(features.getMaxIntensityPeakIonMatch(Enums::PeptideIon::y) == Catch::Approx(114514.0625));

    REQUIRE(features.getIntensityOfMatchedIon(Enums::PeptideIon::b) == Catch::Approx(271127.594604));
    REQUIRE(features.getMaxIntensityPeakIonMatch(Enums::PeptideIon::b) == Catch::Approx(161016.984375));


    // SeqCoverComplementPeaks
    REQUIRE(features.getComplementPairsAaSequenceCoverage() == 5);
    REQUIRE(((double)features.getComplementPairsAaSequenceCoverage() /
             (double)peptide_sp.get()->size()) == Catch::Approx(5.0 / 12.0));
    REQUIRE(((double)0 / (double)peptide_sp.get()->size()) == Catch::Approx(0));


    REQUIRE((features.getTotalIntensityOfMatchedIonComplementPairs() /
             features.getTotalIntensity()) == Catch::Approx(0.1790448148));

    REQUIRE(
      (features.getTotalIntensityOfMatchedIonComplementPairs() < features.getTotalIntensity()));

    REQUIRE(features.getMaxIntensityMatchedIonComplementPairPrecursorMassDelta() ==
            Catch::Approx(0.0008592852).epsilon(0.00001));
  }
}
