/**
 * \file /output/tidd/tidddataoutput.cpp
 * \date 29/9/2022
 * \author Olivier Langella
 * \brief write data in TIDD data table manner
 * (https://pubmed.ncbi.nlm.nih.gov/35354356/)
 */

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


#include "tidddataoutput.h"
#include <pappsomspp/core/psm/xtandem/xtandemhyperscore.h>
#include <cmath>

TiddDataOutput::TiddDataOutput(pappso::UiMonitorInterface *p_monitor,
                               CalcWriterInterface *p_writer,
                               const Project *p_project,
                               pappso::PrecisionPtr ms2precision,
                               double minimumMz)
  : m_psmFeatures(ms2precision, minimumMz), mp_project(p_project)
{

  mp_monitor = p_monitor;
  mp_writer  = p_writer;
  m_peptideEvidenceList.clear();

  mp_monitor->setStatus(
    "collecting peptide evidences (Peptide Spectrum Match) for this "
    "experiment");

  std::vector<const PeptideEvidence *> peptide_evidence_list_target;

  for(IdentificationGroup *identification_group :
      mp_project->getIdentificationGroupList())
    {
      for(auto &p_protein_match : identification_group->getProteinMatchList())
        {
          std::vector<PeptideEvidence *> peptide_evidence_list;
          p_protein_match->collectPeptideEvidences(peptide_evidence_list,
                                                   ValidationState::notValid);

          for(auto &peptide_evidence : peptide_evidence_list)
            {
              m_peptideEvidenceList.push_back(peptide_evidence);
            }

          if(!p_protein_match->getProteinXtpSp().get()->isDecoy())
            {
              peptide_evidence_list_target.insert(
                peptide_evidence_list_target.end(),
                peptide_evidence_list.begin(),
                peptide_evidence_list.end());
            }
        }

      std::sort(m_peptideEvidenceList.begin(), m_peptideEvidenceList.end());
      auto last =
        std::unique(m_peptideEvidenceList.begin(), m_peptideEvidenceList.end());
      m_peptideEvidenceList.erase(last, m_peptideEvidenceList.end());

      std::sort(m_peptideEvidenceList.begin(),
                m_peptideEvidenceList.end(),
                [](const PeptideEvidence *p_a, const PeptideEvidence *p_b) {
                  if(p_a->getMsRunP() == p_b->getMsRunP())
                    return p_a->getMsRunP() < p_b->getMsRunP();
                  return p_a->getUniqueIndex() < p_b->getUniqueIndex();
                });

      std::sort(peptide_evidence_list_target.begin(),
                peptide_evidence_list_target.end());
      auto last2 = std::unique(peptide_evidence_list_target.begin(),
                               peptide_evidence_list_target.end());
      peptide_evidence_list_target.erase(last2,
                                         peptide_evidence_list_target.end());
    }

  if(m_peptideEvidenceList.size() == peptide_evidence_list_target.size())
    {

      throw pappso::PappsoException(QObject::tr(
        "Error : decoy PSM list is empty. Unable to compute TIDD analysis. "
        "Please check that identifications were made using decoy database "
        "options or that decoy regular expression is OK"));
    }

  pappso ::XtandemSpectrumProcess spectrum_process;
  spectrum_process.setMinimumMz(m_minimumMz);
  spectrum_process.setNmostIntense(100);
  spectrum_process.setDynamicRange(100);


  m_ionList.clear();
  m_ionList.push_back(pappso::Enums::PeptideIon::y);
  m_ionList.push_back(pappso::Enums::PeptideIon::b);

  m_ms2precision = pappso::PrecisionFactory::getDaltonInstance(0.02);


  writeHeaders();


  // sort peptide_evidence_list by sample/spectrum index
  sortPeptideEvidenceList();


  mp_monitor->setTotalSteps(m_peptideEvidenceList.size());

  mp_monitor->setStatus(
    QObject::tr(
      "extracting %1 mass spectrum from raw data files and computing features")
      .arg(m_peptideEvidenceList.size()));

  for(auto &peptide_evidence : m_peptideEvidenceList)
    {
      mp_monitor->count();
      writeFeatures(peptide_evidence, spectrum_process);
      if(std::find(peptide_evidence_list_target.begin(),
                   peptide_evidence_list_target.end(),
                   peptide_evidence) != peptide_evidence_list_target.end())
        {
          mp_writer->writeCell("T");
        }
      else
        {
          mp_writer->writeCell("D");
        }
      mp_writer->writeLine();
    }
}

TiddDataOutput::~TiddDataOutput()
{
}

