//
// 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 2,2"

#include <iostream>

#include <pappsomspp/mzrange.h>
#include <pappsomspp/amino_acid/aa.h>
#include <pappsomspp/peptide/peptide.h>
#include <pappsomspp/peptide/peptidestrparser.h>
#include <pappsomspp/protein/protein.h>
#include <pappsomspp/protein/enzyme.h>
#include <pappsomspp/protein/peptidesizefilter.h>
#include <pappsomspp/protein/peptidesemienzyme.h>
#include <pappsomspp/protein/peptidebuilder.h>
#include <pappsomspp/protein/peptidevariablemodificationbuilder.h>
#include <pappsomspp/protein/peptidefixedmodificationbuilder.h>
#include <pappsomspp/protein/peptidevariablemodificationreplacement.h>
#include <pappsomspp/protein/peptidemodificatorpipeline.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();
  }

  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;

int
main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[])
{
  // QCoreApplication a(argc, argv);

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

  Protein cry1Ac(
    "cry1Ac",
    "MDNNPNINECIPYNCLSNPEVEVLGGERIETGYTPIDISLSLTQFLLSEFVPGAGFVLGLVDIIWGIFGPSQWD"
    "AFLVQIEQLINQRIEEFARNQAISRLEGLSNLYQIYAESFREWEADPTNPALREEMRIQFNDMNSALTTAIPLF"
    "AVQNYQVPLLSVYVQAANLHLSVLRDVSVFGQRWGFDAATINSRYNDLTRLIGNYTDYAVRWYNTGLERVWGPD"
    "SRDWVRYNQFRRELTLTVLDIVALFPNYDSRRYPIRTVSQLTREIYTNPVLENFDGSFRGSAQGIERSIRSPHL"
    "MDILNSITIYTDAHRGYYYWSGHQIMASPVGFSGPEFTFPLYGTMGNAAPQQRIVAQLGQGVYRTLSSTLYRRP"
    "FNIGINNQQLSVLDGTEFAYGTSSNLPSAVYRKSGTVDSLDEIPPQNNNVPPRQGFSHRLSHVSMFRSGFSNSS"
    "VSIIRAPMFSWIHRSAEFNNIIASDSITQIPAVKGNFLFNGSVISGPGFTGGDLVRLNSSGNNIQNRGYIEVPI"
    "HFPSTSTRYRVRVRYASVTPIHLNVNWGNSSIFSNTVPATATSLDNLQSSDFGYFESANAFTSSLGNIVGVRNF"
    "SGTAGVIIDRFEFIPVTATLEAEYNLERAQKAVNALFTSTNQLGLKTNVTDYHIDQVSNLVTYLSDEFCLDEKR"
    "ELSEKVKHAKRLSDERNLLQDSNFKDINRQPERGWGGSTGITIQGGDDVFKENYVTLSGTFDECYPTYLYQKID"
    "ESKLKAFTRYQLRGYIEDSQDLEIYLIRYNAKHETVNVPGTGSLWPLSAQSPIGKCGEPNRCAPHLEWNPDLDC"
    "SCRDGEKCAHHSHHFSLDIDVGCTDLNEDLGVWVIFKIKTQDGHARLGNLEFLEEKPLVGEALARVKRAEKKWR"
    "DKREKLEWETNIVYKEAKESVDALFVNSQYDQLQADTNIAMIHAADKRVHSIREAYLPELSVIPGVNAAIFEEL"
    "EGRIFTAFSLYDARNVIKNGDFNNGLSCWNVKGHVDVEEQNNQRSVLVVPEWEAEVSQEVRVCPGRGYILRVTA"
    "YKEGYGEGCVTIHEIENNTDELKFSNCVEEEIYPNNTVTCNDYTVNQEEYGGAYTSRNRGYNEAPSVPADYASV"
    "YEEKSYTDGRRENPCEFNRGYRDYTPLPVGYVTKELEYFPETDKVWIEIGETEGTFIVDSVELLLMEE");

  cout << endl << "cry1Ac Mass = " << cry1Ac.getMass() << endl;
  MzRange cry1Ac_mass(133248.068195739, PrecisionFactory::getPpmInstance(0.5));
  if(!cry1Ac_mass.contains(cry1Ac.getMass()))
    {
      cerr << "ERROR cry1Ac Mass =" << cry1Ac.getMass()
           << " is not expected 133248.068195739 " << endl;
      return 1;
    }

  // BSA
  qDebug() << "init bsa";
  Protein bsa(
    "BSA",
    "MKWVTFISLLLLFSSAYSRGVFRRDTHKSEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNELTEFA"
    "KTCVADESHAGCEKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPDPNTLCD"
    "EFKADEKKFWGKYLYEIARRHPYFYAPELLYYANKYNGVFQECCQAEDKGACLLPKIETMREKVLASSARQRLR"
    "CASIQKFGERALKAWSVARLSQKFPKAEFVEVTKLVTDLTKVHKECCHGDLLECADDRADLAKYICDNQDTISS"
    "KLKECCDKPLLEKSHCIAEVEKDAIPENLPPLTADFAEDKDVCKNYQEAKDAFLGSFLYEYSRRHPEYAVSVLL"
    "RLAKEYEATLEECCAKDDPHACYSTVFDKLKHLVDEPQNLIKQNCDQFEKLGEYGFQNALIVRYTRKVPQVSTP"
    "TLVEVSRSLGKVGTRCCTKPESERMPCTEDYLSLILNRLCVLHEKTPVSEKVTKCCTESLVNRRPCFSALTPDE"
    "TYVPKAFDEKLFTFHADICTLPDTEKQIKKQTALVELLKHKPKATEEQLKTVMENFVAFVDKCCAADDKEACFA"
    "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);

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

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

  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);";

  kinase.eat(0, protein_sp, false, peptide_size2mod);

  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";
  cout << endl << "..:: chose ::.." << 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();
  cout << endl << "..:: Test trypsin peptides on fake prot ::.." << endl;
  kinase.eat(0, fake, false, digestion);

  if(!digestion.contain("K"))
    {
      cerr << "ERROR digestion for " << fake.get()->getAccession().toStdString()
           << " K NOT FOUND" << endl;
      return 1;
    }


  if(!digestion.contain("ECCVVSTQTALA"))
    {
      cerr << "ERROR digestion for " << fake.get()->getAccession().toStdString()
           << " ECCVVSTQTALA NOT FOUND" << endl;
      return 1;
    }

  if(!digestion.contain("VVVMAMEEK"))
    {
      cerr << "ERROR digestion for " << fake.get()->getAccession().toStdString()
           << " VVVMAMEEK NOT FOUND" << endl;
      return 1;
    }

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


  qDebug() << endl << "..:: Test enzyme motif ::.." << endl;
  Enzyme motif_digest("(MAMEE[KR])([^P])");
  motif_digest.eat(0, fake, false, digestion);


  // 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 :
  cout << 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");
  if(!modification_sink.contain(peptide_test))
    {
      cerr << "ERROR modification_sink for "
           << peptide_test.get()->toString().toStdString() << " NOT FOUND"
           << endl;
      return 1;
    }
  cout << peptide_test.get()->toString().toStdString() << " is present" << endl;


  peptide_test =
    PeptideStrParser::parseString("V(MOD:00552)VVMAMEEK(MOD:00429)");
  if(modification_sink.contain(peptide_test))
    {
      cerr << "ERROR modification_sink for "
           << peptide_test.get()->toString().toStdString() << " FOUND" << endl;
      return 1;
    }
  cout << peptide_test.get()->toString().toStdString() << " is absent" << endl;

  peptide_test =
    PeptideStrParser::parseString("V(MOD:00552)VVMAMEEK(MOD:00552)");
  if(!modification_sink.contain(peptide_test))
    {
      cerr << "ERROR modification_sink for "
           << peptide_test.get()->toString().toStdString() << " NOT FOUND"
           << endl;
      return 1;
    }
  cout << peptide_test.get()->toString().toStdString() << " is present" << endl;


  peptide_test = PeptideStrParser::parseString(
    "E(MOD:00429)C(MOD:00397)C(MOD:00397)VVSTQT(MOD:00696,MOD:00696)ALA");
  if(!modification_sink.contain(peptide_test))
    {
      cerr << "ERROR modification_sink for "
           << peptide_test.get()->toString().toStdString() << " NOT FOUND"
           << endl;
      return 1;
    }
  cout << peptide_test.get()->toString().toStdString() << " is present" << endl;

  cout << endl << "..:: test modification motifs ::.." << 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");
  if(!modification_sink.contain(peptide_test))
    {
      cerr << "ERROR modification_sink for "
           << peptide_test.get()->toString().toStdString() << " NOT FOUND"
           << endl;
      return 1;
    }
  if(modification_sink.size() != 1)
    {
      cerr << "ERROR modification_sink.size() != 1 " << modification_sink.size()
           << endl;
      return 1;
    }
  cout << peptide_test.get()->toString().toStdString() << " is present" << endl;


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

  // BSA
  qDebug() << "init bsa with B and Z wildcard";
  Protein bsa_bz(
    "BSA",
    "MXXXKBWVTFISLLLLFSSAYSRGVFRRDTHKSBEIAHRFKDLGEEHFKGLVLIAFSQYLQQCPFDEHVKLVNE"
    "LTEFAKTCVADEBSHAGCEKSLHTLFGDELCKVASLRETYGDMADCCEKQEPERNECFLSHKDDSPDLPKLKPD"
    "PNTLCDEFKADEKKFWGKYLYEIARRHPYFYAPELLYYANKYNGVFQECCQAEDKGACLLPKIETMREKVLASS"
    "ARQRLRCASIQKFGERALKAWSVARLSQKFPKAEFVEVTKLVTDLTKVHKECCHGDLLECADDRADLAKYICDN"
    "QDTISSKLKECCDKPLLEZKSHCIAEVEKDAIPENLPPLTADFAEDKDVCKNYQEAKDAFLGSFLYEYSRRHPE"
    "YAVSVLLRLAKEYZEATLEECCAKDDPHACYSTVFDKLKHLVDEPQNLIKQNCDQFEKLGEYGFQNALIVRYTR"
    "KVPQVSTPTLVEVSRSLGKVGTRCCTKPESERMPCTEDYLSLILNRLCVLHEKTPVSEKVTKCCTESLVNRRPC"
    "FSALTPDETYVPKAFDEKLFTFHADICTLPDTEKQIKKQTALVELLKHKPKATEEQLKTVMENFVAFVDKCCAA"
    "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);
  if(digestion_bz.contain("MXXXK"))
    {
      cerr << endl
           << "ERROR digestion_bz for "
           << protein_bsa_bz_sp.get()->getAccession().toStdString()
           << " MXXXK FOUND" << endl;
      return 1;
    }
  if(digestion_bz.contain("MAXXK"))
    {
      cerr << endl
           << "ERROR digestion_bz for "
           << protein_bsa_bz_sp.get()->getAccession().toStdString()
           << " MAXXK FOUND" << endl;
      return 1;
    }
  if(!digestion_bz.contain("MAAAK"))
    {
      cerr << endl
           << "ERROR digestion_bz for "
           << protein_bsa_bz_sp.get()->getAccession().toStdString()
           << " MAAAK NOT FOUND" << endl;
      return 1;
    }
  if(!digestion_bz.contain("SNEIAHR"))
    {
      cerr << endl
           << "ERROR digestion_bz for "
           << protein_bsa_bz_sp.get()->getAccession().toStdString()
           << " SNEIAHR NOT FOUND" << endl;
      return 1;
    }
  cout << endl << " SNEIAHRFK FOUND " << endl;
  if(!digestion_bz.contain("SDEIAHR"))
    {
      cerr << endl
           << "ERROR digestion_bz for "
           << protein_bsa_bz_sp.get()->getAccession().toStdString()
           << " SDEIAHR NOT FOUND" << endl;
      return 1;
    }
  cout << endl << " SDEIAHRFK FOUND " << endl;
  return 0;
}
