//
// File: test_hyperscore.cpp
// Created by: Olivier Langella
// Created on: 13/3/2015
//
/*******************************************************************************
 * Copyright (c) 2015 Olivier Langella <Olivier.Langella@moulon.inra.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/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@moulon.inra.fr> - initial API and
 *implementation
 ******************************************************************************/

// make test ARGS="-V -I 3,3"
// ./tests/catch2-only-tests [Protein] -s


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

#include <pappsomspp/core/mzrange.h>
#include <pappsomspp/core/amino_acid/aa.h>
#include <pappsomspp/core/peptide/peptide.h>
#include <pappsomspp/core/peptide/peptidestrparser.h>
#include <pappsomspp/core/protein/protein.h>
#include <pappsomspp/core/protein/enzyme.h>
#include <pappsomspp/core/protein/peptidesizefilter.h>
#include <pappsomspp/core/protein/peptidesemienzyme.h>
#include <pappsomspp/core/protein/peptidebuilder.h>
#include <pappsomspp/core/protein/peptidevariablemodificationbuilder.h>
#include <pappsomspp/core/protein/peptidefixedmodificationbuilder.h>
#include <pappsomspp/core/protein/peptidevariablemodificationreplacement.h>
#include <pappsomspp/core/protein/peptidemodificatorpipeline.h>
#include <pappsomspp/core/protein/proteinintegercode.h>
#include <QDebug>
#include <QtCore>
#include "config.h"


using namespace std;
// using namespace pwiz::msdata;
namespace pappso
{
class DigestionHandler : public EnzymeProductInterface
{
  public:
  void
  setPeptide(std::int8_t sequence_database_id [[maybe_unused]],
             const ProteinSp &protein_sp [[maybe_unused]],
             bool is_decoy [[maybe_unused]],
             const QString &peptide,
             unsigned int start,
             bool is_nter [[maybe_unused]],
             unsigned int missed_cleavage_number [[maybe_unused]],
             bool semi_enzyme [[maybe_unused]]) override
  {
    qDebug() << " " << start << "-" << peptide;
    _peptide_list.append(peptide);
  };

  bool
  contain(const QString &peptide) const
  {
    return _peptide_list.contains(peptide);
  }
  void
  clear()
  {
    _peptide_list.clear();
  }

  std::size_t
  size() const
  {
    return _peptide_list.size();
  };

  private:
  QStringList _peptide_list;
};


class PeptideModHandler : public PeptideModificatorInterface
{
  public:
  void
  setPeptideSp(std::int8_t sequence_database_id [[maybe_unused]],
               const ProteinSp &protein_sp [[maybe_unused]],
               bool is_decoy [[maybe_unused]],
               const PeptideSp &peptide_sp,
               unsigned int start,
               bool is_nter [[maybe_unused]],
               unsigned int missed_cleavage_number [[maybe_unused]],
               bool semi_enzyme [[maybe_unused]]) override
  {
    qDebug() << "PeptideModHandler: " << start << " "
             << peptide_sp.get()->toString();
    _peptide_list.push_back(peptide_sp);
  };

  bool
  contain(const PeptideSp &peptide) const
  {
    return (std::find_if(_peptide_list.begin(),
                         _peptide_list.end(),
                         [peptide](const PeptideSp &peptide_in_list) {
                           return *(peptide_in_list.get()) == *(peptide.get());
                         }) != _peptide_list.end());
  }
  void
  clear()
  {
    _peptide_list.clear();
  }
  size_t
  size()
  {
    return _peptide_list.size();
  }

  private:
  std::vector<PeptideSp> _peptide_list;
};
// AaModificationP carbamido = AaModification::getInstance("MOD:00397");
} // namespace pappso

