/**
 * \file pappsomspp/widget/spectrumwidget/massspectrumwidget.cpp
 * \date 22/12/2017
 * \author Olivier Langella
 * \brief plot a sectrum and annotate with peptide
 */


/*******************************************************************************
 * Copyright (c) 2017 Olivier Langella <Olivier.Langella@u-psud.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@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/
#include "massspectrumwidget.h"
#include "../../pappsoexception.h"
#include "../../peptide/peptidenaturalisotopelist.h"
#include <QDebug>
#include <QWidget>


using namespace pappso;

MassSpectrumWidget::MassSpectrumWidget(QWidget *parent)
  : GraphicDeviceWidget(parent)
{
  qDebug();

  _ms_level    = 1;
  _ion_list    = PeptideFragmentIonListBase::getCIDionList();
  _custom_plot = nullptr;

  this->setLayout(new QHBoxLayout(this));

  this->layout()->setContentsMargins(0, 0, 0, 0);
  setVisibleMassDelta(false);
  qDebug();
}
MassSpectrumWidget::~MassSpectrumWidget()
{
}

bool
MassSpectrumWidget::savePdf(const QString &fileName, int width, int height)
{

  if(_custom_plot != nullptr)
    {
      return _custom_plot->savePdf(fileName, width, height);
    }
  else
    {
      return false;
    }
}


void
MassSpectrumWidget::toQPaintDevice(QPaintDevice *device, const QSize &size)
{

  if(_custom_plot != nullptr)
    {
      QCPPainter painter;
      painter.begin(device);
      _custom_plot->toPainter(&painter, size.width(), size.height());
      painter.end();
    }
}
void
MassSpectrumWidget::setVisibleMassDelta(bool visible)
{
  qDebug();
  if(_custom_plot != nullptr)
    {
      if(visible == _is_visible_mass_delta)
        return;
      delete _custom_plot;
    }
  _is_visible_mass_delta = visible;
  while(auto item = this->layout()->takeAt(0))
    {
      delete item->widget();
    }
  qDebug();
  _custom_plot = new QCPSpectrum(this, visible);
  this->layout()->addWidget(_custom_plot);
  qDebug();
  _custom_plot->xAxis->setLabel("m/z");
  _custom_plot->yAxis->setLabel("intensity");
  qDebug();
  _custom_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
  _custom_plot->axisRect()->setRangeDrag(Qt::Horizontal);
  _custom_plot->axisRect()->setRangeZoom(Qt::Horizontal);
  qDebug();
  // legend->setVisible(false);
  // legend->setFont(QFont("Helvetica",9));
  // set locale to english, so we get english decimal separator:
  // setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom));
  qDebug();
}
void
MassSpectrumWidget::clearData()
{
  qDebug();
  _custom_plot->clearData();
  qDebug();
  _custom_plot->clearItems();
  _custom_plot->setSpectrumP(_spectrum_sp.get());
  qDebug();
}

void
MassSpectrumWidget::setPeptideCharge(unsigned int parent_ion_charge)
{
  _peptide_charge = parent_ion_charge;
}
void
MassSpectrumWidget::setIonList(const std::list<PeptideIon> &ion_list)
{
  _ion_list = ion_list;
}

void
MassSpectrumWidget::setMsLevel(unsigned int ms_level)
{
  qDebug() << "ms_level=" << ms_level;
  _ms_level = ms_level;


  if(_ms_level == 1)
    {
      setVisibleMassDelta(false);
    }
  else
    {
      setVisibleMassDelta(true);
    }

  //_precision._precision = precision._precision;
}
void
MassSpectrumWidget::setMs1Precision(PrecisionPtr precision)
{
  qDebug() << "precision->toString()=" << precision->toString();
  _p_ms1_precision = precision;
  //_precision._precision = precision._precision;
}
void
MassSpectrumWidget::setMs2Precision(PrecisionPtr precision)
{
  qDebug() << "precision->toString()=" << precision->toString();
  _p_ms2_precision = precision;
  //_precision._precision = precision._precision;
}

void
MassSpectrumWidget::setMaximumIsotopeNumber(unsigned int max_isotope_number)
{
  _max_isotope_number = max_isotope_number;
}

void
MassSpectrumWidget::setMaximumIsotopeRank(unsigned int max_isotope_rank)
{
  _max_isotope_rank = max_isotope_rank;
}
void
MassSpectrumWidget::peptideAnnotate()
{
  qDebug() << " _max_isotope_number=" << _max_isotope_number;
  clearData();
  _peak_ion_isotope_match_list.clear();
  if((_spectrum_sp == nullptr) || (_peptide_sp == nullptr))
    {
    }
  else
    {
      if(_ms_level > 1)
        {
          qDebug();
          PeptideIsotopeSpectrumMatch psm_match(*(_spectrum_sp.get()),
                                                _peptide_sp,
                                                _peptide_charge,
                                                _p_ms2_precision,
                                                _ion_list,
                                                _max_isotope_number,
                                                _max_isotope_rank);

          _peak_ion_isotope_match_list = psm_match.getPeakIonIsotopeMatchList();
          qDebug() << "_peak_ion_isotope_match_list.size()="
                   << _peak_ion_isotope_match_list.size();
        }
      else
        {
        }
      computeIsotopeMassList();
    }
  qDebug();
}

void
MassSpectrumWidget::setPeptideSp(const PeptideSp &peptide_sp)
{
  qDebug();
  _peptide_sp = peptide_sp;

  // clearData();
  qDebug();
}

void
MassSpectrumWidget::setMassSpectrumCstSPtr(const MassSpectrumCstSPtr &spectrum)
{
  qDebug();
  _spectrum_sp = spectrum;

  clearData();
  qDebug();
}

void
MassSpectrumWidget::rescale()
{
  qDebug();

  _custom_plot->rescale();

  /*
  if (_p_delta_axis_rect != nullptr) {
      _p_delta_axis_rect->axis(QCPAxis::AxisType::atLeft)->rescale();
  }
  */
  _custom_plot->replot();
  qDebug();
}

