/**
 * \file grouping/ptm/ptmisland.cpp
 * \date 24/5/2017
 * \author Olivier Langella
 * \brief store overlapping peptides containing PTMs
 */

/*******************************************************************************
 * 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 "ptmisland.h"
#include <pappsomspp/core/pappsoexception.h>
#include "ptmislandsubgroup.h"
#include "ptmislandgroup.h"
#include "ptmgroupingexperiment.h"
#include <pappsomspp/core/utils.h>
#include <set>

PtmIsland::PtmIsland(ProteinMatch *p_protein_match, unsigned int position)
  : _protein_match_p(p_protein_match)
{
  _position_list.push_back(position);
}


PtmIsland::PtmIsland(const PtmIsland &other)
  : _protein_match_p(other._protein_match_p)
{
  _position_list = other._position_list;
}
PtmIsland::~PtmIsland()
{
}
unsigned int
PtmIsland::getStart() const
{
  return _protein_start;
}
unsigned int
PtmIsland::size() const
{
  return _protein_stop - _protein_start;
}

bool
PtmIsland::isChecked() const
{
  return m_checked;
}

void
PtmIsland::setChecked(bool check_status)
{
  m_checked = check_status;
}

unsigned int
PtmIsland::countSampleScanMultiPtm(
  const PtmGroupingExperiment *p_ptm_grouping_experiment) const
{
  std::set<std::size_t> sample_scan_list;
  for(auto &p_peptide_match : _peptide_match_list)
    {
      if(p_ptm_grouping_experiment->countPeptideMatchPtm(p_peptide_match) > 1)
        {
          sample_scan_list.insert(
            p_peptide_match.getPeptideEvidence()->getHashSampleScan());
        }
    }
  return sample_scan_list.size();
}
std::size_t
PtmIsland::countSampleScanWithPtm(
  const PtmGroupingExperiment *p_ptm_grouping_experiment) const
{
  std::set<std::size_t> sample_scan_list;
  for(auto &p_peptide_match : _peptide_match_list)
    {
      if(p_ptm_grouping_experiment->countPeptideMatchPtm(p_peptide_match) >= 1)
        {
          sample_scan_list.insert(
            p_peptide_match.getPeptideEvidence()->getHashSampleScan());
        }
    }
  return sample_scan_list.size();
}

std::size_t
PtmIsland::countSequence() const
{
  std::set<QString> sequence_list;
  for(auto &peptide_match : _peptide_match_list)
    {
      sequence_list.insert(peptide_match.getPeptideEvidence()
                             ->getPeptideXtpSp()
                             .get()
                             ->getSequence());
    }
  // qDebug() <<"ProteinMatch::countValidAndCheckedPeptide end " <<
  // sequence_list.size();
  return sequence_list.size();
}
const PtmIslandSubgroup *
PtmIsland::getPtmIslandSubroup() const
{
  return _ptm_island_subgroup_p;
}
const std::vector<unsigned int> &
PtmIsland::getPositionList() const
{
  return _position_list;
}
const QString
PtmIsland::getGroupingId() const
{
  if(_prot_number == 0)
    {
      return "";
    }
  return QString("ptm%1%2%3")
    .arg(pappso::Utils::getLexicalOrderedString(
      _ptm_island_subgroup_p->getPtmIslandGroup()->getGroupNumber()))
    .arg(pappso::Utils::getLexicalOrderedString(
      _ptm_island_subgroup_p->getSubgroupNumber()))
    .arg(pappso::Utils::getLexicalOrderedString(_prot_number));
}
void
PtmIsland::setPtmIslandSubgroup(PtmIslandSubgroup *p_ptm_island_subgroup)
{
  _ptm_island_subgroup_p = p_ptm_island_subgroup;
}
void
PtmIsland::setProteinNumber(unsigned int prot_number)
{
  _prot_number = prot_number;
}
ProteinMatch *
PtmIsland::getProteinMatch()
{
  return _protein_match_p;
}
std::vector<std::size_t>
PtmIsland::getSampleScanSet() const
{
  std::vector<std::size_t> sample_scan_set;
  for(const PeptideMatch &peptide_match : _peptide_match_list)
    {
      sample_scan_set.push_back(
        peptide_match.getPeptideEvidence()->getHashSampleScan());
    }
  std::sort(sample_scan_set.begin(), sample_scan_set.end());
  sample_scan_set.erase(
    std::unique(sample_scan_set.begin(), sample_scan_set.end()),
    sample_scan_set.end());
  return sample_scan_set;
}
bool
PtmIsland::containsPeptideMatch(const PeptideMatch &peptide_match) const
{

  return std::any_of(_peptide_match_list.begin(),
                     _peptide_match_list.end(),
                     [peptide_match](const PeptideMatch &element) {
                       return (peptide_match == element);
                     });
}

void
PtmIsland::addPeptideMatch(const PeptideMatch &peptide_match)
{
  if(_position_list.size() == 1)
    {
      unsigned int position = _position_list[0];
      qDebug() << position;
      if(peptide_match.containsPosition(position))
        {
          if(_peptide_match_list.size() == 0)
            {
              _protein_start = peptide_match.getStart();
            }
          _peptide_match_list.push_back(peptide_match);
          if(peptide_match.getStart() < _protein_start)
            {
              _protein_start = peptide_match.getStart();
            }
          if(peptide_match.getStop() > _protein_stop)
            {
              _protein_stop = peptide_match.getStop();
            }
        }
    }
  else
    {
      throw pappso::PappsoException(
        QObject::tr(
          "not able to add peptide match if _position_list.size()!=1 (%1)")
          .arg(_position_list.size()));
    }
}

unsigned int
PtmIsland::getProteinStartPosition() const
{
  return _protein_start;
}

bool
PtmIsland::merge(PtmIslandSp ptm_island_sp)
{
  if(std::none_of(_peptide_match_list.begin(),
                  _peptide_match_list.end(),
                  [ptm_island_sp](const PeptideMatch &element) {
                    return ptm_island_sp.get()->containsPeptideMatch(element);
                  }))
    {
      return false;
    }
  else
    {
      // so we merge
      _peptide_match_list.insert(
        _peptide_match_list.begin(),
        ptm_island_sp.get()->_peptide_match_list.begin(),
        ptm_island_sp.get()->_peptide_match_list.end());
      std::sort(_peptide_match_list.begin(),
                _peptide_match_list.end(),
                [](const PeptideMatch &a, const PeptideMatch &b) {
                  return (a.operator<(b));
                });
      auto last =
        std::unique(_peptide_match_list.begin(), _peptide_match_list.end());
      _peptide_match_list.erase(last, _peptide_match_list.end());

      _position_list.insert(_position_list.begin(),
                            ptm_island_sp.get()->_position_list.begin(),
                            ptm_island_sp.get()->_position_list.end());
      std::sort(_position_list.begin(), _position_list.end());
      _position_list.erase(
        std::unique(_position_list.begin(), _position_list.end()),
        _position_list.end());


      std::vector<PeptideMatch>::const_iterator it_result =
        std::max_element(_peptide_match_list.begin(),
                         _peptide_match_list.end(),
                         [](const PeptideMatch &a, const PeptideMatch &b) {
                           return (a.getStop() < b.getStop());
                         });
      _protein_stop = it_result->getStop();

      it_result =
        std::min_element(_peptide_match_list.begin(),
                         _peptide_match_list.end(),
                         [](const PeptideMatch &a, const PeptideMatch &b) {
                           return a.getStart() < b.getStart();
                         });
      _protein_start = it_result->getStart();
      return true;
    }
}
const std::vector<PeptideMatch> &
PtmIsland::getPeptideMatchList() const
{
  return _peptide_match_list;
}

std::vector<PtmSampleScanSp>
PtmIsland::getPtmSampleScanSpList() const
{
  qDebug() << "PtmIsland::getPtmSampleScanSpList begin";
  std::vector<PtmSampleScanSp> sample_scan_list;
  for(const PeptideMatch &peptide_match : _peptide_match_list)
    {

      std::vector<PtmSampleScanSp>::iterator it_ptm = sample_scan_list.begin();
      std::vector<PtmSampleScanSp>::iterator it_ptm_end =
        sample_scan_list.end();
      while((it_ptm != it_ptm_end) &&
            (it_ptm->get()->add(peptide_match) == false))
        {
          // peptide added
          it_ptm++;
        }
      if(it_ptm == it_ptm_end)
        {
          // peptide NOT added
          sample_scan_list.push_back(
            std::make_shared<PtmSampleScan>(peptide_match));
        }
    }

  qDebug() << "PtmIsland::getPtmSampleScanSpList end";
  return sample_scan_list;
}
