/**
 * \file pappsomspp/psm/peptidespectrummatch.cpp
 * \date 2/4/2015
 * \author Olivier Langella
 * \brief find peaks matching between ions and spectrum
 */

/*******************************************************************************
 * 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 <list>
#include <numeric>
#include "peakionmatch.h"
#include "peptidespectrummatch.h"
#include "../pappsoexception.h"
#include "../peptide/peptidefragment.h"
#include "../peptide/peptidefragmentionlistbase.h"

namespace pappso
{


void
PeptideSpectrumMatch::privMatchIonList(
  const MassSpectrum &spectrum,
  const PeptideFragmentIonListBase &fragmentIonList,
  unsigned int max_charge,
  const std::list<PeptideIon> &ion_type_list)
{

  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
  //         << " ion_type_list.size()=" << ion_type_list.size();
  std::list<DataPoint> peak_list(spectrum.begin(), spectrum.end());
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
  //         << " peak_list.size()=" << peak_list.size();

  for(auto ion_type : ion_type_list)
    {
      auto ion_list = fragmentIonList.getPeptideFragmentIonSp(ion_type);
      // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
      //         << " ion_list.size()=" << ion_list.size();
      for(unsigned int charge = 1; charge <= max_charge; charge++)
        {
          for(auto &&ion : ion_list)
            {
              std::list<DataPoint>::iterator it_peak =
                getBestPeakIterator(peak_list, ion, charge);

              if(it_peak != peak_list.end())
                {

                  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " <<
                  // __LINE__
                  //        << " peak FOUND";
                  _peak_ion_match_list.push_back(
                    PeakIonMatch(*it_peak, ion, charge));
                  _ion_type_count[(std::int8_t)ion->getPeptideIonType()]++;
                  peak_list.erase(it_peak);
                }
            }
        }
    }
}

PeptideSpectrumMatch::PeptideSpectrumMatch(
  const MassSpectrum &spectrum,
  std::vector<PeptideFragmentIonSp> &v_peptide_fragment_ion,
  std::vector<unsigned int> &v_peptide_fragment_ion_charge,
  PrecisionPtr precision)
  : _precision(precision)
{
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__;
  // throw PappsoException(QObject::tr("v_peptideIsotopeList.size() %1 !=
  // v_peptideIonList.size()
  // %2").arg(v_peptideIsotopeList.size()).arg(v_peptideIonList.size()));
  if(v_peptide_fragment_ion.size() != v_peptide_fragment_ion_charge.size())
    {
      throw PappsoException(
        QObject::tr("v_peptide_fragment_ion.size() != "
                    "v_peptide_fragment_ion_charge.size() %2")
          .arg(v_peptide_fragment_ion.size())
          .arg(v_peptide_fragment_ion_charge.size()));
    }


  auto ionIt    = v_peptide_fragment_ion.begin();
  auto chargeIt = v_peptide_fragment_ion_charge.begin();
  std::list<DataPoint> peak_list(spectrum.begin(), spectrum.end());

  while(ionIt != v_peptide_fragment_ion.end())
    {
      std::list<DataPoint>::iterator it_peak =
        getBestPeakIterator(peak_list, *ionIt, *chargeIt);
      if(it_peak != peak_list.end())
        {
          _peak_ion_match_list.push_back(
            PeakIonMatch(*it_peak, *ionIt, *chargeIt));
          _ion_type_count[(std::int8_t)ionIt->get()->getPeptideIonType()]++;
          peak_list.erase(it_peak);
        }

      ionIt++;
      chargeIt++;
    }
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
           << " _ion_type_count[PeptideIon::y]="
           << _ion_type_count[(std::int8_t)PeptideIon::y];
}


PeptideSpectrumMatch::PeptideSpectrumMatch(
  const MassSpectrum &spectrum,
  const PeptideFragmentIonListBase &fragmentIonList,
  unsigned int max_charge,
  PrecisionPtr precision,
  const std::list<PeptideIon> &ion_type_list)
  : _precision(precision)
{
  privMatchIonList(spectrum, fragmentIonList, max_charge, ion_type_list);
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
           << " _ion_type_count[PeptideIon::y]="
           << _ion_type_count[(std::int8_t)PeptideIon::y];
}

PeptideSpectrumMatch::PeptideSpectrumMatch(
  const MassSpectrum &spectrum,
  const pappso::PeptideSp &peptideSp,
  unsigned int parent_charge,
  PrecisionPtr precision,
  const std::list<PeptideIon> &ion_type_list)
  : _precision(precision)
{
  PeptideFragmentIonListBase fragmentIonList(peptideSp, ion_type_list);
  privMatchIonList(spectrum, fragmentIonList, parent_charge, ion_type_list);
  qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
           << " _ion_type_count[PeptideIon::y]="
           << _ion_type_count[(std::int8_t)PeptideIon::y];
}


PeptideSpectrumMatch::PeptideSpectrumMatch(const PeptideSpectrumMatch &other)
  : _precision(other._precision),
    _peak_ion_match_list(other._peak_ion_match_list)
{
}

std::list<DataPoint>::iterator
PeptideSpectrumMatch::getBestPeakIterator(std::list<DataPoint> &peak_list,
                                          const PeptideFragmentIonSp &ion,
                                          unsigned int charge) const
{
  // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
  //         << " peak_list.size()=" << peak_list.size();
  std::list<DataPoint>::iterator itpeak   = peak_list.begin();
  std::list<DataPoint>::iterator itend    = peak_list.end();
  std::list<DataPoint>::iterator itselect = peak_list.end();

  pappso_double best_intensity = 0;

  while(itpeak != itend)
    {

      // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
      //         << " itpeak->x=" << itpeak->x;
      if(ion.get()->matchPeak(_precision, itpeak->x, charge))
        {
          // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
          //         << " ion.get()->matchPeak";
          if(itpeak->y > best_intensity)
            {
              best_intensity = itpeak->y;
              itselect       = itpeak;
            }
        }
      itpeak++;
    }

  return (itselect);
}

PeptideSpectrumMatch::~PeptideSpectrumMatch()
{
}


unsigned int
PeptideSpectrumMatch::size() const
{
  return _peak_ion_match_list.size();
}

PeptideSpectrumMatch::const_iterator
PeptideSpectrumMatch::begin() const
{
  return _peak_ion_match_list.begin();
}
PeptideSpectrumMatch::const_iterator
PeptideSpectrumMatch::end() const
{
  return _peak_ion_match_list.end();
}

unsigned int
PeptideSpectrumMatch::countTotalMatchedIons() const
{
  return std::accumulate(_ion_type_count.begin(), _ion_type_count.end(), 0);
}

const std::array<unsigned int, PEPTIDE_ION_TYPE_COUNT> &
PeptideSpectrumMatch::getIonTypeCountArray() const
{
  return _ion_type_count;
}

bool
PeptideSpectrumMatch::contains(const PeptideFragmentIon *peptideFragmentIonSp,
                               unsigned int z) const
{
  auto it = _peak_ion_match_list.begin();
  while(it != _peak_ion_match_list.end())
    {
      const PeakIonMatch &ref = *it;
      if((ref.getCharge() == z) &&
         (ref.getPeptideFragmentIonSp().get() == peptideFragmentIonSp))
        {
          return true;
        }
      it++;
    }
  return false;
}
} // namespace pappso