void
MassSpectrumWidget::setQualifiedMassSpectrum(
  const QualifiedMassSpectrum &spectrum)
{
  qDebug() << "spectrum.getPrecursorCharge()=" << spectrum.getPrecursorCharge();

  setMsLevel(spectrum.getMsLevel());
  setMassSpectrumCstSPtr(spectrum.getMassSpectrumCstSPtr());

  qDebug();
}

void
MassSpectrumWidget::plot()
{

  qDebug() << "_ms_level=" << _ms_level;
  peptideAnnotate();
  if(_ms_level == 1)
    {
      qDebug();
      if(_spectrum_sp != nullptr)
        {
          qDebug();
          if(_isotope_mass_list.size() > 0)
            {

              qDebug() << "_isotope_mass_list.size()="
                       << _isotope_mass_list.size();
              std::sort(_isotope_mass_list.begin(),
                        _isotope_mass_list.end(),
                        [](const PeptideNaturalIsotopeAverageSp &a,
                           const PeptideNaturalIsotopeAverageSp &b) {
                          return a.get()->getMz() < b.get()->getMz();
                        });

              if(_isotope_mass_list.size() > 0)
                {
                  PeptideNaturalIsotopeAverageSp precursor_peptide =
                    _isotope_mass_list.at(0);
                  qDebug() << "precursor_peptide.get()->getMz()="
                           << precursor_peptide.get()->getMz();
                  MzRange precursor_mass(precursor_peptide.get()->getMz(),
                                         _p_ms1_precision);
                  DataPoint monoisotope_peak;
                  monoisotope_peak.y = 0;

                  for(const DataPoint &peak : *(_spectrum_sp.get()))
                    {
                      if(precursor_mass.contains(peak.x))
                        {
                          if(peak.y > monoisotope_peak.y)
                            {
                              qDebug() << "SpectrumWidget::plot  "
                                          "(peak.intensity > "
                                          "monoisotope_peak.intensity) ";
                              monoisotope_peak = peak;
                            }
                        }
                    }
                  if(monoisotope_peak.y > 0)
                    {
                      qDebug() << "addMs1IsotopePattern";
                      _custom_plot->addMs1IsotopePattern(_isotope_mass_list,
                                                         monoisotope_peak.y);
                    }
                }
            }
        }
    }
  else
    {
      qDebug() << "_peak_ion_isotope_match_list.size()="
               << _peak_ion_isotope_match_list.size();
      _peak_ion_isotope_match_list.sort(
        [](const PeakIonIsotopeMatch &a, const PeakIonIsotopeMatch &b) {
          return a.getPeak().y > b.getPeak().y;
        });
      unsigned int i = 0;
      for(const PeakIonIsotopeMatch &peak_ion_match :
          _peak_ion_isotope_match_list)
        {
          _custom_plot->addPeakIonIsotopeMatch(peak_ion_match);

          _custom_plot->addMassDelta(peak_ion_match);
          //_p_delta_graph->addData(peak_ion_match.getPeak().x,
          // peak_ion_match.getPeak().y);
          if(i < _tag_nmost_intense)
            {
              QCPItemText *text_label = new QCPItemText(_custom_plot);
              text_label->setVisible(true);
              //_custom_plot->addItem(text_label);
              text_label->setPositionAlignment(Qt::AlignBottom |
                                               Qt::AlignHCenter);
              text_label->position->setType(QCPItemPosition::ptPlotCoords);
              text_label->position->setCoords(
                peak_ion_match.getPeak().x,
                peak_ion_match.getPeak()
                  .y); // place position at center/top of axis rect
              text_label->setFont(QFont(font().family(), 8));
              text_label->setText(
                peak_ion_match.getPeptideFragmentIonSp()
                  .get()
                  ->getCompletePeptideIonName(peak_ion_match.getCharge()));
              // text_label->setPen(QPen(PeptideFragmentIon::getPeptideIonColor(peak_ion_match.getPeptideIonType()),
              // 1)); // show black border around text
              text_label->setColor(
                QColor(PeptideFragmentIon::getPeptideIonColor(
                  peak_ion_match.getPeptideIonType())));
            }
          i++;
        }
    }

  _custom_plot->replot();
  qDebug();
}
void
MassSpectrumWidget::mzChangeEvent(pappso_double mz) const
{
  emit mzChanged(mz);
}

