/**
 * \file utils/peptideevidence.cpp
 * \date 18/11/2017
 * \author Olivier Langella
 * \brief peptide evidence : a peptide sequence + spectrum + identification
 * engine evaluation (psm)
 */


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

#include "peptideevidence.h"
#include <pappsomspp/core/pappsoexception.h>
#include <pappsomspp/core/exception/exceptionnotfound.h>

std::hash<std::string> PeptideEvidence::_hash_fn;

PeptideEvidence::PeptideEvidence(MsRun *msrunid_sp, std::size_t scan_index)
{
  _msrunid_sp   = msrunid_sp;
  m_scan_number = scan_index;
  m_isSpectrumIndex =
    false; // we are not sure as it were not defined. By default, consider it is
           // a scan number to ensure compatibility

  _hash_sample_scan      = PeptideEvidence::_hash_fn(QString("%1 %2")
                                                  .arg(msrunid_sp->getXmlId())
                                                  .arg(m_scan_number)
                                                  .toStdString());
  _identification_engine = IdentificationEngine::unknown;
}

PeptideEvidence::PeptideEvidence(MsRun *msrunid_sp,
                                 std::size_t scan_index,
                                 bool isSpectrumIndex)
{
  _msrunid_sp       = msrunid_sp;
  m_scan_number     = scan_index;
  m_isSpectrumIndex = isSpectrumIndex;

  _hash_sample_scan      = PeptideEvidence::_hash_fn(QString("%1 %2")
                                                  .arg(msrunid_sp->getXmlId())
                                                  .arg(m_scan_number)
                                                  .toStdString());
  _identification_engine = IdentificationEngine::unknown;
}

PeptideEvidence::PeptideEvidence(const PeptideEvidence &other)
  : PeptideEvidence(
      other._msrunid_sp, other.m_scan_number, other.m_isSpectrumIndex)
{
  _hash_sample_scan        = other._hash_sample_scan;
  _sp_grp_peptide          = other._sp_grp_peptide;
  _peptide_sp              = other._peptide_sp;
  _rt                      = other._rt;
  _evalue                  = other._evalue;
  _exp_mass                = other._exp_mass;
  _charge                  = other._charge;
  _p_identification_source = other._p_identification_source;
  _params                  = other._params;
  _checked                 = other._checked;
  _proxy_valid             = other._proxy_valid;
  _identification_engine   = other._identification_engine;
  m_scan_number            = other.m_scan_number;
  m_isSpectrumIndex        = other.m_isSpectrumIndex;
}

PeptideEvidence::~PeptideEvidence()
{
}

pappso::pappso_double
PeptideEvidence::getTheoreticalMz() const
{
  return (getPeptideXtpSp().get()->getMz(getCharge()));
}

PeptideEvidenceSp
PeptideEvidence::makePeptideEvidenceSp() const
{
  return std::make_shared<PeptideEvidence>(*this);
}

void
PeptideEvidence::updateAutomaticFilters(
  const AutomaticFilterParameters &automatic_filter_parameters)
{
  // qDebug() << this->_p_identification_source;
  _proxy_valid =
    this->_p_identification_source->isValid(this, automatic_filter_parameters);
  // qDebug();
}

void
PeptideEvidence::setValid(bool is_valid)
{
  _proxy_valid = is_valid;
}

void
PeptideEvidence::setRetentionTime(pappso::pappso_double rt)
{
  _rt = rt;
}
void
PeptideEvidence::setEvalue(pappso::pappso_double evalue)
{
  _evalue = evalue;
}


void
PeptideEvidence::setIdentificationEngine(
  IdentificationEngine identification_engine)
{
  _identification_engine = identification_engine;
}

IdentificationEngine
PeptideEvidence::getIdentificationEngine() const
{
  return _identification_engine;
}

/** \brief set specific parameter value
 */
void
PeptideEvidence::setParam(PeptideEvidenceParam param, const QVariant &value)
{
  auto ret =
    _params.insert(std::pair<PeptideEvidenceParam, QVariant>(param, value));

  if(ret.second == false)
    {
      ret.first->second = value;
    }
}
const QVariant
PeptideEvidence::getParam(PeptideEvidenceParam param) const
{
  try
    {
      return _params.at(param);
    }
  catch(std::out_of_range &std_error)
    {
      return QVariant();
    }
}
const std::map<PeptideEvidenceParam, QVariant> &
PeptideEvidence::getParamList() const
{
  return _params;
}

