
/*******************************************************************************
 * 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
 ******************************************************************************/

#include "pappsomspp/core/pappsoexception.h"
#include "peptidenaturalisotopeaverage.h"

#include "peptidenaturalisotopelist.h"

namespace pappso
{


PeptideNaturalIsotopeAverage::PeptideNaturalIsotopeAverage(const PeptideInterfaceSp &peptide,
                                                           unsigned int isotopeNumber,
                                                           unsigned int charge,
                                                           PrecisionPtr precision)
  : mcsp_peptideSp(peptide),
    m_isotopeLevel(isotopeNumber),
    m_isotopeRank(1),
    m_z(charge),
    mp_precision(precision)
{

  m_peptideNaturalIsotopeSpList.clear();

  double diffC13 = ((double)isotopeNumber * DIFFC12C13) / (double)charge;

  m_averageMz      = peptide.get()->getMz(charge) + diffC13;
  m_abundanceRatio = 0;
}

PeptideNaturalIsotopeAverage::PeptideNaturalIsotopeAverage(const PeptideInterfaceSp &peptide,
                                                           unsigned int askedIsotopeRank,
                                                           unsigned int isotopeLevel,
                                                           unsigned int charge,
                                                           PrecisionPtr precision)
  : PeptideNaturalIsotopeAverage(
      PeptideNaturalIsotopeList(peptide), askedIsotopeRank, isotopeLevel, charge, precision)
{
}

PeptideNaturalIsotopeAverage::PeptideNaturalIsotopeAverage(
  const PeptideNaturalIsotopeList &isotopeList,
  unsigned int askedIsotopeRank,
  unsigned int isotope_number,
  unsigned int charge,
  PrecisionPtr precision)
  : mcsp_peptideSp(isotopeList.getPeptideInterfaceSp()),
    m_isotopeLevel(isotope_number),
    m_isotopeRank(askedIsotopeRank),
    m_z(charge),
    mp_precision(precision)
{ // get the askedIsotopeRank :
  std::vector<PeptideNaturalIsotopeSp> v_isotope_list(
    isotopeList.getByIsotopeNumber(isotope_number, m_z));

  qDebug() << "v_isotope_list.size()=" << v_isotope_list.size() << " " << isotope_number << " "
           << askedIsotopeRank;
  m_abundanceRatio = 0;
  m_averageMz      = 0;
  if(askedIsotopeRank > v_isotope_list.size())
    {
      // there is no isotope at this rank
      return;
      // throw PappsoException(QObject::tr("askedIsotopeRank greater than
      // v_isotope_list.size() %1 vs
      // %2").arg(askedIsotopeRank).arg(v_isotope_list.size()));
    }
  else if(askedIsotopeRank < 1)
    {
      throw PappsoException(
        QObject::tr("askedIsotopeRank must be 1 or more and not %1").arg(askedIsotopeRank));
    }

  unsigned int rank = 0;

  recursiveDepletion(v_isotope_list, rank);

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


void
PeptideNaturalIsotopeAverage::recursiveDepletion(
  std::vector<PeptideNaturalIsotopeSp> &v_isotope_list, unsigned int rank)
{
  rank++;

  m_abundanceRatio = 0;
  m_averageMz      = 0;
  m_peptideNaturalIsotopeSpList.clear();
  std::vector<PeptideNaturalIsotopeSp> peptide_list;
  // select neighbors in the precision range :
  MzRange mz_range(v_isotope_list[0].get()->getMz(m_z), mp_precision);

  for(auto &isotope_sp : v_isotope_list)
    {
      if(mz_range.contains(isotope_sp.get()->getMz(m_z)))
        {
          peptide_list.push_back(isotope_sp);
          m_abundanceRatio += isotope_sp.get()->getIntensityRatio(m_z);
          m_averageMz += (isotope_sp.get()->getMz(m_z) * isotope_sp.get()->getIntensityRatio(m_z));
        }
    }

  if(peptide_list.size() > 0)
    {
      m_averageMz = m_averageMz / m_abundanceRatio;

      // depletion
      auto it_remove =
        std::remove_if(v_isotope_list.begin(),
                       v_isotope_list.end(),
                       [peptide_list](const PeptideNaturalIsotopeSp &isotope_sp) {
                         auto it = std::find(peptide_list.begin(), peptide_list.end(), isotope_sp);
                         return (it != peptide_list.end());
                       });
      v_isotope_list.erase(it_remove, v_isotope_list.end());

      if(rank == m_isotopeRank)
        {
          m_peptideNaturalIsotopeSpList = peptide_list;
          return;
        }
      else
        {
          unsigned int charge = m_z;
          std::sort(v_isotope_list.begin(),
                    v_isotope_list.end(),
                    [charge](const PeptideNaturalIsotopeSp &m, const PeptideNaturalIsotopeSp &n) {
                      return (m.get()->getIntensityRatio(charge) >
                              n.get()->getIntensityRatio(charge));
                    });
          recursiveDepletion(v_isotope_list, rank);
        }
    }
  else
    {
      m_abundanceRatio = 0;
      m_averageMz      = 0;
    }
}

PeptideNaturalIsotopeAverageSp
PeptideNaturalIsotopeAverage::makePeptideNaturalIsotopeAverageSp() const
{
  return std::make_shared<PeptideNaturalIsotopeAverage>(*this);
}

PeptideNaturalIsotopeAverage::PeptideNaturalIsotopeAverage(
  const PeptideNaturalIsotopeAverage &other)
  : mcsp_peptideSp(other.mcsp_peptideSp), mp_precision(other.mp_precision)
{

  qDebug();
  m_peptideNaturalIsotopeSpList = other.m_peptideNaturalIsotopeSpList;

  m_averageMz      = other.m_averageMz;
  m_abundanceRatio = other.m_abundanceRatio;
  m_isotopeLevel   = other.m_isotopeLevel;
  m_isotopeRank    = other.m_isotopeRank;
  m_z              = other.m_z;
  qDebug();
}

PeptideNaturalIsotopeAverage::~PeptideNaturalIsotopeAverage()
{
}

pappso_double
PeptideNaturalIsotopeAverage::getMz() const
{
  return m_averageMz;
}

pappso_double
PeptideNaturalIsotopeAverage::getIntensityRatio() const
{
  return m_abundanceRatio;
}

unsigned int
PeptideNaturalIsotopeAverage::getCharge() const
{
  return m_z;
}

unsigned int
PeptideNaturalIsotopeAverage::getIsotopeNumber() const
{
  return m_isotopeLevel;
}

unsigned int
PeptideNaturalIsotopeAverage::getIsotopeRank() const
{
  return m_isotopeRank;
}

const std::vector<PeptideNaturalIsotopeSp> &
PeptideNaturalIsotopeAverage::getComponents() const
{
  return m_peptideNaturalIsotopeSpList;
}

const PeptideInterfaceSp &
PeptideNaturalIsotopeAverage::getPeptideInterfaceSp() const
{
  return mcsp_peptideSp;
}

PrecisionPtr
PeptideNaturalIsotopeAverage::getPrecision() const
{
  return mp_precision;
}

bool
PeptideNaturalIsotopeAverage::matchPeak(pappso_double peak_mz) const
{
  // qDebug() << "PeptideNaturalIsotopeAverage::matchPeak";
  // qDebug() << "PeptideNaturalIsotopeAverage::matchPeak precision " <<
  // mp_precision.getDelta(200);
  return (MzRange(getMz(), mp_precision).contains(peak_mz));
}

bool
PeptideNaturalIsotopeAverage::isEmpty() const
{
  return (m_peptideNaturalIsotopeSpList.size() == 0);
}

QString
PeptideNaturalIsotopeAverage::toString() const
{
  return QString("%1 l%2 mz%3 z%4 N%5")
    .arg(getPeptideInterfaceSp().get()->getSequence())
    .arg(getPeptideInterfaceSp().get()->size())
    .arg(getMz())
    .arg(getCharge())
    .arg(getIsotopeNumber());
}
} // namespace pappso
