
/*******************************************************************************
 * Copyright (c) 2017 Olivier Langella <olivier.langella@u-psud.fr>.
 *
 * This file is part of XTPcpp.
 *
 *     XTPcpp 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.
 *
 *     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <olivier.langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/

#include "proteinmatch.h"
#include <pappsomspp/grouping/grpprotein.h>
#include <pappsomspp/pappsoexception.h>
#include <set>
#include <cmath>


QColor ProteinMatch::_color_peptide_background             = QColor("yellow");
QColor ProteinMatch::_color_highlighted_peptide_background = QColor("#ff7878");


ProteinMatch::ProteinMatch()
{
}

ProteinMatch::~ProteinMatch()
{
}

ValidationState
ProteinMatch::getValidationState() const
{
  if(isGrouped())
    {
      return ValidationState::grouped;
    }
  else if(isValidAndChecked())
    {
      return ValidationState::validAndChecked;
    }
  else if(isValid())
    {
      return ValidationState::valid;
    }
  return ValidationState::notValid;
}

void
ProteinMatch::updateAutomaticFilters(
  const AutomaticFilterParameters &automatic_filter_parameters)
{
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  _proxy_valid                          = false;
  unsigned int number_of_valid_peptides = 0;
  bool cross_sample =
    automatic_filter_parameters.getFilterCrossSamplePeptideNumber();

  if(cross_sample)
    {
      number_of_valid_peptides =
        countSequenceLi(ValidationState::validAndChecked, nullptr);
    }
  else
    {
      std::set<const MsRun *> msrun_set;
      for(auto &p_peptide_match : _peptide_match_list)
        {
          if(p_peptide_match.getPeptideEvidence()->isValidAndChecked())
            {
              msrun_set.insert(
                p_peptide_match.getPeptideEvidence()->getMsRunP());
            }
        }
      for(const MsRun *p_msrun : msrun_set)
        {
          unsigned int count =
            countSequenceLi(ValidationState::validAndChecked, p_msrun);
          if(count > number_of_valid_peptides)
            {
              number_of_valid_peptides = count;
            }
        }
    }
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  // qDebug() <<"ProteinMatch::updateAutomaticFilters begin 2" ;
  if(number_of_valid_peptides == 0)
    {

      _proxy_valid = false;
    }
  else
    {

      if(number_of_valid_peptides <
         automatic_filter_parameters.getFilterMinimumPeptidePerMatch())
        {
        }
      else
        {
          if(getEvalue() <=
             automatic_filter_parameters.getFilterProteinEvalue())
            {
              _proxy_valid = true;
            }
        }
    }

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
}

const ProteinXtpSp &
ProteinMatch::getProteinXtpSp() const
{
  return _protein_sp;
}

void
ProteinMatch::setProteinXtpSp(ProteinXtpSp protein_sp)
{
  _protein_sp = protein_sp;
}


bool
ProteinMatch::isValid() const
{
  return _proxy_valid;
}
bool
ProteinMatch::isChecked() const
{
  return _checked;
}

bool
ProteinMatch::isValidAndChecked() const
{
  return _proxy_valid && _checked;
}

bool
ProteinMatch::isGrouped() const
{
  if(_sp_grp_protein.get() == nullptr)
    {
      return false;
    }
  if(_sp_grp_protein.get()->getGroupNumber() == 0)
    {
      return false;
    }
  return true;
}

void
ProteinMatch::setChecked(bool arg1)
{
  _checked = arg1;
}

void
ProteinMatch::addPeptideMatch(const PeptideMatch &peptide_match)
{
  // qDebug() << "ProteinMatch::addPeptideMatch begin " <<
  // peptide_match.getPeptideEvidence()->getPeptideXtpSp().get()->toAbsoluteString();
  _peptide_match_list.push_back(peptide_match);
}


std::vector<PeptideMatch> &
ProteinMatch::getPeptideMatchList()
{
  return _peptide_match_list;
}

const std::vector<PeptideMatch> &
ProteinMatch::getPeptideMatchList() const
{
  return _peptide_match_list;
}

void
ProteinMatch::setGroupingExperiment(GroupingExperiment *p_grp_experiment)
{
  _sp_grp_protein = nullptr;
  if((isValidAndChecked() && (!_protein_sp.get()->isDecoy())))
    {
      if(_protein_sp.get()->isContaminant() &&
         (p_grp_experiment->getContaminantRemovalMode() ==
          ContaminantRemovalMode::before))
        {
          // do not group contaminant proteins
        }
      else
        {
          _sp_grp_protein = p_grp_experiment->getGrpProteinSp(this);

          for(auto &p_peptide_match : _peptide_match_list)
            {
              p_peptide_match.getPeptideEvidence()->setGrpPeptideSp(nullptr);
              if(p_peptide_match.getPeptideEvidence()->isValidAndChecked())
                {
                  p_peptide_match.getPeptideEvidence()->setGrpPeptideSp(
                    p_grp_experiment->setGrpPeptide(
                      _sp_grp_protein, p_peptide_match.getPeptideEvidence()));
                }
            }


          if(_protein_sp.get()->isContaminant())
            {
              if(p_grp_experiment->getContaminantRemovalMode() ==
                 ContaminantRemovalMode::groups)
                {
                  p_grp_experiment->addPostGroupingGrpProteinSpRemoval(
                    _sp_grp_protein);
                }
            }
        }
    }
}

const pappso::GrpProteinSp &
ProteinMatch::getGrpProteinSp() const
{
  return _sp_grp_protein;
}


void
ProteinMatch::countPeptideMass(
  std::vector<pappso::GrpPeptide *> &count_peptide_mass,
  ValidationState state) const
{
  for(auto &p_peptide_match : _peptide_match_list)
    {
      if(p_peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          count_peptide_mass.push_back(
            p_peptide_match.getPeptideEvidence()->getGrpPeptideSp().get());
        }
    }
}

void
ProteinMatch::countPeptideMassSample(
  std::vector<size_t> &count_peptide_mass_sample, ValidationState state) const
{
  for(auto &p_peptide_match : _peptide_match_list)
    {
      if(p_peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          count_peptide_mass_sample.push_back(
            p_peptide_match.getPeptideEvidence()->getHashPeptideMassSample());
        }
    }
}
std::vector<PeptideMatch>
ProteinMatch::getPeptideMatchList(ValidationState state,
                                  const MsRun *p_msrun_id) const
{
  std::vector<PeptideMatch> peptide_match_list;
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          if(p_msrun_id == nullptr)
            {
              peptide_match_list.push_back(peptide_match);
            }
          else
            {
              if(peptide_match.getPeptideEvidence()->getMsRunP() == p_msrun_id)
                {
                  peptide_match_list.push_back(peptide_match);
                }
            }
        }
    }
  // it should automatically use the move semantic :
  return peptide_match_list;
}

std::size_t
ProteinMatch::countSampleScan(ValidationState state,
                              const MsRun *p_msrun_id,
                              const Label *p_label) const
{
  std::vector<std::size_t> count_sample_scan;
  for(auto &peptide_match : _peptide_match_list)
    {
      const PeptideEvidence *p_peptide_evidence =
        peptide_match.getPeptideEvidence();
      if(p_peptide_evidence->getValidationState() >= state)
        {
          if(p_msrun_id == nullptr)
            {
              count_sample_scan.push_back(
                p_peptide_evidence->getHashSampleScan());
            }
          else
            {
              if(p_label != nullptr)
                {
                  if((p_peptide_evidence->getMsRunP() == p_msrun_id) &&
                     (p_peptide_evidence->getPeptideXtpSp().get()->getLabel() ==
                      p_label))
                    {
                      count_sample_scan.push_back(
                        p_peptide_evidence->getHashSampleScan());
                    }
                }
              else
                {
                  if(p_peptide_evidence->getMsRunP() == p_msrun_id)
                    {
                      count_sample_scan.push_back(
                        p_peptide_evidence->getHashSampleScan());
                    }
                }
            }
        }
    }
  std::sort(count_sample_scan.begin(), count_sample_scan.end());
  auto last = std::unique(count_sample_scan.begin(), count_sample_scan.end());
  return std::distance(count_sample_scan.begin(), last);
}

std::size_t
ProteinMatch::countPeptideMatch(ValidationState state) const
{
  return std::count_if(
    _peptide_match_list.begin(),
    _peptide_match_list.end(),
    [state](const PeptideMatch &peptide_match) {
      if(peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          return true;
        }
      else
        {
          return false;
        }
    });
}

size_t
ProteinMatch::countSequenceLi(ValidationState state,
                              const MsRun *p_msrun_id,
                              const Label *p_label) const
{
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  std::set<QString> sequence_list;
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          if(p_msrun_id != nullptr)
            {

              if(p_label != nullptr)
                {
                  if((peptide_match.getPeptideEvidence()->getMsRunP() ==
                      p_msrun_id) &&
                     (peptide_match.getPeptideEvidence()
                        ->getPeptideXtpSp()
                        .get()
                        ->getLabel() == p_label))
                    {
                      sequence_list.insert(peptide_match.getPeptideEvidence()
                                             ->getPeptideXtpSp()
                                             .get()
                                             ->getSequenceLi());
                    }
                }
              else
                {
                  // within sample
                  if(peptide_match.getPeptideEvidence()->getMsRunP() ==
                     p_msrun_id)
                    {
                      sequence_list.insert(peptide_match.getPeptideEvidence()
                                             ->getPeptideXtpSp()
                                             .get()
                                             ->getSequenceLi());
                    }
                }
            }
          else
            {
              // overall samples
              sequence_list.insert(peptide_match.getPeptideEvidence()
                                     ->getPeptideXtpSp()
                                     .get()
                                     ->getSequenceLi());
            }
        }
    }
  // qDebug() <<"ProteinMatch::countValidAndCheckedPeptide end " <<
  // sequence_list.size(); qDebug() << __FILE__ << " " << __FUNCTION__ << " " <<
  // __LINE__;
  return sequence_list.size();
}

std::size_t
ProteinMatch::countDistinctMsSamples(ValidationState state) const
{
  std::set<QString> sequence_list;
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          sequence_list.insert(QString("%1").arg(
            peptide_match.getPeptideEvidence()->getMsRunP()->getXmlId()));
        }
    }
  return sequence_list.size();
}

std::size_t
ProteinMatch::countPeptideMassCharge(ValidationState state,
                                     const MsRun *sp_msrun_id,
                                     const Label *p_label) const
{
  std::set<QString> sequence_list;
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->getValidationState() >= state)
        {
          if(sp_msrun_id != nullptr)
            {
              // within sample
              if(peptide_match.getPeptideEvidence()->getMsRunP() == sp_msrun_id)
                {
                  if(p_label != nullptr)
                    {
                      if(peptide_match.getPeptideEvidence()
                           ->getPeptideXtpSp()
                           .get()
                           ->getLabel() == p_label)
                        {
                          sequence_list.insert(
                            QString("%1-%2-%3")
                              .arg(peptide_match.getPeptideEvidence()
                                     ->getPeptideXtpSp()
                                     .get()
                                     ->getNativePeptideP()
                                     ->getSequenceLi())
                              .arg(peptide_match.getPeptideEvidence()
                                     ->getPeptideXtpSp()
                                     .get()
                                     ->getNativePeptideP()
                                     ->getMass())
                              .arg(peptide_match.getPeptideEvidence()
                                     ->getCharge()));
                        }
                    }
                  else
                    {
                      sequence_list.insert(
                        QString("%1-%2-%3")
                          .arg(peptide_match.getPeptideEvidence()
                                 ->getPeptideXtpSp()
                                 .get()
                                 ->getNativePeptideP()
                                 ->getSequenceLi())
                          .arg(peptide_match.getPeptideEvidence()
                                 ->getPeptideXtpSp()
                                 .get()
                                 ->getNativePeptideP()
                                 ->getMass())
                          .arg(
                            peptide_match.getPeptideEvidence()->getCharge()));
                    }
                }
            }
          else
            {
              // overall samples
              sequence_list.insert(
                QString("%1-%2-%3")
                  .arg(peptide_match.getPeptideEvidence()
                         ->getPeptideXtpSp()
                         .get()
                         ->getNativePeptideP()
                         ->getSequenceLi())
                  .arg(peptide_match.getPeptideEvidence()
                         ->getPeptideXtpSp()
                         .get()
                         ->getNativePeptideP()
                         ->getMass())
                  .arg(peptide_match.getPeptideEvidence()->getCharge()));
            }
        }
    }
  return sequence_list.size();
}

pappso::pappso_double
ProteinMatch::getEvalue(const MsRun *sp_msrun_id) const
{
  return (std::pow((double)10.0, getLogEvalue(sp_msrun_id)));
}

pappso::pappso_double
ProteinMatch::getLogEvalue(const MsRun *sp_msrun_id) const
{
  std::map<QString, pappso::pappso_double> map_sequence_evalue;
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->isValidAndChecked())
        {
          QString sequence(peptide_match.getPeptideEvidence()
                             ->getPeptideXtpSp()
                             .get()
                             ->getSequence());
          pappso::pappso_double evalue =
            peptide_match.getPeptideEvidence()->getEvalue();
          if(sp_msrun_id != nullptr)
            {
              // within sample
              if(peptide_match.getPeptideEvidence()->getMsRunP() == sp_msrun_id)
                {
                  auto ret = map_sequence_evalue.insert(
                    std::pair<QString, pappso::pappso_double>(sequence,
                                                              evalue));
                  if(ret.second == false)
                    {
                      if(ret.first->second > evalue)
                        {
                          // get best evalue for sequence
                          ret.first->second = evalue;
                        }
                    }
                }
            }
          else
            {
              // overall samples
              auto ret = map_sequence_evalue.insert(
                std::pair<QString, pappso::pappso_double>(sequence, evalue));
              if(ret.second == false)
                {
                  if(ret.first->second > evalue)
                    {
                      // get best evalue for sequence
                      ret.first->second = evalue;
                    }
                }
            }
        }
    }

  pappso::pappso_double evalue_prot = 0;
  for(auto &&peptide_pair : map_sequence_evalue)
    {
      evalue_prot += std::log10(peptide_pair.second);
      // evalue_prot *= peptide_pair.second;
    }

  // return (std::pow ((double) 10.0,evalue_prot));
  return (evalue_prot);
}
pappso::pappso_double
ProteinMatch::getNsaf(pappso::pappso_double proto_nsaf_sum,
                      const MsRun *p_msrun_id,
                      const Label *p_label) const
{
  if(proto_nsaf_sum == 0)
    {
      throw pappso::PappsoException(
        QObject::tr(
          "Error computing NSAF for protein %1 :\nproto_nsaf_sum is null")
          .arg(this->getProteinXtpSp().get()->getAccession()));
    }
  return (getProtoNsaf(p_msrun_id, p_label) / proto_nsaf_sum);
}

pappso::pappso_double
ProteinMatch::getProtoNsaf(const MsRun *sp_msrun_id, const Label *p_label) const
{
  try
    {
      if(_protein_sp.get()->size() < 1)
        {
          throw pappso::PappsoException(
            QObject::tr("protein has no amino acid sequence"));
        }
      pappso::pappso_double proto_nsaf =
        (pappso::pappso_double)countSampleScan(
          ValidationState::validAndChecked, sp_msrun_id, p_label) /
        (pappso::pappso_double)_protein_sp.get()->size();
      return proto_nsaf;
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error computing proto NSAF for protein %1 :\n%2")
          .arg(this->getProteinXtpSp().get()->getAccession())
          .arg(error.qwhat()));
    }
}

pappso::pappso_double
ProteinMatch::getPAI(const MsRun *sp_msrun_id, const Label *p_label) const
{
  try
    {
      pappso::pappso_double PAI =
        (pappso::pappso_double)countPeptideMassCharge(
          ValidationState::validAndChecked, sp_msrun_id, p_label) /
        (pappso::pappso_double)_protein_sp.get()->countTrypticPeptidesForPAI();
      return PAI;
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::PappsoException(
        QObject::tr("Error computing PAI for protein %1 :\n%2")
          .arg(this->getProteinXtpSp().get()->getAccession())
          .arg(error.qwhat()));
    }
}

pappso::pappso_double
ProteinMatch::getEmPAI(const MsRun *sp_msrun_id, const Label *p_label) const
{
  // compute emPAI, Ishihama 2005
  pappso::pappso_double value =
    std::pow(10.0, getPAI(sp_msrun_id, p_label)) - (pappso::pappso_double)1.0;

  return value;
}

const QString
ProteinMatch::getHtmlSequence(PeptideEvidence *peptide_evidence_to_locate) const
{
  size_t prot_size = _protein_sp.get()->size();
  // qDebug() << "ProteinMatch::getCoverage begin prot_size=" << prot_size << "
  // " << _protein_sp.get()-//>getSequence();
  if(prot_size == 0)
    return 0;
  bool cover_bool[prot_size]     = {false};
  bool highlight_bool[prot_size] = {false};
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence() == peptide_evidence_to_locate)
        {
          size_t size =
            peptide_evidence_to_locate->getPeptideXtpSp().get()->size();
          size_t offset = peptide_match.getStart();
          for(size_t i = 0; (i < size) && (offset < prot_size); i++, offset++)
            {
              highlight_bool[offset] = true;
            }
        }
    }

  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->isValidAndChecked())
        {
          size_t size =
            peptide_match.getPeptideEvidence()->getPeptideXtpSp().get()->size();
          size_t offset = peptide_match.getStart();
          for(size_t i = 0; (i < size) && (offset < prot_size); i++, offset++)
            {
              cover_bool[offset] = true;
            }
        }
    }
  QString sequence = getProteinXtpSp().get()->getSequence();
  QString sequence_html(
    "<style type=\"text/css\">\nspan { margin: 0px;padding: 0px; }\n</style>");
  for(unsigned int i = 0; i < prot_size; i++)
    {
      if(highlight_bool[i])
        {
          sequence_html.append(
            QString("<span style=\"background-color:%2;\">%1")
              .arg(sequence[i])
              .arg(_color_highlighted_peptide_background.name()));
          i++;
          for(; i < prot_size; i++)
            {
              if(highlight_bool[i])
                {
                  sequence_html.append(sequence[i]);
                }
              else
                {
                  sequence_html.append(QString("</span>"));
                  i--;
                  break;
                }
            }
          sequence_html.append(QString("</span>"));
        }
      else if(cover_bool[i])
        {
          sequence_html.append(
            QString("<span style=\"background-color:%2;\">%1")
              .arg(sequence[i])
              .arg(_color_peptide_background.name()));
          i++;
          for(; i < prot_size; i++)
            {
              if(highlight_bool[i])
                {
                  i--;
                  break;
                }
              if(cover_bool[i])
                {
                  sequence_html.append(sequence[i]);
                }
              else
                {
                  sequence_html.append(QString("</span>%1").arg(sequence[i]));
                  break;
                }
            }
          sequence_html.append(QString("</span>"));
        }
      else
        {
          sequence_html.append(sequence[i]);
        }
    }
  return sequence_html;
}

pappso::pappso_double
ProteinMatch::getCoverage() const
{
  size_t prot_size = _protein_sp.get()->size();
  // qDebug() << "ProteinMatch::getCoverage begin prot_size=" << prot_size << "
  // " << _protein_sp.get()-//>getSequence();
  if(prot_size == 0)
    return 0;
  bool cover_bool[prot_size] = {false};
  for(auto &peptide_match : _peptide_match_list)
    {
      if(peptide_match.getPeptideEvidence()->isValidAndChecked())
        {
          size_t size =
            peptide_match.getPeptideEvidence()->getPeptideXtpSp().get()->size();
          size_t offset = peptide_match.getStart();
          for(size_t i = 0; (i < size) && (offset < prot_size); i++, offset++)
            {
              cover_bool[offset] = true;
            }
        }
    }
  size_t count = 0;
  for(size_t i = 0; i < prot_size; i++)
    {
      if(cover_bool[i])
        count++;
    }
  // qDebug() << "ProteinMatch::getCoverage count=" << count << " prot_size=" <<
  // prot_size;
  return (((pappso::pappso_double)count) / ((pappso::pappso_double)prot_size));
}
const GroupingGroupSp &
ProteinMatch::getGroupingGroupSp() const
{
  return _sp_group;
}

void
ProteinMatch::setGroupInstance(GroupStore &group_store)
{
  qDebug();
  _sp_group = nullptr;
  if(_sp_grp_protein != nullptr)
    {
      unsigned int group_number = _sp_grp_protein.get()->getGroupNumber();
      qDebug() << "group_number=" << group_number;
      if(group_number > 0)
        {
          _sp_group = group_store.getInstance(group_number);
          _sp_group.get()->add(this);
        }
    }
  qDebug();
}


void
ProteinMatch::collectPeptideEvidences(
  std::set<const PeptideEvidence *> &peptide_evidence_set,
  ValidationState state) const
{
  for(auto &peptide_match : _peptide_match_list)
    {
      const PeptideEvidence *p_peptide_evidence =
        peptide_match.getPeptideEvidence();
      if(p_peptide_evidence->getValidationState() >= state)
        {
          peptide_evidence_set.insert(p_peptide_evidence);
        }
    }
}


QString
ProteinMatch::getFlankingNterRegion(const PeptideMatch &peptide_match,
                                    int orig_length) const
{
  int length = orig_length;
  int start  = peptide_match.getStart() - length;
  if(start < 0)
    {
      length = length + start;
      start  = 0;
    }
  if(length < 0)
    {
      length = 0;
    }
  QString flanking = getProteinXtpSp().get()->getSequence().mid(start, length);
  if(flanking.size() < orig_length)
    {
      flanking = "-" + flanking;
    }

  return flanking;
}

QString
ProteinMatch::getFlankingCterRegion(const PeptideMatch &peptide_match,
                                    int orig_length) const
{
  int start = peptide_match.getStop();
  int max   = getProteinXtpSp().get()->getSequence().size();
  if(start >= max)
    {
      return ("-");
    }
  QString flanking =
    getProteinXtpSp().get()->getSequence().mid(start, orig_length);
  if(flanking.size() < orig_length)
    {
      flanking = flanking + "-";
    }

  return flanking;
}