using namespace pappso;


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

  SECTION("..:: Test Protein ::..", "[Protein]")
  {

    qDebug() << "init test protein";
    std::cout << std::endl << "..:: Test Protein ::.." << std::endl;

    Protein cry1Ac(
      "cry1Ac",
      "MDNNPNINECIPYNCLSNPEVEVLGGERIETGYTPIDISLSLTQFLLSEFVPGAGFVLGLVDIIWGIFGPSQ"
      "WD"
      "AFLVQIEQLINQRIEEFARNQAISRLEGLSNLYQIYAESFREWEADPTNPALREEMRIQFNDMNSALTTAIP"
      "LF"
      "AVQNYQVPLLSVYVQAANLHLSVLRDVSVFGQRWGFDAATINSRYNDLTRLIGNYTDYAVRWYNTGLERVWG"
      "PD"
      "SRDWVRYNQFRRELTLTVLDIVALFPNYDSRRYPIRTVSQLTREIYTNPVLENFDGSFRGSAQGIERSIRSP"
      "HL"
      "MDILNSITIYTDAHRGYYYWSGHQIMASPVGFSGPEFTFPLYGTMGNAAPQQRIVAQLGQGVYRTLSSTLYR"
      "RP"
      "FNIGINNQQLSVLDGTEFAYGTSSNLPSAVYRKSGTVDSLDEIPPQNNNVPPRQGFSHRLSHVSMFRSGFSN"
      "SS"
      "VSIIRAPMFSWIHRSAEFNNIIASDSITQIPAVKGNFLFNGSVISGPGFTGGDLVRLNSSGNNIQNRGYIEV"
      "PI"
      "HFPSTSTRYRVRVRYASVTPIHLNVNWGNSSIFSNTVPATATSLDNLQSSDFGYFESANAFTSSLGNIVGVR"
      "NF"
      "SGTAGVIIDRFEFIPVTATLEAEYNLERAQKAVNALFTSTNQLGLKTNVTDYHIDQVSNLVTYLSDEFCLDE"
      "KR"
      "ELSEKVKHAKRLSDERNLLQDSNFKDINRQPERGWGGSTGITIQGGDDVFKENYVTLSGTFDECYPTYLYQK"
      "ID"
      "ESKLKAFTRYQLRGYIEDSQDLEIYLIRYNAKHETVNVPGTGSLWPLSAQSPIGKCGEPNRCAPHLEWNPDL"
      "DC"
      "SCRDGEKCAHHSHHFSLDIDVGCTDLNEDLGVWVIFKIKTQDGHARLGNLEFLEEKPLVGEALARVKRAEKK"
      "WR"
      "DKREKLEWETNIVYKEAKESVDALFVNSQYDQLQADTNIAMIHAADKRVHSIREAYLPELSVIPGVNAAIFE"
      "EL"
      "EGRIFTAFSLYDARNVIKNGDFNNGLSCWNVKGHVDVEEQNNQRSVLVVPEWEAEVSQEVRVCPGRGYILRV"
      "TA"
      "YKEGYGEGCVTIHEIENNTDELKFSNCVEEEIYPNNTVTCNDYTVNQEEYGGAYTSRNRGYNEAPSVPADYA"
      "SV"
      "YEEKSYTDGRRENPCEFNRGYRDYTPLPVGYVTKELEYFPETDKVWIEIGETEGTFIVDSVELLLMEE");

    std::cout << std::endl << "cry1Ac Mass = " << cry1Ac.getMass() << std::endl;
    MzRange cry1Ac_mass(133248.068195739,
                        PrecisionFactory::getPpmInstance(0.5));
    REQUIRE(cry1Ac_mass.contains(cry1Ac.getMass()));

    // BSA
    qDebug() << "init bsa";
    Protein bsa("BSA",
                "MKWVTFISLLLLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDE"
                "HVKLVNELTEFA"
                "KTCVADESHAGCEKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDL"
                "PKLKPDPNTLCD"
                "EFKADEKKFWGKYLYEIARRHPYFYAPELLYYANKYNGVFQECCQAEDKGACLLPKIETMRE"
                "KVLASSARQRLR"
                "CASIQKFGERALKAWSVARLSQKFPKAEFVEVTKLVTDLTKVHKECCHGDLLECADDRADLA"
                "KYICDNQDTISS"
                "KLKECCDKPLLEKSHCIAEVEKDAIPENLPPLTADFAEDKDVCKNYQEAKDAFLGSFLYEYS"
                "RRHPEYAVSVLL"
                "RLAKEYEATLEECCAKDDPHACYSTVFDKLKHLVDEPQNLIKQNCDQFEKLGEYGFQNALIV"
                "RYTRKVPQVSTP"
                "TLVEVSRSLGKVGTRCCTKPESERMPCTEDYLSLILNRLCVLHEKTPVSEKVTKCCTESLVN"
                "RRPCFSALTPDE"
                "TYVPKAFDEKLFTFHADICTLPDTEKQIKKQTALVELLKHKPKATEEQLKTVMENFVAFVDK"
                "CCAADDKEACFA"
                "VEGPKLVVSTQTALA");
    qDebug() << "init bsa sp";
    ProteinSp protein_sp = bsa.makeProteinSp();
    qDebug() << "init kinase";
    Enzyme kinase;
    qDebug() << "kinase.setMiscleavage(2)";
    kinase.setMiscleavage(2);
    DigestionHandler digestion;
    qDebug() << "kinase.eat(protein_sp,digestion)";
    kinase.eat(0, protein_sp, false, digestion);


    REQUIRE(digestion.size() == 243);

    PeptideSizeFilter peptide_size(7, 35);
    peptide_size.setSink(&digestion);

    qDebug() << " kinase.eat(protein_sp,peptide_size);";
    digestion.clear();
    kinase.eat(0, protein_sp, false, peptide_size);

    REQUIRE(digestion.size() == 191);

    qDebug() << " modification_sink";

    PeptideModHandler modification_sink;


    qDebug() << " fixed_mod_builder";
    PeptideBuilder fixed_mod_builder;
    fixed_mod_builder.setSink(&modification_sink);

    // QChar aa =QChar('C');
    // carbamido :
    QString acc = "MOD:00397";

    qDebug() << " carbamido";
    AaModificationP carbamido = AaModification::getInstance("MOD:00397");

    fixed_mod_builder.addFixedAaModification('C', carbamido);

    PeptideSizeFilter peptide_size2mod(7, 35);
    peptide_size2mod.setSink(&fixed_mod_builder);

    kinase.setMiscleavage(0);


    qDebug() << " kinase.eat(protein_sp,peptide_size2mod);";

    digestion.clear();
    kinase.eat(0, protein_sp, false, peptide_size2mod);
    REQUIRE(digestion.size() == 0);

    qDebug() << " kinase.eat(protein_sp,peptide_size2mod) ; end";

    //
    //     unsigned int i = 0;
    //     do {
    //       i++;
    //     }
    //       while (i < 100000000000000);

    // variable modifications :
    // MOD:00719
    // acc = "MOD:00719";
    AaModificationP met_oxy = AaModification::getInstance("MOD:00719");

    PeptideVariableModificationBuilder var_mod_builder(met_oxy);
    var_mod_builder.addAa('M');
    var_mod_builder.setSink(&modification_sink);

    //  QString accMo = "MOD:00719";
    std::cout << std::endl << "..:: chose ::.." << std::endl;

    PeptideBuilder fixed_mod_builder2;
    fixed_mod_builder2.setSink(&var_mod_builder);
    fixed_mod_builder2.addFixedAaModification('C', carbamido);

    PeptideSizeFilter peptide_size2varmod(7, 35);
    peptide_size2varmod.setSink(&fixed_mod_builder2);


    kinase.setMiscleavage(0);

    qDebug() << " kinase.eat(protein_sp,peptide_size2varmod);";

    ProteinSp fake =
      Protein("fakeBSA", "MVVKKVVVMAMEEKWVTFISLLLLFTKVHKECCVVSTQTALA")
        .makeProteinSp();
    kinase.eat(0, fake, false, peptide_size2varmod);

    digestion.clear();
    std::cout << std::endl
              << "..:: Test trypsin peptides on fake prot ::.." << std::endl;
    kinase.eat(0, fake, false, digestion);

    REQUIRE(digestion.contain("K"));


    REQUIRE(digestion.contain("ECCVVSTQTALA"));

    REQUIRE(digestion.contain("VVVMAMEEK"));

    REQUIRE(digestion.size() == 6);

    // BSA
    std::cout << std::endl
              << "..:: Test semi tryptic peptides on BSA ::.." << std::endl;
    kinase.setMiscleavage(0);
    PeptideSemiEnzyme semi_tryptic;
    semi_tryptic.setSink(&digestion);
    qDebug() << "kinase.eat(protein_sp,semi_tryptic)";
    digestion.clear();
    kinase.eat(0, protein_sp, false, semi_tryptic);


    REQUIRE(digestion.size() == 1132);


    qDebug() << Qt::endl << "..:: Test enzyme motif ::.." << Qt::endl;
    Enzyme motif_digest("(MAMEE[KR])([^P])");
    digestion.clear();
    motif_digest.eat(0, fake, false, digestion);
    REQUIRE(digestion.size() == 2);
    REQUIRE(digestion.contain("MVVKKVVVMAMEEK"));
    REQUIRE(digestion.contain("WVTFISLLLLFTKVHKECCVVSTQTALA"));


    // potential Nter mod +42.01056@[
    // fixed mod mass 57.02146@C
    // 15.99491@M,79.96633@Y
    // 79.96633:-97.9769@[ST!]
    PeptideModificatorPipeline pmp;
    peptide_size2varmod.setSink(&pmp);
    PeptideVariableModificationReplacement mod_replace(
      AaModification::getInstance("MOD:00397"),
      AaModification::getInstance("MOD:00419"));
    QString pattern_str("^E(C)");
    mod_replace.setModificationPattern(pattern_str);

    pmp.setSink(&mod_replace);
    mod_replace.setSink(&modification_sink);

    pmp.addFixedModificationString("MOD:00397@C");
    // fixed: MOD:00397@C,MOD:00696@[YST]
    pmp.addPotentialModificationString(
      "MOD:00719@M,MOD:00696(0-1)@[YST],MOD:00719(1)@M,MOD:00696(1)@[YST]");
    // var: MOD:00719@M
    // MOD:00429@^K
    pmp.addLabeledModificationString("MOD:00429@(^.|K)");
    // MOD:00552@^K
    pmp.addLabeledModificationString("MOD:00552@(^.|K)");
    // MOD:00638@^K
    pmp.addLabeledModificationString("MOD:00638@(^.|K)");
    kinase.eat(0, fake, false, peptide_size2varmod);

    // reverse protein :
    std::cout << std::endl
              << "reverse :"
              << Protein(*fake.get()).reverse().getSequence().toStdString();

    PeptideSp peptide_test = PeptideStrParser::parseString(
      "E(MOD:00429)C(MOD:00419)C(MOD:00397)VVSTQT(MOD:00696,MOD:00696)ALA");
    REQUIRE(
      peptide_test.get()->toString().toStdString() ==
      "E(MOD:00429)C(MOD:00419)C(MOD:00397)VVSTQT(MOD:00696,MOD:00696)ALA");
    REQUIRE(modification_sink.contain(peptide_test));
    std::cout << peptide_test.get()->toString().toStdString() << " is present"
              << std::endl;


    peptide_test =
      PeptideStrParser::parseString("V(MOD:00552)VVMAMEEK(MOD:00429)");
    REQUIRE_FALSE(modification_sink.contain(peptide_test));
    std::cout << peptide_test.get()->toString().toStdString() << " is absent"
              << std::endl;

    peptide_test =
      PeptideStrParser::parseString("V(MOD:00552)VVMAMEEK(MOD:00552)");
    REQUIRE(modification_sink.contain(peptide_test));
    std::cout << peptide_test.get()->toString().toStdString() << " is present"
              << std::endl;


    peptide_test = PeptideStrParser::parseString(
      "E(MOD:00429)C(MOD:00397)C(MOD:00397)VVSTQT(MOD:00696,MOD:00696)ALA");
    REQUIRE(modification_sink.contain(peptide_test));
    std::cout << peptide_test.get()->toString().toStdString() << " is present"
              << std::endl;

    std::cout << std::endl << "..:: test modification motifs ::.." << std::endl;

    ProteinSp prot_test_motif =
      Protein("prot_test_motif", "MVVVVVMAMEEWVTFISLLLLFTVHECCVVSTQTALA")
        .makeProteinSp();
    modification_sink.clear();
    PeptideBuilder pep_builder;
    PeptideFixedModificationBuilder fixed_mod_motif(
      AaModification::getInstance("MOD:00397"));
    pattern_str = "V(V)";
    fixed_mod_motif.setModificationPattern(pattern_str);
    pep_builder.setSink(&fixed_mod_motif);
    fixed_mod_motif.setSink(&modification_sink);
    kinase.eat(0, prot_test_motif, false, pep_builder);

    peptide_test = PeptideStrParser::parseString(
      "MVV(MOD:00397)V(MOD:00397)V(MOD:00397)V(MOD:00397)"
      "MAMEEWVTFISLLLLFTVHECCVV(MOD:00397)STQTALA");
    REQUIRE(modification_sink.contain(peptide_test));
    REQUIRE(modification_sink.size() == 1);
    std::cout << peptide_test.get()->toString().toStdString() << " is present"
              << std::endl;


    qDebug() << "init test protein";
    std::cout << std::endl
              << "..:: Test Protein with B and Z wildcard ::.." << std::endl;

    // BSA
    qDebug() << "init bsa with B and Z wildcard";
    Protein bsa_bz("BSA",
                   "MXXXKBWVTFISLLLLFSSAYSRGVFRRDTHKSBEIAHRFKDLGEEHFKGLVLIAFSQY"
                   "LQQCPFDEHVKLVNE"
                   "LTEFAKTCVADEBSHAGCEKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFL"
                   "SHKDDSPDLPKLKPD"
                   "PNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLYYANKYNGVFQECCQAEDKGACL"
                   "LPKIETMREKVLASS"
                   "ARQRLRCASIQKFGERALKAWSVARLSQKFPKAEFVEVTKLVTDLTKVHKECCHGDLLE"
                   "CADDRADLAKYICDN"
                   "QDTISSKLKECCDKPLLEZKSHCIAEVEKDAIPENLPPLTADFAEDKDVCKNYQEAKDA"
                   "FLGSFLYEYSRRHPE"
                   "YAVSVLLRLAKEYZEATLEECCAKDDPHACYSTVFDKLKHLVDEPQNLIKQNCDQFEKL"
                   "GEYGFQNALIVRYTR"
                   "KVPQVSTPTLVEVSRSLGKVGTRCCTKPESERMPCTEDYLSLILNRLCVLHEKTPVSEK"
                   "VTKCCTESLVNRRPC"
                   "FSALTPDETYVPKAFDEKLFTFHADICTLPDTEKQIKKQTALVELLKHKPKATEEQLKT"
                   "VMENFVAFVDKCCAA"
                   "DDKEACFAVEGPKLVVSTQTALA");
    qDebug() << "init bsa_bz sp";
    ProteinSp protein_bsa_bz_sp = bsa_bz.makeProteinSp();
    qDebug() << "init kinase";
    DigestionHandler digestion_bz;
    qDebug() << "kinase.eat(protein_sp,digestion) B Z";
    kinase.eat(0, protein_bsa_bz_sp, false, digestion_bz);
    REQUIRE_FALSE(digestion_bz.contain("MXXXK"));
    REQUIRE_FALSE(digestion_bz.contain("MAXXK"));
    REQUIRE(digestion_bz.contain("MAAAK"));
    REQUIRE(digestion_bz.contain("SNEIAHR"));
    std::cout << std::endl << " SNEIAHRFK FOUND " << std::endl;
    REQUIRE(digestion_bz.contain("SDEIAHR"));
    std::cout << std::endl << " SDEIAHRFK FOUND " << std::endl;
  }


  SECTION("..:: Test digestion pipeline ::..", "[DigestionPipeline]")
  {

    // potential Nter mod +42.01056@[
    // fixed mod mass 57.02146@C
    // 15.99491@M,79.96633@Y
    // 79.96633:-97.9769@[ST!]
    PeptideModHandler modification_sink;
    PeptideSizeFilter peptide_size2varmod(7, 35);
    PeptideModificatorPipeline pmp;
    peptide_size2varmod.setSink(&pmp);

    pmp.setSink(&modification_sink);

    ProteinSp fake =
      Protein("fakeBSA", "MVVKKVVVMAMEEKWVTFISLLLLFTKVHKECCVVSTQTALA")
        .makeProteinSp();
    Enzyme kinase;
    kinase.eat(0, fake, false, peptide_size2varmod);

    PeptideSp peptide_test = PeptideStrParser::parseString("VVVMAMEEK");
    REQUIRE(modification_sink.contain(peptide_test));
  }
}


