/**
 * \file pappsomspp/peptide/peptiderawfragmentmasses.cpp
 * \date 16/7/2016
 * \author Olivier Langella
 * \brief class dedicated to raw mass computations of peptide products
 * (fragments)
 */

/*******************************************************************************
 * Copyright (c) 2016 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 "peptiderawfragmentmasses.h"
namespace pappso
{


PeptideRawFragmentMasses::ionDeltatMzMassMap PeptideRawFragmentMasses::m_ionDeltaMz = {
  [(std::int8_t)Enums::PeptideIon::b]     = 0,
  [(std::int8_t)Enums::PeptideIon::bstar] = -MASSNH3,
  [(std::int8_t)Enums::PeptideIon::bo]    = -MASSH2O,
  [(std::int8_t)Enums::PeptideIon::a]     = -MASSCO,
  [(std::int8_t)Enums::PeptideIon::astar] = -MASSCO - MASSNH3,
  [(std::int8_t)Enums::PeptideIon::ao]    = -MASSCO - MASSH2O,
  [(std::int8_t)Enums::PeptideIon::bp]    = -1,
  [(std::int8_t)Enums::PeptideIon::c]     = MASSNH3,
  [(std::int8_t)Enums::PeptideIon::y]     = MASSH2O,
  [(std::int8_t)Enums::PeptideIon::ystar] = MASSH2O - MASSNH3,
  [(std::int8_t)Enums::PeptideIon::yo]    = 0,
  [(std::int8_t)Enums::PeptideIon::z]     = MASSOXYGEN - MASSNITROGEN - MPROTIUM,
  [(std::int8_t)Enums::PeptideIon::yp]    = -1,
  [(std::int8_t)Enums::PeptideIon::x]     = MASSCO + MASSOXYGEN};


pappso_double
PeptideRawFragmentMasses::getDeltaMass(Enums::PeptideIon ion_type)
{
  return m_ionDeltaMz[(std::int8_t)ion_type];
}


PeptideRawFragmentMasses::PeptideRawFragmentMasses(const Peptide &peptide,
                                                   RawFragmentationMode mode)
{

  qDebug();
  std::vector<Aa>::const_iterator it(peptide.begin());
  std::vector<Aa>::const_iterator end(peptide.end());
  if(it == end)
    return;

  qDebug();
  pappso_double cumulative_mass = it->getMass();
  if(peptide.getNterModification() != nullptr)
    {
      cumulative_mass += peptide.getNterModification()->getMass();
    }
  qDebug();
  m_cumulativeNterMasses.push_back(cumulative_mass);
  it++;

  if(it != end)
    {
      end--;
      qDebug();
      while(it != end)
        {

          qDebug() << " cumulative_mass=" << cumulative_mass;
          cumulative_mass += it->getMass();
          if((mode == RawFragmentationMode::proline_effect) && (it->getLetter() == 'P'))
            {
            }
          else
            {
              m_cumulativeNterMasses.push_back(cumulative_mass);
            }
          it++;
        }
    }

  qDebug();
  std::vector<Aa>::const_reverse_iterator rit(peptide.rbegin());
  std::vector<Aa>::const_reverse_iterator ritf(peptide.rbegin());
  std::vector<Aa>::const_reverse_iterator rend(peptide.rend());
  ritf++;
  cumulative_mass = rit->getMass();
  if(peptide.getCterModification() != nullptr)
    {
      cumulative_mass += peptide.getCterModification()->getMass();
    }
  if((mode == RawFragmentationMode::proline_effect) && (ritf != rend) && (ritf->getLetter() == 'P'))
    {
    }
  else
    {
      m_cumulativeCterMasses.push_back(cumulative_mass);
    }
  qDebug();

  rit++;
  ritf++;
  if(rit != rend)
    {
      rend--;
      while(rit != rend)
        {
          cumulative_mass += rit->getMass();
          if((mode == RawFragmentationMode::proline_effect) && (ritf != peptide.rend()) &&
             (ritf->getLetter() == 'P'))
            {
            }
          else
            {
              m_cumulativeCterMasses.push_back(cumulative_mass);
            }
          rit++;
          ritf++;
        }
    }

  qDebug();
}

void
PeptideRawFragmentMasses::pushBackIonMasses(std::vector<pappso_double> &mass_list,
                                            Enums::PeptideIon ion_type) const
{
  const std::vector<pappso_double> *p_mass_list = &m_cumulativeCterMasses;

  if(peptideIonIsNter(ion_type))
    {
      // nter
      p_mass_list = &m_cumulativeNterMasses;
    }

  for(pappso_double mass : *p_mass_list)
    {
      mass_list.push_back(mass + m_ionDeltaMz[(std::int8_t)ion_type]);
    }
}

void
PeptideRawFragmentMasses::pushBackIonMz(std::vector<pappso_double> &mass_list,
                                        Enums::PeptideIon ion_type,
                                        unsigned int charge) const
{
  const std::vector<pappso_double> *p_mass_list = &m_cumulativeCterMasses;

  if(peptideIonIsNter(ion_type))
    {
      // nter
      p_mass_list = &m_cumulativeNterMasses;
    }

  for(pappso_double mass : *p_mass_list)
    {
      mass_list.push_back((mass + m_ionDeltaMz[(std::int8_t)ion_type] + (MHPLUS * charge)) /
                          charge);
    }
}

void
PeptideRawFragmentMasses::pushBackMatchSpectrum(std::vector<SimplePeakIonMatch> &peak_match_list,
                                                const MassSpectrum &spectrum,
                                                PrecisionPtr precision,
                                                Enums::PeptideIon ion_type,
                                                unsigned int charge) const
{
  std::vector<pappso_double> mass_list;
  pushBackIonMz(mass_list, ion_type, charge);

  // no need to sort
  // std::sort(mass_list.begin(), mass_list.end());

  std::vector<pappso_double>::iterator it_mz     = mass_list.begin();
  std::vector<pappso_double>::iterator it_mz_end = mass_list.end();

  // scan products over each peak in spectrum :
  std::vector<DataPoint>::const_iterator it_peak     = spectrum.begin();
  std::vector<DataPoint>::const_iterator it_peak_end = spectrum.end();
  unsigned int ion_size                              = 1;
  while((it_peak != it_peak_end) && (it_mz != it_mz_end))
    {
      MzRange massrange(it_peak->x, precision);
      if((*it_mz) > massrange.upper())
        {
          it_peak++;
          continue;
        }
      if((*it_mz) < massrange.lower())
        {
          it_mz++;
          ion_size++;
          continue;
        }
      peak_match_list.push_back({(*it_peak), ion_type, ion_size, charge, (*it_mz)});
      it_mz++;
      ion_size++;
    }
}

PeptideRawFragmentMasses::~PeptideRawFragmentMasses()
{
}
} // namespace pappso