std::size_t
PeptideEvidence::getHashPeptideMassSample() const
{
  return PeptideEvidence::_hash_fn(QString("%1 %2")
                                     .arg(_peptide_sp.get()->toAbsoluteString())
                                     .arg(_msrunid_sp->getXmlId())
                                     .toStdString());
}

std::size_t
PeptideEvidence::getHashSampleScan() const
{
  return _hash_sample_scan;
}
pappso::pappso_double
PeptideEvidence::getEvalue() const
{
  return _evalue;
}
pappso::pappso_double
PeptideEvidence::getExperimentalMass() const
{
  return _exp_mass;
}
pappso::pappso_double
PeptideEvidence::getExperimentalMhplus() const
{
  return _exp_mass + pappso::MHPLUS;
}

pappso::pappso_double
PeptideEvidence::getExperimentalMz() const
{
  pappso::pappso_double mz = _exp_mass;
  for(unsigned int i = 0; i < _charge; i++)
    {
      mz += pappso::MHPLUS;
    }
  mz = mz / _charge;
  return mz;
}
void
PeptideEvidence::setExperimentalMass(pappso::pappso_double exp_mass)
{
  _exp_mass = exp_mass;
}

void
PeptideEvidence::setExperimentalMassToCharge(pappso::pappso_double exp_mz)
{
  _exp_mass = (exp_mz * (double)_charge) - ((double)_charge * pappso::MHPLUS);
}

pappso::pappso_double
PeptideEvidence::getDeltaMass() const
{
  return ((_exp_mass + pappso::MHPLUS) - _peptide_sp.get()->getMz(1));
}
pappso::pappso_double
PeptideEvidence::getPpmDeltaMass() const
{
  // return (_peptide_sp.get()->getMz(1) - (_exp_mass+pappso::MHPLUS));
  pappso::pappso_double diff = getDeltaMass();
  while(diff > 0.5)
    { // assume that there is an isotope error
      diff = diff - pappso::DIFFC12C13;
    }

  while(diff < -0.5)
    { // assume that there is an isotope error
      diff = diff + pappso::DIFFC12C13;
    }
  diff = (diff / getPeptideXtpSp().get()->getMz(1)) * pappso::ONEMILLION;
  return diff;
}
void
PeptideEvidence::setCharge(unsigned int charge)
{
  _charge = charge;
}

void
PeptideEvidence::setPeptideXtpSp(PeptideXtpSp peptide)
{
  _peptide_sp = peptide;
  _peptide_sp.get()->observedInMsRun(_msrunid_sp);
}

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

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

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

bool
PeptideEvidence::isSpectrumIndex() const
{
  return m_isSpectrumIndex;
}


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

bool
PeptideEvidence::isGrouped() const
{
  if(_sp_grp_peptide.get() == nullptr)
    {
      return false;
    }
  if(_sp_grp_peptide.get()->getGroupNumber() == 0)
    {
      return false;
    }
  return true;
}
void
PeptideEvidence::setIdentificationDataSource(
  IdentificationDataSource *identification_source)
{
  _p_identification_source = identification_source;

  if(_identification_engine == IdentificationEngine::unknown)
    {
      _identification_engine = identification_source->getIdentificationEngine();
    }
}
IdentificationDataSource *
PeptideEvidence::getIdentificationDataSource() const
{
  return _p_identification_source;
}
unsigned int
PeptideEvidence::getScanNumber() const
{
  return m_scan_number;
}
pappso::pappso_double
PeptideEvidence::getRetentionTime() const
{
  return _rt;
}