TEST_CASE("Test Protein code", "[proteincode]")
{
  // Set the debugging message formatting pattern.
  qSetMessagePattern(QString("%{file}@%{line}, %{function}(): %{message}"));

  SECTION("..:: Test Protein ::..", "[proteincode]")
  {

    ProteinSp fake =
      Protein("fakeBSA", "MVVKKVVVMAMEEKWVTFISLLLLFTKVHKECCVVSTQTALA")
        .makeProteinSp();

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

    AaStringCodec codec(aa_code);


    ProteinIntegerCode protein_code(fake, codec, 5);

    auto code5 = protein_code.getPeptideCodedFragment(5);

    REQUIRE(code5.back() == 338550);

    REQUIRE(codec.decode(code5.back()).toStdString() == "QITAA");

    REQUIRE(code5.size() == fake.get()->size() - 4);

    std::vector<std::uint32_t> test_code_list;
    test_code_list.push_back(codec.codeLlc("MAMEE"));

    test_code_list.push_back(codec.codeLlc("MAME"));

    auto match = protein_code.match(test_code_list);

    REQUIRE(match.back().first == 5);

    REQUIRE(match.back().second == 8);

    test_code_list.clear();
    test_code_list.push_back(codec.codeLlc("MV"));

    match = protein_code.match(test_code_list);

    REQUIRE(match.front().first == 2);
    REQUIRE(match.front().second == 0);

    REQUIRE(match.back().first == 2);
    REQUIRE(match.back().second == 7);
  }
}
