/**
 * \file grouping/ptm/ptmgroupingexperiment.cpp
 * \date 24/5/2017
 * \author Olivier Langella
 * \brief handle grouping experiment based on ptm islands
 */

/*******************************************************************************
 * 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 "ptmgroupingexperiment.h"
#include "../../grouping/ptm/ptmgroupingexperiment.h"
#include "../../utils/ptmbuilder.h"
#include "QDebug"

PtmGroupingExperiment::PtmGroupingExperiment(PtmMode mode)
{
  /*
   * accession	MOD:00696
  name	phosphorylated residue
  PSI-MS label	Phospho
  PSI-MOD label	PhosRes
  diff mono	79.966331
  diff formula	H 1 O 3 P 1

  // dehydrated residue [MOD:00704] -18.010565
  accession	MOD:00416
  name	phosphorylation of an hydroxyl amino acid with prompt loss of phosphate
  PSI-MS label	Methyl
  PSI-MOD label
  diff mono	-18.010565
  diff formula	H -2 O -1


    public boolean is_phopho_modifs() {
      if ((this.modvalue > 79.9) & (this.modvalue < 80.1))
        return true;
      boolean serthre = false;
      if (this.AA.equals("T"))
        serthre = true;
      else if (this.AA.equals("S"))
        serthre = true;
      if ((serthre) & (this.modvalue < -17.9) & (this.modvalue > -18.1))
        return true;
      return false;
    }
  */

  // mpa_ptmClass = new PtmPhospho();
  mpa_ptmClass = PtmBuilder::newPtm(mode);
}

PtmGroupingExperiment::~PtmGroupingExperiment()
{
  delete mpa_ptmClass;
}

const std::vector<PtmIslandSubgroupSp> &
PtmGroupingExperiment::getPtmIslandSubgroupList() const
{
  return _ptm_island_subgroup_list;
}

const std::vector<PtmIslandGroupSp> &
PtmGroupingExperiment::getPtmIslandGroupList() const
{
  return _ptm_island_group_list;
}

const std::vector<PtmIslandSp> &
PtmGroupingExperiment::getPtmIslandList() const
{
  // qDebug() << "PtmGroupingExperiment::getPtmIslandList begin" <<
  // _ptm_island_list.size();
  return _ptm_island_list;
}

void
PtmGroupingExperiment::numbering()
{
  std::sort(_ptm_island_group_list.begin(),
            _ptm_island_group_list.end(),
            [](const PtmIslandGroupSp a, const PtmIslandGroupSp b) {
              return *(a.get()) < *(b.get());
            });

  unsigned int number = 0;
  for(PtmIslandGroupSp ptm_island_group : _ptm_island_group_list)
    {
      number++;
      ptm_island_group.get()->setGroupNumber(number);
    }
}
void
PtmGroupingExperiment::startGrouping()
{
  // we have a list of ptm island subgroups to group :
  for(PtmIslandSubgroupSp ptm_island_subgroup : _ptm_island_subgroup_list)
    {
      if(std::none_of(_ptm_island_group_list.begin(),
                      _ptm_island_group_list.end(),
                      [ptm_island_subgroup](PtmIslandGroupSp element) {
                        return element.get()->mergePtmIslandSubgroupSp(
                          ptm_island_subgroup);
                      }))
        {
          // create a new group
          _ptm_island_group_list.push_back(
            std::make_shared<PtmIslandGroup>(ptm_island_subgroup));
        }
    }
  numbering();
}
void
PtmGroupingExperiment::addPtmIsland(PtmIslandSp ptm_island_sp)
{
  _ptm_island_list.push_back(ptm_island_sp);
  if(std::none_of(_ptm_island_subgroup_list.begin(),
                  _ptm_island_subgroup_list.end(),
                  [ptm_island_sp](PtmIslandSubgroupSp element) {
                    return element.get()->mergePtmIslandSp(ptm_island_sp);
                  }))
    {
      // create a new subgroup
      _ptm_island_subgroup_list.push_back(
        std::make_shared<PtmIslandSubgroup>(ptm_island_sp));
    }
}
void
PtmGroupingExperiment::setValidationState(ValidationState validation_state)
{
  _peptide_validation_state = validation_state;
}
void
PtmGroupingExperiment::addProteinMatch(ProteinMatch *p_protein_match)
{
  if(p_protein_match->getValidationState() >= _peptide_validation_state)
    {
      std::vector<unsigned int> ptm_position_list =
        mpa_ptmClass->getPtmPositions(p_protein_match);
      std::vector<PtmIslandSp> ptm_island_list;
      for(unsigned int position : ptm_position_list)
        {
          ptm_island_list.push_back(
            std::make_shared<PtmIsland>(p_protein_match, position));
        }
      for(PeptideMatch &peptide_match :
          p_protein_match->getPeptideMatchList(_peptide_validation_state))
        {
          for(PtmIslandSp ptm_island_sp : ptm_island_list)
            {
              ptm_island_sp.get()->addPeptideMatch(peptide_match);
            }
        }

      std::vector<PtmIslandSp> protein_ptm_islands =
        mergePeptideMatchPtmIslandList(ptm_island_list);
      //=> ptm island subgroups (same set of sample scan numbers)
      for(PtmIslandSp ptm_island : protein_ptm_islands)
        {
          addPtmIsland(ptm_island);
        }
    }
}

std::vector<PtmIslandSp>
PtmGroupingExperiment::mergePeptideMatchPtmIslandList(
  std::vector<PtmIslandSp> ptm_island_list)
{
  std::vector<PtmIslandSp> new_ptm_island_list;

  for(PtmIslandSp ptm_island_sp : ptm_island_list)
    {
      // http://en.cppreference.com/w/cpp/algorithm/all_any_none_of
      if(std::none_of(new_ptm_island_list.begin(),
                      new_ptm_island_list.end(),
                      [ptm_island_sp](PtmIslandSp element) {
                        return element.get()->merge(ptm_island_sp);
                      }))
        {
          new_ptm_island_list.push_back(ptm_island_sp);
        }
    }

  return new_ptm_island_list;
}

std::size_t
PtmGroupingExperiment::countPeptideMatchPtm(
  const PeptideMatch &peptide_match) const
{

  return mpa_ptmClass->countPeptideMatchPtm(peptide_match);
}

std::vector<unsigned int>
PtmGroupingExperiment::getPtmPositions(const PeptideMatch &peptide_match) const
{
  return mpa_ptmClass->getPtmPositions(peptide_match);
}