void
MassSpectrumWidget::peakChangeEvent(const DataPoint *p_peak_match)
{
  qDebug() << "p_peak_match=" << p_peak_match;
  if(_p_mouse_peak != p_peak_match)
    {
      _p_mouse_peak = p_peak_match;
      DataPointCstSPtr peak_shp;
      // emit peakChanged(peak_shp);
      if(_p_mouse_peak != nullptr)
        {
          qDebug() << "_p_mouse_peak->x=" << _p_mouse_peak->x;
          peak_shp = _p_mouse_peak->makeDataPointCstSPtr();
          emit peakChanged(peak_shp);
          // try to find matched ion (if it exists)
          bool found = false;
          for(const PeakIonIsotopeMatch &peak_ion_match :
              _peak_ion_isotope_match_list)
            {
              if(peak_ion_match.getPeak().x == _p_mouse_peak->x)
                {
                  // found
                  emit ionChanged(std::make_shared<const PeakIonIsotopeMatch>(
                    PeakIonIsotopeMatch(peak_ion_match)));
                  found = true;
                }
            }
          if(!found)
            {
              emit ionChanged(std::shared_ptr<const PeakIonIsotopeMatch>());
            }
        }
      else
        {
          qDebug() << "no peak";
          emit peakChanged(peak_shp);
        }
    }
}

void
MassSpectrumWidget::computeIsotopeMassList()
{
  qDebug() << "_p_ms1_precision->toString()=" << _p_ms1_precision->toString();
  _isotope_mass_list.resize(0);
  // compute isotope masses :
  if(_peptide_sp != nullptr)
    {
      pappso::PeptideNaturalIsotopeList isotope_list(_peptide_sp);
      std::map<unsigned int, pappso::pappso_double> map_isotope_number =
        isotope_list.getIntensityRatioPerIsotopeNumber();

      for(unsigned int i = 0; i < map_isotope_number.size(); i++)
        {

          unsigned int asked_rank = 0;
          unsigned int given_rank = 0;
          bool more_rank          = true;
          while(more_rank)
            {
              asked_rank++;
              pappso::PeptideNaturalIsotopeAverage isotopeAverageMono(
                isotope_list, asked_rank, i, _peptide_charge, _p_ms1_precision);
              given_rank = isotopeAverageMono.getIsotopeRank();
              if(given_rank < asked_rank)
                {
                  more_rank = false;
                }
              else if(isotopeAverageMono.getIntensityRatio() == 0)
                {
                  more_rank = false;
                }
              else
                {
                  // isotopeAverageMono.makePeptideNaturalIsotopeAverageSp();
                  _isotope_mass_list.push_back(
                    isotopeAverageMono.makePeptideNaturalIsotopeAverageSp());
                }
            }
        }
    }
  qDebug() << "_isotope_mass_list.size()=" << _isotope_mass_list.size();
}

void
pappso::MassSpectrumWidget::highlightPrecursorPeaks()
{

  if(_ms_level > 1)
    {
      pappso_double precursor_mz_1 = _peptide_sp->getMz(1);
      _custom_plot->highlightPrecursorPeaks(
        precursor_mz_1, 1, _p_ms2_precision);
      pappso_double precursor_mz_charge = _peptide_sp->getMz(_peptide_charge);
      _custom_plot->highlightPrecursorPeaks(
        precursor_mz_charge, _peptide_charge, _p_ms2_precision);
    }
}