pappso::pappso_double
PeptideEvidence::getHardenedRetentionTime() const
{
  if(_rt == 0)
    {
      // try to look at mz data file
      if(_p_identification_source != nullptr)
        {
          MsRunSp msrunsp = _p_identification_source->getMsRunSp();
          if(msrunsp != nullptr)
            {
              pappso::MsRunReaderSPtr reader =
                msrunsp.get()->getMsRunReaderSPtr();
              if(reader != nullptr)
                {
                  pappso::QualifiedMassSpectrum qmassspec =
                    reader.get()->qualifiedMassSpectrum(m_scan_number, false);

                  return qmassspec.getRtInSeconds();
                }
            }
        }
    }
  return _rt;
}

unsigned int
PeptideEvidence::getCharge() const
{
  return _charge;
}

const PeptideXtpSp &
PeptideEvidence::getPeptideXtpSp() const
{
  // if (_sp_grp_peptide.get() == nullptr) {
  //    throw pappso::PappsoException(QObject::tr("Peptide is null in %1
  //    %2").arg(_msrunid_sp->getXmlId()).arg(this->getScan()));
  //}
  return _peptide_sp;
}

const MsRun *
PeptideEvidence::getMsRunP() const
{
  return _msrunid_sp;
}


MsRun *
PeptideEvidence::getMsRunPtr()
{
  return _msrunid_sp;
}


void
PeptideEvidence::setGrpPeptideSp(const pappso::GrpPeptideSp &sp_grp_peptide)
{
  _sp_grp_peptide = sp_grp_peptide;
}

const pappso::GrpPeptideSp &
PeptideEvidence::getGrpPeptideSp() const
{
  return _sp_grp_peptide;
}

pappso::MassSpectrumCstSPtr
PeptideEvidence::getMassSpectrumCstSPtr() const
{
  if(m_isSpectrumIndex)
    {
      return _msrunid_sp->getMassSpectrumCstSPtrBySpectrumIndex(m_scan_number);
    }
  else
    {
      return _msrunid_sp->getMassSpectrumCstSPtrByScanNumber(m_scan_number);
    }
}

pappso::QualifiedMassSpectrum
PeptideEvidence::getQualifiedMassSpectrum(bool data) const
{

  if(m_isSpectrumIndex)
    {
      return _msrunid_sp->getQualifiedMassSpectrumBySpectrumIndex(m_scan_number,
                                                                  data);
    }
  else
    {
      return _msrunid_sp->getQualifiedMassSpectrumByScanNumber(m_scan_number,
                                                               data);
    }
}


std::size_t
PeptideEvidence::getSpectrumIndex() const
{
  if(m_isSpectrumIndex)
    {
      return m_scan_number;
    }
  throw pappso::ExceptionNotFound(
    QObject::tr(
      "this peptide evidence has no spectrum index, only a scan number : %1")
      .arg(m_scan_number));
}


std::size_t
PeptideEvidence::getSpectrumIndexByScanNumber() const
{
  if(m_isSpectrumIndex)
    {
      return m_scan_number;
    }
  else
    {
      if(_msrunid_sp != nullptr)
        {
          return _msrunid_sp->scanNumber2SpectrumIndex(m_scan_number);
        }
      else
        {
          throw pappso::PappsoException(QObject::tr("_msrunid_sp == nullptr"));
        }
    }
}

std::size_t
PeptideEvidence::getUniqueIndex() const
{
  return m_scan_number;
}

QString
PeptideEvidence::getHtmlSequence() const
{
  QString html = getPeptideXtpSp().get()->getSequence();

  std::vector<std::size_t> position_list;
  QVariant param = getParam(PeptideEvidenceParam::deepprot_delta_positions);
  if(!param.isNull())
    {
      for(auto pos_str : param.toString().split(" ", Qt::SkipEmptyParts))
        {
          position_list.push_back(pos_str.toUInt());
        }
    }

  std::sort(position_list.begin(),
            position_list.end(),
            [](std::size_t a, std::size_t b) { return a > b; });

  for(std::size_t pos : position_list)
    {
      html.insert(pos + 1, "</font>");
      html.insert(pos, "<font color=\"red\">");
    }

  return html;
}

QString
PeptideEvidence::toProForma() const
{
  return getPeptideXtpSp().get()->toProForma();
}

void
PeptideEvidence::setIsSpectrumIndex(bool is_a_spectrum_index)
{
  m_isSpectrumIndex = is_a_spectrum_index;
}