void
TiddDataOutput::sortPeptideEvidenceList()
{
  std::sort(m_peptideEvidenceList.begin(),
            m_peptideEvidenceList.end(),
            [](const PeptideEvidence *a, PeptideEvidence *b) {
              if(a->getMsRunP() < b->getMsRunPtr())
                return true;
              else if(a->getMsRunP() > b->getMsRunPtr())
                return false;
              return (a->getSpectrumIndexByScanNumber() <
                      b->getSpectrumIndexByScanNumber());
            });
}


void
TiddDataOutput::writeHeaders()
{
  // Filename	Scan	Charge	PrecursorM	Peptide	Protein	Title

  mp_writer->writeCell("SampleName");
  mp_writer->writeCell("SpectrumIndex");
  mp_writer->writeCell("Peptide");
  mp_writer->writeCell("PeptideSpecFit");
  mp_writer->writeCell("Charge");
  mp_writer->writeCell("PrecursorM");
  mp_writer->writeCell("PrecursorMZ");
  mp_writer->writeCell("CalMW");
  mp_writer->writeCell("DeltaMass");
  mp_writer->writeCell("AbsDeltaMass");
  mp_writer->writeCell("PeptideLength");
  mp_writer->writeCell("IdentificationEngine");
  // SpecOMS
  mp_writer->writeCell("SpecOmsScore");
  mp_writer->writeCell("SpecFitScore");
  mp_writer->writeCell("SpecOmsMatchType");
  mp_writer->writeCell("SpecOmsCandidateStatus");
  mp_writer->writeCell("TIC");
  mp_writer->writeCell("MaxIntALL");
  mp_writer->writeCell("MaxYionInt");
  mp_writer->writeCell("MaxBionInt");
  mp_writer->writeCell("SumYmatchInt");
  mp_writer->writeCell("SumBmatchInt");
  mp_writer->writeCell("FracYmatchInt");
  mp_writer->writeCell("FracBmatchInt");
  mp_writer->writeCell("SeqCoverYion");
  mp_writer->writeCell("SeqCoverBion");
  mp_writer->writeCell("ConsecutiveYion");
  mp_writer->writeCell("ConsecutiveBion");
  mp_writer->writeCell("MassErrMean");
  mp_writer->writeCell("MassErrSD");
  mp_writer->writeCell("NumofAnnoPeaks");
  mp_writer->writeCell("NumofComplementPeaks");
  mp_writer->writeCell("SumComplementPeaksInt");
  mp_writer->writeCell("FracComplementPeaksInt");
  mp_writer->writeCell("SeqCoverComplementPeaks");
  mp_writer->writeCell("IsotopeRatioVectorSize");
  mp_writer->writeCell("IsotopeRatioCoefficientOfDetermination");
  mp_writer->writeCell("Hyperscore");
  //


  //********************
  mp_writer->writeCell("TorD");
  mp_writer->writeLine();
}


