//
// File: test_c13n15.cpp
// Created by: Olivier Langella
// Created on: 12/7/2023
//
/*******************************************************************************
 * Copyright (c) 2023 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/catch2-only-tests [C13N15] -s

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

#include <QDebug>
#include <QString>
#include <pappsomspp/core/mzrange.h>
#include <pappsomspp/core/amino_acid/aa.h>
#include <pappsomspp/core/amino_acid/aacode.h>
#include <pappsomspp/core/amino_acid/aastringcodec.h>
#include <pappsomspp/core/peptide/peptideproformaparser.h>
#include <pappsomspp/core/peptide/peptide.h>
#include <pappsomspp/core/peptide/peptidestrparser.h>
#include <iostream>
#include <pappsomspp/core/obo/obopsimod.h>
#include <pappsomspp/core/obo/filterobopsimodtermlabel.h>
#include <pappsomspp/core/obo/filterobopsimodsink.h>
#include <pappsomspp/core/obo/filterobopsimodtermdiffmono.h>
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include <pappsomspp/core/exception/exceptionoutofrange.h>

using namespace pappso;
using namespace std;


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

  SECTION("..:: AA check C13 N15 amino acid composition ::..", "[C13N15]")
  {

    Aa lysine('K');

    REQUIRE(lysine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(lysine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 2);

    REQUIRE(lysine.getMass() == Catch::Approx(128.09496301));

    AaModificationP k_c13n15 = AaModification::getInstance("C13N15:K");
    lysine.addAaModification(k_c13n15);

    REQUIRE(lysine.getNumberOfIsotope(Enums::Isotope::C13) == 6);
    REQUIRE(lysine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(lysine.getNumberOfIsotope(Enums::Isotope::N15) == 2);
    REQUIRE(lysine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 2);

    REQUIRE(lysine.getMass() == Catch::Approx(136.1091618297));

    Aa lysine_2('K');
    pappso::ChemicalFormula formula(lysine_2);
    formula.setFullIsotope(Enums::Isotope::C13);
    formula.setFullIsotope(Enums::Isotope::N15);
    REQUIRE(formula.getMass() == Catch::Approx(136.1091618297));
    REQUIRE(formula.getNumberOfIsotope(Enums::Isotope::P) == 0);
    REQUIRE(formula.getNumberOfIsotope(Enums::Isotope::C13) == 6);
    REQUIRE(formula.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(formula.getNumberOfIsotope(Enums::Isotope::N15) == 2);
    REQUIRE(formula.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 2);


    Aa aspartic_acid('D');

    REQUIRE(aspartic_acid.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 4);
    REQUIRE(aspartic_acid.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP d_c13n15 = AaModification::getInstance("C13N15:D");
    aspartic_acid.addAaModification(d_c13n15);

    REQUIRE(aspartic_acid.getNumberOfIsotope(Enums::Isotope::C13) == 4);
    REQUIRE(aspartic_acid.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 4);
    REQUIRE(aspartic_acid.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(aspartic_acid.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);


    Aa isoleucine('I');

    REQUIRE(isoleucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(isoleucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP i_c13n15 = AaModification::getInstance("C13N15:I");
    isoleucine.addAaModification(i_c13n15);

    REQUIRE(isoleucine.getNumberOfIsotope(Enums::Isotope::C13) == 6);
    REQUIRE(isoleucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(isoleucine.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(isoleucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);


    Aa leucine('L');

    REQUIRE(leucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(leucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP l_c13n15 = AaModification::getInstance("C13N15:L");
    leucine.addAaModification(l_c13n15);

    REQUIRE(leucine.getNumberOfIsotope(Enums::Isotope::C13) == 6);
    REQUIRE(leucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 6);
    REQUIRE(leucine.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(leucine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    Aa methionine('M');

    REQUIRE(methionine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(methionine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP m_c13n15 = AaModification::getInstance("C13N15:M");
    methionine.addAaModification(m_c13n15);

    REQUIRE(methionine.getNumberOfIsotope(Enums::Isotope::C13) == 5);
    REQUIRE(methionine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(methionine.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(methionine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);


    Aa glutamine('Q');

    REQUIRE(glutamine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(glutamine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 2);

    AaModificationP q_c13n15 = AaModification::getInstance("C13N15:Q");
    glutamine.addAaModification(q_c13n15);

    REQUIRE(glutamine.getNumberOfIsotope(Enums::Isotope::C13) == 5);
    REQUIRE(glutamine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(glutamine.getNumberOfIsotope(Enums::Isotope::N15) == 2);
    REQUIRE(glutamine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 2);


    Aa valine('V');

    REQUIRE(valine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(valine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP v_c13n15 = AaModification::getInstance("C13N15:V");
    valine.addAaModification(v_c13n15);

    REQUIRE(valine.getNumberOfIsotope(Enums::Isotope::C13) == 5);
    REQUIRE(valine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 5);
    REQUIRE(valine.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(valine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    Aa alanine('A');

    REQUIRE(alanine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 3);
    REQUIRE(alanine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);

    AaModificationP a_c13n15 = AaModification::getInstance("C13N15:A");
    alanine.addAaModification(a_c13n15);

    REQUIRE(alanine.toAbsoluteString().toStdString() == "A(C13N15:A)");
    REQUIRE(alanine.getNumberOfIsotope(Enums::Isotope::C13) == 3);
    REQUIRE(alanine.getNumberOfAtom(Enums::AtomIsotopeSurvey::C) == 3);
    REQUIRE(alanine.getNumberOfIsotope(Enums::Isotope::N15) == 1);
    REQUIRE(alanine.getNumberOfAtom(Enums::AtomIsotopeSurvey::N) == 1);
    REQUIRE(alanine.getMass() == Catch::Approx(75.0442131924));
    REQUIRE(a_c13n15->getMass() == Catch::Approx(4.0070994068));

    formula = alanine.getChemicalFormula();
    REQUIRE(formula.getMass() == Catch::Approx(75.0442131924));

    PeptideSp sample = PeptideStrParser::parseString(
      "S(C13N15:S)A(C13N15:A)M(C13N15:M)P(C13N15:P)L(C13N15:L)E(C13N15:E)R("
      "C13N15:R)");

    REQUIRE(sample.get()->getFormula(1).toStdString() == "C 0 (13)C 33 H 59 O 11 N 0 (15)N 10 S 1");


    REQUIRE(sample.get()->getChemicalFormulaCharge(1).toString().toStdString() ==
            "(13)C 33 (15)N 10 H 59 O 11 S 1");

    REQUIRE(sample.get()->toString().toStdString() ==
            "S(C13N15:S)A(C13N15:A)M(C13N15:M)P(C13N15:P)L(C13N15:L)E(C13N15:E)"
            "R(MOD:00587)");
  }


  SECTION("..:: AA init ::..", "[C13N15]")
  {


    Peptide sampler("SAMPLER");

    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:S"),
                                            Enums::AminoAcidChar::serine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:A"),
                                            Enums::AminoAcidChar::alanine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:M"),
                                            Enums::AminoAcidChar::methionine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:P"),
                                            Enums::AminoAcidChar::proline);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:L"),
                                            Enums::AminoAcidChar::leucine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:E"),
                                            Enums::AminoAcidChar::glutamic_acid);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:R"),
                                            Enums::AminoAcidChar::arginine);


    REQUIRE(sampler.toString().toStdString() ==
            "S(C13N15:S)A(C13N15:A)M(C13N15:M)P(C13N15:P)L(C13N15:L)E(C13N15:E)"
            "R(MOD:00587)");


    REQUIRE(sampler.getMass() == Catch::Approx(845.4817824886));

    PeptideSp sampler_C13 = PeptideProFormaParser::parseString("<13C><15N>SAMPLER");
    REQUIRE(sampler_C13.get()->toProForma().toStdString() == "<13C><15N>SAMPLER");
    REQUIRE(sampler_C13.get()->getChemicalFormula().getMass() == Catch::Approx(845.4817824886));
    REQUIRE(sampler_C13.get()->getMass() == Catch::Approx(845.4817824886));
  }


  SECTION("..:: check C13 N15 on MQVILLDK ::..", "[MQVILLDK]")
  {


    Peptide sampler("MQVILLDK");

    REQUIRE(sampler.getMass() == Catch::Approx(958.5521391716));

    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:M"),
                                            Enums::AminoAcidChar::methionine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:Q"),
                                            Enums::AminoAcidChar::glutamine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:V"),
                                            Enums::AminoAcidChar::valine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:I"),
                                            Enums::AminoAcidChar::isoleucine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:L"),
                                            Enums::AminoAcidChar::leucine);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:D"),
                                            Enums::AminoAcidChar::aspartic_acid);
    sampler.addAaModificationOnAllAminoAcid(AaModification::getInstance("C13N15:K"),
                                            Enums::AminoAcidChar::lysine);


    REQUIRE(sampler.toString().toStdString() ==
            "M(C13N15:M)Q(C13N15:Q)V(C13N15:V)I(C13N15:I)L(C13N15:L)L(C13N15:L)"
            "D(C13N15:D)K(MOD:00582)");


    REQUIRE(sampler.getMass() == Catch::Approx(1011.666746131));

    PeptideSp sampler_C13 = PeptideProFormaParser::parseString("<13C><15N>MQVILLDK");
    REQUIRE(sampler_C13.get()->toProForma().toStdString() == "<13C><15N>MQVILLDK");
    REQUIRE(sampler_C13.get()->getChemicalFormula().getMass() == Catch::Approx(1011.666746131));
    REQUIRE(sampler_C13.get()->getMass() == Catch::Approx(1011.666746131));
  }
}
