
/*******************************************************************************
 * 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 "grppeptideset.h"
#include "grpsubgroup.h"
#include "grppeptide.h"
#include "grpsubgroupset.h"
#include "grpgroup.h"
#include "grpexperiment.h"
#include "../pappsoexception.h"
#include "../utils.h"

using namespace pappso;

GrpGroup::GrpGroup(GrpSubGroupSp &grpSubGroupSp)
{
  m_peptideSet.addAll(grpSubGroupSp.get()->getPeptideSet());
  m_subGroupList.push_back(grpSubGroupSp);
  m_mapPeptideToSubGroupSet.add(grpSubGroupSp.get());
}
GrpGroup::GrpGroup(const GrpGroup &other)
  : m_subGroupList(other.m_subGroupList),
    m_peptideSet(other.m_peptideSet),
    m_mapPeptideToSubGroupSet(other.m_mapPeptideToSubGroupSet)
{
}

GrpGroup::~GrpGroup()
{
}

std::vector<GrpSubGroupSpConst>
GrpGroup::getGrpSubGroupSpList() const
{
  std::vector<GrpSubGroupSpConst> subgroup_list;
  for(GrpSubGroupSp subgroup : m_subGroupList)
    {
      subgroup_list.push_back(subgroup);
    }
  return subgroup_list;
};

const std::list<GrpSubGroupSp> &
GrpGroup::getSubGroupSpList() const
{
  return m_subGroupList;
}

const GrpPeptideSet &
GrpGroup::getGrpPeptideSet() const
{
  return m_peptideSet;
}
unsigned int
GrpGroup::getGroupNumber() const
{
  return m_groupNumber;
}
const QString
GrpGroup::getGroupingId() const
{
  if(m_groupNumber == 0)
    {
      return "";
    }
  return QString("%1").arg(Utils::getLexicalOrderedString(m_groupNumber));
}
bool
GrpGroup::operator<(const GrpGroup &other) const
{
  return ((*(m_subGroupList.begin()->get())) <
          (*(other.m_subGroupList.begin()->get())));
}

GrpGroupSp
GrpGroup::makeGrpGroupSp()
{
  return std::make_shared<GrpGroup>(*this);
}

bool
GrpGroup::containsAny(const GrpPeptideSet &peptideSet) const
{

  return m_peptideSet.containsAny(peptideSet);
}

void
GrpGroup::addGroup(GrpGroup *p_group_to_add)
{
  if(this == p_group_to_add)
    {
      throw PappsoException(
        QObject::tr("addGroup ERROR, this == p_group_to_add"));
    }

  for(GrpSubGroupSp &sgToAdd : p_group_to_add->m_subGroupList)
    {
      addSubGroupSp(sgToAdd);
    }
}

void
GrpGroup::check() const
{
  qDebug() << "GrpGroup::check begin ";
  GrpSubGroupSet impacted_subgroup_list;
  m_mapPeptideToSubGroupSet.getSubGroupSet(this->m_peptideSet,
                                           impacted_subgroup_list);

  // if (impacted_subgroup_list.size() != this->m_subGroupList.size()) {
  qDebug() << "GrpGroup::check impacted_subgroup_list.size() != "
              "this->m_subGroupList.size()";
  qDebug() << impacted_subgroup_list.printInfos();

  for(auto sg : m_subGroupList)
    {
      qDebug() << sg->getFirstAccession() << " " << sg.get();
    }
  //}
  qDebug() << m_mapPeptideToSubGroupSet.printInfos();
  qDebug() << m_peptideSet.printInfos();
  qDebug() << "GrpGroup::check end ";
}


void
GrpGroup::addSubGroupSp(const GrpSubGroupSp &grpSubGroupSp)
{

  qDebug() << "GrpGroup::addSubGroupSp begin "
           << grpSubGroupSp.get()->getFirstAccession();


  // look for impacted subgroups (containing peptides alsoe present in incoming
  // subgroup
  GrpSubGroupSet impacted_subgroup_list;
  m_mapPeptideToSubGroupSet.getSubGroupSet(grpSubGroupSp.get()->getPeptideSet(),
                                           impacted_subgroup_list);
  if(impacted_subgroup_list.contains(grpSubGroupSp.get()))
    {
      throw PappsoException(
        QObject::tr("addSubGroupSp ERROR, subgroup %1 is already in group")
          .arg(grpSubGroupSp.get()->getFirstAccession()));
    }

  // look for impacted subgroup equal to incoming subgroup and merge it and exit
  qDebug() << "GrpGroup::addSubGroupSp look for impacted subgroup equal to "
              "incoming subgroup and merge it and exit";
  auto it_impacted_sg     = impacted_subgroup_list.m_grpSubGroupPtrList.begin();
  auto it_impacted_sg_end = impacted_subgroup_list.m_grpSubGroupPtrList.end();
  while(it_impacted_sg != it_impacted_sg_end)
    {

      if((*it_impacted_sg)->merge(grpSubGroupSp.get()))
        {
          qDebug() << "GrpGroup::addSubGroupSp merge";
          return;
        }
      it_impacted_sg++;
    }

  // look for impacted subgroup including totally the incoming subgroup and exit
  qDebug() << "GrpGroup::addSubGroupSp look for impacted subgroup including "
              "totally the incoming subgroup and exit";
  it_impacted_sg = impacted_subgroup_list.m_grpSubGroupPtrList.begin();
  while(it_impacted_sg != it_impacted_sg_end)
    {
      if((*it_impacted_sg)->includes(grpSubGroupSp.get()))
        {
          qDebug() << "GrpGroup::addSubGroupSp includes";
          return;
        }
      it_impacted_sg++;
    }

  // look for impacted subgroup totally included in incoming subgroup and remove
  // it
  qDebug() << "GrpGroup::addSubGroupSp look for impacted subgroup totally "
              "included in incoming subgroup and remove it";
  it_impacted_sg = impacted_subgroup_list.m_grpSubGroupPtrList.begin();
  while(it_impacted_sg != it_impacted_sg_end)
    {
      if(grpSubGroupSp.get()->includes((*it_impacted_sg)))
        {
          // this->removeSubGroup(*it_impacted_sg);
          // it_impacted_sg = impacted_subgroup_list.erase(it_impacted_sg);
          GrpSubGroup *sg_to_remove = *it_impacted_sg;
          it_impacted_sg = impacted_subgroup_list.erase(it_impacted_sg);
          m_mapPeptideToSubGroupSet.remove(sg_to_remove);
          m_subGroupList.remove_if([sg_to_remove](GrpSubGroupSp &sub_groupSp) {
            return (sg_to_remove == sub_groupSp.get());
          });
          // m_mapPeptideToSubGroupSet.check(m_subGroupList);
        }
      else
        {
          it_impacted_sg++;
        }
    }
  qDebug()
    << "GrpGroup::addSubGroupSp finally add the new subgroup to current group";

  // finally add the new subgroup to current group
  m_subGroupList.push_back(grpSubGroupSp);
  m_mapPeptideToSubGroupSet.add(grpSubGroupSp.get());
  m_peptideSet.addAll(grpSubGroupSp.get()->getPeptideSet());


  // check();
  // m_mapPeptideToSubGroupSet.check(m_subGroupList);

  qDebug() << "GrpGroup::addSubGroupSp end";
}


void
GrpGroup::setGroupNumber(unsigned int i)
{
  qDebug() << "GrpGroup::setGroupNumber begin";
  m_groupNumber = i;
  for(auto &&sub_group_sp : m_subGroupList)
    {
      sub_group_sp->setGroupNumber(i);
    }
  m_peptideSet.setGroupNumber(i);
  qDebug() << "GrpGroup::setGroupNumber end";
}


void
GrpGroup::numbering()
{
  qDebug() << "GrpGroup::numbering begin";

  for(auto &&sub_group_sp : m_subGroupList)
    {
      sub_group_sp.get()->numbering();
    }
  m_subGroupList.sort([](GrpSubGroupSp &first, GrpSubGroupSp &second) {
    return ((*first.get()) < (*second.get()));
  });
  unsigned int i = 1;
  for(auto &&sub_group_sp : m_subGroupList)
    {
      sub_group_sp.get()->setSubGroupNumber(i);
      i++;
    }

  m_peptideSet.numbering();
  qDebug() << "GrpGroup::numbering end";
}


bool
GrpGroup::removeFirstNonInformativeSubGroup()
{
  qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup begin";
  std::list<GrpSubGroup *> subgroup_list_to_remove;

  for(auto &&sub_group_sp : m_subGroupList)
    {
      if(m_mapPeptideToSubGroupSet.hasSpecificPeptide(sub_group_sp.get()))
        {
        }
      else
        {
          subgroup_list_to_remove.push_back(sub_group_sp.get());
        }
    }
  if(subgroup_list_to_remove.size() == 0)
    {
      qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup end false";
      return false;
    }

  // sort subgroup to remove
  subgroup_list_to_remove.sort([](GrpSubGroup *first, GrpSubGroup *second) {
    return ((*first) < (*second));
  });

  // remove the last one
  qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup "
              "subgroup_list_to_remove.front()->peptideListSize() "
           << subgroup_list_to_remove.front()->peptideListSize();
  qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup "
              "subgroup_list_to_remove.back()->peptideListSize() "
           << subgroup_list_to_remove.back()->peptideListSize();

  // the first subgroup is weaker (less peptides or less protein than others)
  GrpSubGroup *sg_to_remove = subgroup_list_to_remove.front();
  m_mapPeptideToSubGroupSet.remove(sg_to_remove);
  qDebug()
    << "GrpGroup::removeFirstNonInformativeSubGroup m_subGroupList.size() "
    << m_subGroupList.size();
  m_subGroupList.remove_if([sg_to_remove](GrpSubGroupSp &sub_groupSp) {
    return (sg_to_remove == sub_groupSp.get());
  });
  qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup after remove if "
              "m_subGroupList.size() "
           << m_subGroupList.size();
  qDebug() << "GrpGroup::removeFirstNonInformativeSubGroup end true";
  return true;
}

bool
GrpGroup::removeNonInformativeSubGroups()
{
  qDebug() << "GrpGroup::removeNonInformativeSubGroups begin";
  if(removeFirstNonInformativeSubGroup())
    {
      while(removeFirstNonInformativeSubGroup())
        {
          qDebug() << "GrpGroup::removeNonInformativeSubGroups while";
        }
    }
  else
    {
      return false;
    }
  return true;
}