void
TiddDataOutput::writeFeatures(const PeptideEvidence *p_peptide_evidence,
                              pappso::XtandemSpectrumProcess &spectrum_process)
{
  qDebug();
  const PeptideEvidence *p_best_peptide_evidence = p_peptide_evidence;

  if((p_peptide_evidence->getMsRunP() == m_MassSpectrumCache.mp_msrun) &&
     (p_peptide_evidence->getUniqueIndex() ==
      m_MassSpectrumCache.m_spectrum_index))
    {
    }
  else
    {
      m_MassSpectrumCache.m_MassSpectrumCstSPtrCache =
        p_peptide_evidence->getMassSpectrumCstSPtr();
      m_MassSpectrumCache.mp_msrun = p_peptide_evidence->getMsRunP();
      m_MassSpectrumCache.m_spectrum_index =
        p_peptide_evidence->getUniqueIndex();
    }

  pappso::MassSpectrum spectrum = spectrum_process.process(
    *m_MassSpectrumCache.m_MassSpectrumCstSPtrCache.get(),
    p_peptide_evidence->getExperimentalMz(),
    p_peptide_evidence->getCharge());


  std::shared_ptr<pappso::XtandemHyperscore> p_best_hyperscore(
    std::make_shared<pappso::XtandemHyperscore>(
      spectrum,
      p_peptide_evidence->getPeptideXtpSp(),
      p_peptide_evidence->getCharge(),
      m_ms2precision,
      m_ionList,
      true));

  std::vector<pappso::XtandemHyperscore> resultable;
  std::vector<std::shared_ptr<PeptideEvidence>> peptide_evidence_solution_list =
    generatePeptideEvidenceSolutions(p_peptide_evidence);
  for(auto sp_peptide_evidence : peptide_evidence_solution_list)
    {
      qDebug();
      std::shared_ptr<pappso::XtandemHyperscore> p_hyperscore(
        std::make_shared<pappso::XtandemHyperscore>(
          spectrum,
          sp_peptide_evidence->getPeptideXtpSp(),
          sp_peptide_evidence->getCharge(),
          m_ms2precision,
          m_ionList,
          true));

      if(p_hyperscore.get()->getHyperscore() >
         p_best_hyperscore.get()->getHyperscore())
        {
          p_best_hyperscore       = p_hyperscore;
          p_best_peptide_evidence = sp_peptide_evidence.get();
        }
      qDebug();
    }

  qDebug();
  mp_writer->writeCell(
    p_peptide_evidence->getIdentificationDataSource()->getSampleName());
  mp_writer->writeCell(p_peptide_evidence->getSpectrumIndexByScanNumber());
  mp_writer->writeCell(
    p_peptide_evidence->getPeptideXtpSp().get()->getSequence());
  mp_writer->writeCell(
    p_best_peptide_evidence->getPeptideXtpSp().get()->getSequence());

  // Charge
  mp_writer->writeCell((std::size_t)p_best_peptide_evidence->getCharge());
  // PrecursorM
  mp_writer->writeCell(p_peptide_evidence->getExperimentalMhplus());

  // PrecursorMZ
  mp_writer->writeCell(p_peptide_evidence->getExperimentalMz());

  // CalMW
  mp_writer->writeCell(p_peptide_evidence->getTheoreticalMz());
  // DeltaMass
  mp_writer->writeCell(p_peptide_evidence->getDeltaMass());
  // AbsDeltaMass
  mp_writer->writeCell(std::abs(p_peptide_evidence->getDeltaMass()));

  // PeptideLength
  std::size_t peptide_size =
    p_best_peptide_evidence->getPeptideXtpSp().get()->size();

  mp_writer->writeCell(peptide_size);

  // IdentificationEngine
  mp_writer->writeCell(
    (std::int8_t)p_peptide_evidence->getIdentificationEngine());

  mp_writer->writeCell(
    p_peptide_evidence->getParam(PeptideEvidenceParam::deepprot_original_count)
      .toInt());
  mp_writer->writeCell(
    p_peptide_evidence->getParam(PeptideEvidenceParam::deepprot_fitted_count)
      .toInt());
  mp_writer->writeCell(
    p_peptide_evidence->getParam(PeptideEvidenceParam::deepprot_match_type)
      .toInt());
  mp_writer->writeCell(
    p_peptide_evidence
      ->getParam(PeptideEvidenceParam::deepprot_peptide_candidate_status)
      .toInt());


  m_psmFeatures.setPeptideSpectrumCharge(
    p_best_peptide_evidence->getPeptideXtpSp(),
    m_MassSpectrumCache.m_MassSpectrumCstSPtrCache.get(),
    p_best_peptide_evidence->getCharge(),
    2);

  // TIC
  mp_writer->writeCell(std::log(m_psmFeatures.getTotalIntensity()));

  // MaxIntALL
  mp_writer->writeCell(
    checkInf(std::log(m_MassSpectrumCache.m_MassSpectrumCstSPtrCache.get()
                        ->maxIntensityDataPoint()
                        .y)));

  // MaxYionInt
  mp_writer->writeCell(checkInf(std::log(
    m_psmFeatures.getMaxIntensityPeakIonMatch(pappso::Enums::PeptideIon::y))));

  // MaxBionInt
  mp_writer->writeCell(checkInf(std::log(
    m_psmFeatures.getMaxIntensityPeakIonMatch(pappso::Enums::PeptideIon::b))));

  // SumYmatchInt
  mp_writer->writeCell(checkInf(
    std::log(m_psmFeatures.getIntensityOfMatchedIon(pappso::Enums::PeptideIon::y))));
  // SumBmatchInt
  mp_writer->writeCell(checkInf(
    std::log(m_psmFeatures.getIntensityOfMatchedIon(pappso::Enums::PeptideIon::b))));

  // FracYmatchInt
  mp_writer->writeCell(
    checkInf(m_psmFeatures.getIntensityOfMatchedIon(pappso::Enums::PeptideIon::y) /
             m_psmFeatures.getTotalIntensity()));
  // FracBmatchInt
  mp_writer->writeCell(
    checkInf(m_psmFeatures.getIntensityOfMatchedIon(pappso::Enums::PeptideIon::b) /
             m_psmFeatures.getTotalIntensity()));

  // SeqCoverYion
  mp_writer->writeCell(
    (double)m_psmFeatures.getAaSequenceCoverage(pappso::Enums::PeptideIon::y) /
    (double)peptide_size);
  // SeqCoverBion
  mp_writer->writeCell(
    (double)m_psmFeatures.getAaSequenceCoverage(pappso::Enums::PeptideIon::b) /
    (double)peptide_size);


  // ConsecutiveYion
  mp_writer->writeCell(
    (double)m_psmFeatures.getMaxConsecutiveIon(pappso::Enums::PeptideIon::y) /
    (double)peptide_size);
  // ConsecutiveBion
  mp_writer->writeCell(
    (double)m_psmFeatures.getMaxConsecutiveIon(pappso::Enums::PeptideIon::b) /
    (double)peptide_size);

  // MassErrMean
  mp_writer->writeCell(m_psmFeatures.getMatchedMzDiffMean());

  // MassErrSD
  mp_writer->writeCell(m_psmFeatures.getMatchedMzDiffSd());

  // NumofAnnoPeaks
  mp_writer->writeCell(m_psmFeatures.getNumberOfMatchedIons());

  // NumofComplementPeaks
  std::size_t num_of_pairs = m_psmFeatures.countMatchedIonComplementPairs();
  if(num_of_pairs > 0)
    {
      mp_writer->writeCell(num_of_pairs);
      // SumComplementPeaksInt
      mp_writer->writeCell(
        std::log(m_psmFeatures.getTotalIntensityOfMatchedIonComplementPairs()));

      // FracComplementPeaksInt
      mp_writer->writeCell(
        m_psmFeatures.getTotalIntensityOfMatchedIonComplementPairs() /
        m_psmFeatures.getTotalIntensity());
      // SeqCoverComplementPeaks
      mp_writer->writeCell(
        (double)m_psmFeatures.getComplementPairsAaSequenceCoverage() /
        (double)peptide_size);
    }
  else
    {
      mp_writer->writeCell(0);
      // SumComplementPeaksInt
      mp_writer->writeCell(0);
      // FracComplementPeaksInt
      mp_writer->writeCell(0);
      // SeqCoverComplementPeaks
      mp_writer->writeCell(0);
    }

  pappso::LinearRegression lr = m_psmFeatures.getIonIsotopeLinearRegression();

  mp_writer->writeCell(lr.getSize());

  double coeff_of_determination = lr.getCoefficientOfDetermination();
  if(std::isnan(coeff_of_determination))
    {
      mp_writer->writeCell(0);
    }
  else
    {
      mp_writer->writeCell(coeff_of_determination);
    }

  // Hyperscore
  mp_writer->writeCell(p_best_hyperscore->getHyperscore());

  //


  qDebug();
}


