/**
 * \file pappsomspp/peptide/peptidefragmention.cpp
 * \date 10/3/2015
 * \author Olivier Langella
 * \brief peptide ion model
 */

/*******************************************************************************
 * 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 <QDebug>
#include "peptidefragmention.h"
#include "pappsomspp/core/pappsoexception.h"
#include "peptidenaturalisotopelist.h"
#include "peptiderawfragmentmasses.h"

namespace pappso
{
PeptideFragmentIon::PeptideFragmentIon(const PeptideFragmentSp &sp_fragment,
                                       Enums::PeptideIon ion_type)
  : PeptideFragmentIon(sp_fragment, ion_type, 0)
{
}

PeptideFragmentIon::PeptideFragmentIon(const PeptideFragmentSp &sp_fragment,
                                       Enums::PeptideIon ion_type,
                                       unsigned int number_of_neutral_phospho_loss)
  : msp_fragment(sp_fragment), m_ionType(ion_type)
{
  m_mass                     = msp_fragment.get()->getMass();
  PeptideDirection direction = msp_fragment.get()->getPeptideIonDirection();
  if(direction != pappso::getPeptideIonDirection(m_ionType))
    {
      throw PappsoException(QString("Enums::PeptideIon %1 is not an %2 fragment")
                              .arg(PeptideFragmentIon::getPeptideIonName(m_ionType))
                              .arg(PeptideFragment::getPeptideIonDirectionName(direction)));
    }
  m_mass -= MASSH2O;
  switch(m_ionType)
    {
        // -MASSH2O
      case Enums::PeptideIon::yp:
        m_neutralPhosphoLossNumber = number_of_neutral_phospho_loss;
        m_mass -= ((MASSH2O + MASSPHOSPHORYLATEDR) * m_neutralPhosphoLossNumber);
        ion_type = Enums::PeptideIon::y;
        break;
      case Enums::PeptideIon::bp:
        m_neutralPhosphoLossNumber = number_of_neutral_phospho_loss;
        m_mass -= ((MASSH2O + MASSPHOSPHORYLATEDR) * m_neutralPhosphoLossNumber);
        ion_type = Enums::PeptideIon::b;
        break;
      default:
        break;
    }
  m_mass += PeptideRawFragmentMasses::getDeltaMass(ion_type);
}

PeptideFragmentIon::PeptideFragmentIon(const PeptideFragmentIon &other)
  : msp_fragment(other.msp_fragment), m_ionType(other.m_ionType)
{
  m_mass = other.m_mass;
}


PeptideFragmentIon::PeptideFragmentIon(PeptideFragmentIon &&toCopy) // move constructor
  : msp_fragment(std::move(toCopy.msp_fragment)), m_ionType(toCopy.m_ionType), m_mass(toCopy.m_mass)
{
}

PeptideFragmentIon::~PeptideFragmentIon()
{
}


PeptideDirection
PeptideFragmentIon::getPeptideIonDirection(Enums::PeptideIon ion_type)
{
  return pappso::getPeptideIonDirection(ion_type);
}


const QString
PeptideFragmentIon::getCompletePeptideIonName(unsigned int charge) const
{

  std::size_t size = msp_fragment.get()->size();
  QString plusstr  = "+";
  plusstr          = plusstr.repeated(charge);
  if(m_ionType == Enums::PeptideIon::yp)
    {
      return QString("y%1(-P%2)%3").arg(size).arg(m_neutralPhosphoLossNumber).arg(plusstr);
    }
  else if(m_ionType == Enums::PeptideIon::bp)
    {
      return QString("b%1(-P%2)%3").arg(size).arg(m_neutralPhosphoLossNumber).arg(plusstr);
    }

  return QString("%1%2%3").arg(getPeptideIonName(m_ionType)).arg(size).arg(plusstr);
}

const QString
PeptideFragmentIon::getPeptideIonName() const
{
  if((m_ionType == Enums::PeptideIon::yp) || (m_ionType == Enums::PeptideIon::bp))
    {
      return QString("%1(%2)").arg(getPeptideIonName(m_ionType)).arg(m_neutralPhosphoLossNumber);
    }
  return getPeptideIonName(m_ionType);
}

const QString
PeptideFragmentIon::getPeptideIonName(Enums::PeptideIon m_ionType)
{
  switch(m_ionType)
    {
      case Enums::PeptideIon::y:
        return "y";
        break;
      case Enums::PeptideIon::yp:
        return "yP";
        break;
      case Enums::PeptideIon::ystar:
        return "y*";
        break;
      case Enums::PeptideIon::yo:
        return "yO";
        break;
      case Enums::PeptideIon::bstar:
        return "b*";
        break;
      case Enums::PeptideIon::bo:
        return "bO";
        break;
      case Enums::PeptideIon::a:
        return "a";
        break;
      case Enums::PeptideIon::astar:
        return "a*";
        break;
      case Enums::PeptideIon::ao:
        return "aO";
        break;
      case Enums::PeptideIon::c:
        return "c";
        break;
        // SvgIon.moxygen - mN
      case Enums::PeptideIon::z:
        return "z";
        break;
      case Enums::PeptideIon::b:
        return "b";
        break;
      case Enums::PeptideIon::bp:
        return "bP";
        break;
      case Enums::PeptideIon::x:
        return "x";
        break;
      default:
        throw PappsoException(QString("Enums::PeptideIon name not implemented"));
        break;
    }
}

const QColor
PeptideFragmentIon::getPeptideIonColor(Enums::PeptideIon m_ionType)
{
  switch(m_ionType)
    {
      case Enums::PeptideIon::y:
        return QColor("red");
        break;
      case Enums::PeptideIon::yp:
        return QColor("red");
        break;
      case Enums::PeptideIon::ystar:
        return QColor("red");
        break;
      case Enums::PeptideIon::yo:
        return QColor("orange");
        break;
      case Enums::PeptideIon::x:
        return QColor("orange");
        break;
      case Enums::PeptideIon::bstar:
        return QColor("blue");
        break;
      case Enums::PeptideIon::bo:
        return QColor("#ff00ff");
        break;
      case Enums::PeptideIon::a:
        return QColor("green");
        break;
      case Enums::PeptideIon::astar:
        return QColor("green");
        break;
      case Enums::PeptideIon::ao:
        return QColor("green");
        break;
      case Enums::PeptideIon::c:
        return QColor("blue");
        break;
        // SvgIon.moxygen - mN
      case Enums::PeptideIon::z:
        return QColor("red");
        break;
      case Enums::PeptideIon::b:
        return QColor("blue");
        break;
      case Enums::PeptideIon::bp:
        return QColor("blue");
        break;
      default:
        throw PappsoException(
          QString("Enums::PeptideIon color not implemented %1").arg(getPeptideIonName(m_ionType)));
        break;
    }
}

int
PeptideFragmentIon::getNumberOfAtom(Enums::AtomIsotopeSurvey atom) const
{
  int number = msp_fragment.get()->getNumberOfAtom(atom);
  int diff   = 0;
  switch(atom)
    {
      case Enums::AtomIsotopeSurvey::C:
        switch(m_ionType)
          {
            case Enums::PeptideIon::y:
              break;
            case Enums::PeptideIon::yp:
              // H 1 O 3 P 1 + H 2 0
              break;
            case Enums::PeptideIon::ystar:
              // m_mass -= MASSNH3;
              break;
            case Enums::PeptideIon::yo:
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::bstar:
              // m_mass -= MASSH2O;
              // m_mass -= MASSNH3;
              break;
            case Enums::PeptideIon::bo:
              // m_mass -= MASSH2O;
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::a:
              // m_mass -= MASSH2O;
              // m_mass -= MASSCO;
              diff = -1;
              break;
            case Enums::PeptideIon::c:
              // m_mass += MASSNH3;
              break;
              // SvgIon.moxygen - mN
            case Enums::PeptideIon::z:
              // m_mass -= MASSH2O;
              // m_mass += MASSOXYGEN - MASSNITROGEN - MPROTIUM;
              break;
            case Enums::PeptideIon::b:
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::bp:
              // H 1 O 3 P 1 + H 2 0
              break;

            case Enums::PeptideIon::astar:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSNH3;
              diff = -1;
              break;
            case Enums::PeptideIon::ao:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSH2O;
              diff = -1;
              break;
            case Enums::PeptideIon::x:
              // +MASSCO + MASSOXYGEN
              diff = +1;
              break;
            default:
              throw PappsoException(QString("Enums::PeptideIon name not implemented"));
              break;
          }
        break;
      case Enums::AtomIsotopeSurvey::H:
        switch(m_ionType)
          {
            case Enums::PeptideIon::y:
              break;
            case Enums::PeptideIon::yp:
              // H 1 O 3 P 1 + H 2 0
              diff = -3 * m_neutralPhosphoLossNumber;
              break;
            case Enums::PeptideIon::ystar:
              // m_mass -= MASSNH3;
              diff = -3;
              break;
            case Enums::PeptideIon::yo:
              // m_mass -= MASSH2O;
              diff = -2;
              break;
            case Enums::PeptideIon::bstar:
              // m_mass -= MASSH2O;
              // m_mass -= MASSNH3;
              diff = -5;
              break;
            case Enums::PeptideIon::bo:
              // m_mass -= MASSH2O;
              // m_mass -= MASSH2O;
              diff = -4;
              break;
            case Enums::PeptideIon::a:
              // m_mass -= MASSH2O;
              // m_mass -= MASSCO;
              diff = -2;
              break;
            case Enums::PeptideIon::c:
              // m_mass += MASSNH3;
              diff = -3;
              break;
              // SvgIon.moxygen - mN
            case Enums::PeptideIon::z:
              // m_mass -= MASSH2O;
              // m_mass += MASSOXYGEN - MASSNITROGEN - MPROTIUM;
              diff = -3;
              break;
            case Enums::PeptideIon::b:
              // m_mass -= MASSH2O;
              diff = -2;
              break;
            case Enums::PeptideIon::bp:
              // H 1 O 3 P 1 + H 2 0
              diff = -3 * m_neutralPhosphoLossNumber;
              break;


            case Enums::PeptideIon::astar:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSNH3;
              diff = -5;
              break;
            case Enums::PeptideIon::ao:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSH2O;
              diff = -4;
              break;
            case Enums::PeptideIon::x:
              // +MASSCO + MASSOXYGEN
              diff = -2;
              break;
            default:
              throw PappsoException(QString("Enums::PeptideIon name not implemented"));
              break;
          }
        break;
      case Enums::AtomIsotopeSurvey::N:
        switch(m_ionType)
          {
            case Enums::PeptideIon::y:
              break;
            case Enums::PeptideIon::yp:
              // H 1 O 3 P 1 + H 2 0
              break;

            case Enums::PeptideIon::ystar:
              // m_mass -= MASSNH3;
              diff = -1;
              break;
            case Enums::PeptideIon::yo:
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::bstar:
              // m_mass -= MASSH2O;
              // m_mass -= MASSNH3;
              diff = -1;
              break;
            case Enums::PeptideIon::bo:
              // m_mass -= MASSH2O;
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::a:
              // m_mass -= MASSH2O;
              // m_mass -= MASSCO;
              break;
            case Enums::PeptideIon::c:
              // m_mass += MASSNH3;
              diff = -1;
              break;
              // SvgIon.moxygen - mN
            case Enums::PeptideIon::z:
              // m_mass -= MASSH2O;
              // m_mass += MASSOXYGEN - MASSNITROGEN - MPROTIUM;
              diff = -1;
              break;
            case Enums::PeptideIon::b:
              // m_mass -= MASSH2O;
              break;
            case Enums::PeptideIon::bp:
              // H 1 O 3 P 1 + H 2 0
              break;


            case Enums::PeptideIon::astar:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSNH3;
              diff = -1;
              break;
            case Enums::PeptideIon::ao:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSH2O;
              break;
            case Enums::PeptideIon::x:
              // +MASSCO + MASSOXYGEN
              break;
            default:
              throw PappsoException(QString("Enums::PeptideIon name not implemented"));
              break;
          }
        break;
      case Enums::AtomIsotopeSurvey::O:
        switch(m_ionType)
          {
            case Enums::PeptideIon::y:
              break;
            case Enums::PeptideIon::yp:
              // H 1 O 3 P 1 + H 2 0
              diff = -4 * m_neutralPhosphoLossNumber;
              break;

            case Enums::PeptideIon::ystar:
              // m_mass -= MASSNH3;
              break;
            case Enums::PeptideIon::yo:
              // m_mass -= MASSH2O;
              diff = -1;
              break;
            case Enums::PeptideIon::bstar:
              // m_mass -= MASSH2O;
              // m_mass -= MASSNH3;
              diff = -1;
              break;
            case Enums::PeptideIon::bo:
              // m_mass -= MASSH2O;
              // m_mass -= MASSH2O;
              diff = -2;
              break;
            case Enums::PeptideIon::a:
              // m_mass -= MASSH2O;
              // m_mass -= MASSCO;
              diff = -2;
              break;
            case Enums::PeptideIon::c:
              // m_mass += MASSNH3;
              break;
              // SvgIon.moxygen - mN
            case Enums::PeptideIon::z:
              // m_mass -= MASSH2O;
              // m_mass += MASSOXYGEN - MASSNITROGEN  - MPROTIUM;
              diff = -2;
              break;
            case Enums::PeptideIon::b:
              // m_mass -= MASSH2O;
              diff = -1;
              break;
            case Enums::PeptideIon::bp:
              // H 1 O 3 P 1 + H 2 0
              diff = -4 * m_neutralPhosphoLossNumber;
              break;


            case Enums::PeptideIon::astar:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSNH3;
              diff = -2;
              break;
            case Enums::PeptideIon::ao:
              // m_mass -= MASSH2O;
              // m_mass = - MASSCO - MASSH2O;
              diff = -3;
              break;
            case Enums::PeptideIon::x:
              // +MASSCO + MASSOXYGEN
              diff = +1;
              break;
            default:
              throw PappsoException(QObject::tr("Enums::PeptideIon name not implemented"));
              break;
          }
        break;
      case Enums::AtomIsotopeSurvey::S:
        break;
      default:
        qDebug() << "PeptideFragmentIon::getNumberOfAtom(Enums::AtomIsotopeSurvey "
                    "atom) NOT IMPLEMENTED";
    }
  return number + diff;
}


int
PeptideFragmentIon::getNumberOfIsotope(Enums::Isotope isotope) const
{
  int number = msp_fragment.get()->getNumberOfIsotope(isotope);
  return number;
}

PeptideFragmentIonSp
PeptideFragmentIon::makePeptideFragmentIonSp() const
{
  return std::make_shared<PeptideFragmentIon>(*this);
}


unsigned int
PeptideFragmentIon::size() const
{
  return msp_fragment.get()->size();
}

const QString
PeptideFragmentIon::getSequence() const
{
  return msp_fragment.get()->getSequence();
}

const PeptideFragmentSp &
PeptideFragmentIon::getPeptideFragmentSp() const
{
  return msp_fragment;
}

const QString
PeptideFragmentIon::getName() const
{
  return QString("%1-%2").arg(getPeptideIonName(m_ionType)).arg(size());
}

pappso_double
PeptideFragmentIon::getMass() const
{
  return m_mass;
}

Enums::PeptideIon
PeptideFragmentIon::getPeptideIonType() const
{
  return m_ionType;
}
PeptideDirection
PeptideFragmentIon::getPeptideIonDirection() const
{
  return msp_fragment.get()->getPeptideIonDirection();
}
unsigned int
PeptideFragmentIon::getNumberOfNeutralPhosphoLoss() const
{
  return m_neutralPhosphoLossNumber;
}

bool
PeptideFragmentIon::isPalindrome() const
{
  return msp_fragment.get()->isPalindrome();
}


} // namespace pappso
