
/*******************************************************************************
 * 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 "peptidemodificatorpipeline.h"
#include "peptidefixedmodificationbuilder.h"
#include "peptidevariablemodificationbuilder.h"
#include "../pappsoexception.h"
#include <QStringList>
using namespace pappso;


// QRegularExpression
// PeptideModificatorPipeline::_rx_modmass("[-+]?[0-9]*\\.?[0-9]*");

PeptideModificatorPipeline::PeptideModificatorPipeline()
{
}

PeptideModificatorPipeline::PeptideModificatorPipeline(
  const PeptideModificatorPipeline &other)
{
  throw PappsoException(
    QObject::tr("unable to copy PeptideModificatorPipeline object"));
  if(other.mp_peptideModificatorTee != nullptr)
    {
      // mp_peptideModificatorTee = new
      // PeptideModificatorTee(other.mp_peptideModificatorTee);
    }

  mp_lastPeptideSinkInterface = nullptr;
  mp_firstModificator         = nullptr;
  for(auto p_mod : other.m_pepModificatorPtrList)
    {
      delete(p_mod);
    }


  m_sink = other.m_sink;

  mp_lastPeptideSinkInterface = other.mp_lastPeptideSinkInterface;

  mp_firstModificator = other.mp_firstModificator;

  m_pepModificatorPtrList = other.m_pepModificatorPtrList;
}

PeptideModificatorPipeline::~PeptideModificatorPipeline()
{

  for(auto p_mod : m_pepModificatorPtrList)
    {
      delete(p_mod);
    }
  if(mp_peptideModificatorTee == nullptr)
    {
      delete(mp_peptideModificatorTee);
    }
}

void
PeptideModificatorPipeline::setSink(PeptideModificatorInterface *sink)
{
  if(mp_peptideModificatorTee != nullptr)
    {
      throw PappsoException(QObject::tr(
        "Please use setSink before addLabeledModificationString function"));
    }

  m_sink = sink;
  if(mp_firstModificator != nullptr)
    {
      mp_lastPeptideSinkInterface->setSink(m_sink);
    }
};

void
PeptideModificatorPipeline::addFixedModificationString(const QString &mod_str)
{
  privAddFixedModificationString(mod_str, true, true, true);
}
void
PeptideModificatorPipeline::addFixedNterModificationString(
  const QString &mod_str)
{
  privAddFixedModificationString(mod_str, true, false, false);
}
void
PeptideModificatorPipeline::addFixedCterModificationString(
  const QString &mod_str)
{
  privAddFixedModificationString(mod_str, false, true, false);
}

void
PeptideModificatorPipeline::privAddFixedModificationString(
  const QString &mod_str, bool Nter, bool Cter, bool else_prot)
{
  //"MOD:00397@C, MOD:00398@R"
  if(mp_peptideModificatorTee != nullptr)
    {
      throw PappsoException(
        QObject::tr("Unable to add fixed modification string after "
                    "addLabeledModificationString function"));
    }
  QStringList mod_list_str =
    mod_str.simplified().replace(" ", "").split(",", Qt::SkipEmptyParts);
  for(auto i = 0; i < mod_list_str.size(); ++i)
    {
      parseFixedModification(mod_list_str[i], Nter, Cter, else_prot);
    }
}


void
PeptideModificatorPipeline::parseFixedModification(const QString &mod_str,
                                                   bool Nter,
                                                   bool Cter,
                                                   bool else_prot)
{

  QStringList str_split = mod_str.split("@", Qt::SkipEmptyParts);

  pappso::AaModificationP aa_mod = AaModification::getInstance(str_split[0]);

  PeptideFixedModificationBuilder *mod =
    new PeptideFixedModificationBuilder(aa_mod);
  mod->setModificationPattern(str_split[1]);
  mod->setSink(m_sink);
  mod->setProtNter(Nter);
  mod->setProtCter(Cter);
  mod->setProtElse(else_prot);

  m_pepModificatorPtrList.push_back(mod);

  if(mp_firstModificator == nullptr)
    {
      mp_firstModificator = mod;
    }
  else
    {
      mp_lastPeptideSinkInterface->setSink(mod);
    }
  mp_lastPeptideSinkInterface = mod;
}

void
PeptideModificatorPipeline::addPotentialModificationString(
  const QString &mod_str)
{
  privAddPotentialModificationString(mod_str, true, true, true);
}
// protein Nter modification
void
PeptideModificatorPipeline::addPotentialNterModificationString(
  const QString &mod_str)
{
  privAddPotentialModificationString(mod_str, true, false, false);
}
// protein Cter modification
void
PeptideModificatorPipeline::addPotentialCterModificationString(
  const QString &mod_str)
{
  privAddPotentialModificationString(mod_str, false, true, false);
}

void
PeptideModificatorPipeline::privAddPotentialModificationString(
  const QString &mod_str, bool Nter, bool Cter, bool else_prot)
{
  //"MOD:00397@C, MOD:00398@R"
  if(mp_peptideModificatorTee != nullptr)
    {
      throw PappsoException(
        QObject::tr("Unable to add potential modification string after "
                    "addLabeledModificationString function"));
    }

  QStringList mod_list_str =
    mod_str.simplified().replace(" ", "").split(",", Qt::SkipEmptyParts);
  for(auto i = 0; i < mod_list_str.size(); ++i)
    {
      parsePotentialModification(mod_list_str[i], Nter, Cter, else_prot);
    }
}


void
PeptideModificatorPipeline::parsePotentialModification(const QString &mod_str,
                                                       bool Nter,
                                                       bool Cter,
                                                       bool else_prot)
{

  QStringList str_split = mod_str.split("@", Qt::SkipEmptyParts);

  QString mod_acc_str       = str_split[0];
  QStringList str_acc_split = mod_acc_str.split("(", Qt::SkipEmptyParts);
  pappso::AaModificationP aa_mod =
    AaModification::getInstance(str_acc_split[0]);

  PeptideVariableModificationBuilder *mod =
    new PeptideVariableModificationBuilder(aa_mod);

  // qDebug() << "PeptideModificatorPipeline::parsePotentialModification " << ;
  if(str_acc_split.length() == 2)
    {
      QStringList max_num_str_list =
        str_acc_split[1].replace(")", "").split("-", Qt::SkipEmptyParts);
      if(max_num_str_list.length() == 1)
        {
          mod->setModificationCounter(max_num_str_list[0].toUInt());
        }
      else if(max_num_str_list.length() == 2)
        {
          mod->setMinNumberMod(max_num_str_list[0].toUInt());
          mod->setMaxNumberMod(max_num_str_list[1].toUInt());
        }
    }
  mod->setModificationPattern(str_split[1]);
  mod->setSink(m_sink);
  mod->setProtNter(Nter);
  mod->setProtCter(Cter);
  mod->setProtElse(else_prot);

  m_pepModificatorPtrList.push_back(mod);

  if(mp_firstModificator == nullptr)
    {
      mp_firstModificator = mod;
    }
  else
    {
      mp_lastPeptideSinkInterface->setSink(mod);
    }
  mp_lastPeptideSinkInterface = mod;
}


void
PeptideModificatorPipeline::addLabeledModificationString(const QString &mod_str)
{

  if(m_sink == nullptr)
    {
      throw PappsoException(QObject::tr(
        "Please use setSink before addLabeledModificationString function"));
    }
  if(mp_peptideModificatorTee == nullptr)
    {
      mp_peptideModificatorTee = new PeptideModificatorTee;
    }
  if(mp_firstModificator == nullptr)
    {
      mp_firstModificator = mp_peptideModificatorTee;
    }

  // backup pointers
  PeptideModificatorInterface *backup_sink = m_sink;

  PeptideSpSinkInterface *backup_p_last_peptide_sink_interface =
    mp_lastPeptideSinkInterface;

  PeptideModificatorInterface *backup_p_first_modificator = mp_firstModificator;


  QStringList mod_list_str =
    mod_str.simplified().replace(" ", "").split(",", Qt::SkipEmptyParts);
  for(auto i = 0; i < mod_list_str.size(); ++i)
    {
      parseLabeledModification(mod_list_str[i], true, true, true);

      if(i == 0)
        {
          mp_peptideModificatorTee->addModificator(mp_firstModificator);
        }
    }

  m_sink                      = backup_sink;
  mp_lastPeptideSinkInterface = backup_p_last_peptide_sink_interface;
  mp_firstModificator         = backup_p_first_modificator;

  mp_lastPeptideSinkInterface->setSink(mp_peptideModificatorTee);
}


void
PeptideModificatorPipeline::parseLabeledModification(const QString &mod_str,
                                                     bool Nter,
                                                     bool Cter,
                                                     bool else_prot)
{

  QStringList str_split          = mod_str.split("@", Qt::SkipEmptyParts);
  pappso::AaModificationP aa_mod = AaModification::getInstance(str_split[0]);

  PeptideFixedModificationBuilder *mod =
    new PeptideFixedModificationBuilder(aa_mod);
  mod->setModificationPattern(str_split[1]);
  mod->setSink(m_sink);
  mod->setProtNter(Nter);
  mod->setProtCter(Cter);
  mod->setProtCter(else_prot);

  m_pepModificatorPtrList.push_back(mod);

  mp_lastPeptideSinkInterface->setSink(mod);
  mp_lastPeptideSinkInterface = mod;
  mp_firstModificator         = mod;
}


void
PeptideModificatorPipeline::setPeptideSp(std::int8_t sequence_database_id,
                                         const ProteinSp &protein_sp,
                                         bool is_decoy,
                                         const PeptideSp &peptide_sp_original,
                                         unsigned int start,
                                         bool is_nter,
                                         unsigned int missed_cleavage_number,
                                         bool semi_enzyme)
{
  if(mp_firstModificator == nullptr)
    {
      m_sink->setPeptideSp(sequence_database_id,
                           protein_sp,
                           is_decoy,
                           peptide_sp_original,
                           start,
                           is_nter,
                           missed_cleavage_number,
                           semi_enzyme);
    }
  else
    {
      mp_firstModificator->setPeptideSp(sequence_database_id,
                                        protein_sp,
                                        is_decoy,
                                        peptide_sp_original,
                                        start,
                                        is_nter,
                                        missed_cleavage_number,
                                        semi_enzyme);
    }
}


void
PeptideModificatorPipeline::setPeptide(std::int8_t sequence_database_id,
                                       const ProteinSp &protein_sp,
                                       bool is_decoy,
                                       const QString &peptide_str,
                                       unsigned int start,
                                       bool is_nter,
                                       unsigned int missed_cleavage_number,
                                       bool semi_enzyme)
{

  qDebug() << "PeptideModificatorPipeline::setPeptide begin";

  Peptide peptide(peptide_str);

  PeptideSp peptide_sp = peptide.makePeptideSp();

  qDebug() << "PeptideModificatorPipeline::setPeptide m_sink->setPeptideSp";
  setPeptideSp(sequence_database_id,
               protein_sp,
               is_decoy,
               peptide_sp,
               start,
               is_nter,
               missed_cleavage_number,
               semi_enzyme);
  qDebug() << "PeptideModificatorPipeline::setPeptide end";
}
