
/*******************************************************************************
 * 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 "peptidevariablemodificationbuilder.h"

namespace pappso
{
bool
PeptideVariableModificationBuilder::next_combination(
  const std::vector<unsigned int>::iterator first,
  std::vector<unsigned int>::iterator k,
  const std::vector<unsigned int>::iterator last)
{
  /* Credits: Mark Nelson http://marknelson.us */
  if((first == last) || (first == k) || (last == k))
    return false;
  std::vector<unsigned int>::iterator i1 = first;
  std::vector<unsigned int>::iterator i2 = last;
  ++i1;
  if(last == i1)
    return false;
  i1 = last;
  --i1;
  i1 = k;
  --i2;
  while(first != i1)
    {
      if(*--i1 < *i2)
        {
          std::vector<unsigned int>::iterator j = k;
          while(!(*i1 < *j))
            ++j;
          std::iter_swap(i1, j);
          ++i1;
          ++j;
          i2 = k;
          std::rotate(i1, j, last);
          while(last != j)
            {
              ++j;
              ++i2;
            }
          std::rotate(k, i2, last);
          return true;
        }
    }
  std::rotate(first, k, last);
  return false;
}

PeptideVariableModificationBuilder::PeptideVariableModificationBuilder(AaModificationP mod)
  : mp_mod(mod)
{
}

PeptideVariableModificationBuilder::~PeptideVariableModificationBuilder()
{
}

void
PeptideVariableModificationBuilder::addAa(char aa)
{

  m_aaModificationList.append(aa);

  m_pattern.setPattern(QString("[%1]").arg(m_aaModificationList));
}

void
PeptideVariableModificationBuilder::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)
{
  // QString s = "Banana";
  // s.replace(QRegExp("a[mn]"), "ox");


  bool modify_this_peptide = true;
  if(m_isProtNterMod || m_isProtCterMod)
    {
      modify_this_peptide = false;
      if((m_isProtNterMod) && (is_nter))
        {
          // this an Nter peptide
          modify_this_peptide = true;
        }
      else if((m_isProtCterMod) &&
              (protein_sp.get()->size() == (start + peptide_sp_original.get()->size())))
        {
          // this is a Cter peptide
          modify_this_peptide = true;
        }
      else if(m_isProtElseMod)
        {
          modify_this_peptide = true;
        }
    }

  if(modify_this_peptide)
    {

      std::vector<unsigned int> position_list;
      getModificationPositionList(
        position_list, peptide_sp_original.get(), mp_mod, m_modificationCount);


      // std::vector< unsigned int > position_list =
      // peptide_sp_original.get()->getAaPositionList(m_aaModificationList);
      // std::string s = "12345";
      // no AA modification :
      if(m_minNumberMod == 0)
        {
          m_sink->setPeptideSp(sequence_database_id,
                               protein_sp,
                               is_decoy,
                               peptide_sp_original,
                               start,
                               is_nter,
                               missed_cleavage_number,
                               semi_enzyme);
        }

      unsigned int nb_pos = position_list.size();
      if(nb_pos > 0)
        {
          // loop to find 1 to n-1 AA modification combinations
          unsigned int comb_size = 1;
          while((comb_size < nb_pos) && (comb_size <= m_maxNumberMod))
            {
              do
                {
                  // std::cout << std::string(being,begin + comb_size) <<
                  // std::endl;
                  Peptide new_peptide(*(peptide_sp_original.get()));
                  for(unsigned int i = 0; i < comb_size; i++)
                    {
                      new_peptide.addAaModification(mp_mod, position_list[i]);
                    }
                  PeptideSp new_peptide_sp = new_peptide.makePeptideSp();
                  m_sink->setPeptideSp(sequence_database_id,
                                       protein_sp,
                                       is_decoy,
                                       new_peptide_sp,
                                       start,
                                       is_nter,
                                       missed_cleavage_number,
                                       semi_enzyme);
                }
              while(next_combination(
                position_list.begin(), position_list.begin() + comb_size, position_list.end()));
              comb_size++;
            }

          if(nb_pos <= m_maxNumberMod)
            {
              // the last combination : all aa are modified :
              Peptide new_peptide(*(peptide_sp_original.get()));
              for(unsigned int i = 0; i < nb_pos; i++)
                {
                  new_peptide.addAaModification(mp_mod, position_list[i]);
                }
              PeptideSp new_peptide_sp = new_peptide.makePeptideSp();
              m_sink->setPeptideSp(sequence_database_id,
                                   protein_sp,
                                   is_decoy,
                                   new_peptide_sp,
                                   start,
                                   is_nter,
                                   missed_cleavage_number,
                                   semi_enzyme);
            }
        }
    }
  else
    {
      // no modification
      m_sink->setPeptideSp(sequence_database_id,
                           protein_sp,
                           is_decoy,
                           peptide_sp_original,
                           start,
                           is_nter,
                           missed_cleavage_number,
                           semi_enzyme);
    }
}

} // namespace pappso