std::vector<std::shared_ptr<PeptideEvidence>>
TiddDataOutput::generatePeptideEvidenceSolutions(
  const PeptideEvidence *peptide_evidence) const
{
  std::vector<std::shared_ptr<PeptideEvidence>> solution_list;
  std::vector<std::size_t> position_list;
  QVariant param =
    peptide_evidence->getParam(PeptideEvidenceParam::deepprot_delta_positions);
  if(!param.isNull())
    {
      for(auto pos_str : param.toString().split(" ", Qt::SkipEmptyParts))
        {
          position_list.push_back(pos_str.toUInt());
        }
    }
  if(position_list.size() > 0)
    {
      std::sort(position_list.begin(),
                position_list.end(),
                [](std::size_t a, std::size_t b) { return a < b; });

      pappso::AaModificationP modification =
        pappso::AaModification::getInstanceCustomizedMod(
          peptide_evidence->getDeltaMass());
      qDebug();
      for(std::size_t pos : position_list)
        {
          qDebug();
          PeptideXtpSp peptide_sp =
            PeptideXtp(*(peptide_evidence->getPeptideXtpSp().get()))
              .makePeptideXtpSp();

          qDebug() << peptide_sp.get()->toAbsoluteString();
          peptide_sp.get()->addAaModification(modification, pos);

          qDebug() << peptide_sp.get()->toAbsoluteString();
          PeptideEvidenceSp pe =
            PeptideEvidence(*(peptide_evidence)).makePeptideEvidenceSp();
          pe.get()->setPeptideXtpSp(peptide_sp);

          qDebug();
          solution_list.push_back(pe);
          qDebug() << pe.get()->getHtmlSequence();
        }
    }
  qDebug();
  return solution_list;
}

double
TiddDataOutput::checkInf(double input) const
{
  if(input < 0)
    return 0;
  return input;
}

const std::vector<PeptideEvidence *> &
TiddDataOutput::getPeptideEvidenceList() const
{
  return m_peptideEvidenceList;
}
