
/*******************************************************************************
 * Copyright (c) 2015 Olivier Langella <Olivier.Langella@moulon.inra.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/>.
 *
 ******************************************************************************/
#include "project.h"
#include "../input/xpipreader.h"
#include "../input/sage/sagefilereader.h"
#include "../input/psmcbor/cborpsmfilereader.h"
#include "../input/mzidentml/mzidentmlreader.h"
#include "peptidematch.h"
#include "proteinmatch.h"
#include <QDebug>
#include <pappsomspp/core/exception/exceptionnotfound.h>
#include <pappsomspp/core/exception/exceptionnotrecognized.h>
#include "./qvalue/computeqvalues.h"
#include "../config.h"

Project::Project()
{
  msp_masschroqFileParametersSp = std::make_shared<MasschroqFileParameters>();
  msp_masschroqFileParametersSp.get()->load(
    ProposedMassSpectrometerParameterFamily::undefined);
  // msp_masschroqFileParametersSp.get()->m_zivyParams.loadSettings();
}

Project::~Project()
{
  auto it = _identification_goup_list.begin();
  while(it != _identification_goup_list.end())
    {
      delete(*it);
      it++;
    }
}

bool
Project::checkPsimodCompliance() const
{
  return _peptide_store.checkPsimodCompliance();
}
void
Project::readResultFile(pappso::UiMonitorInterface *p_monitor, QString filename)
{
  bool is_ok = false;
  if(QFileInfo(filename).suffix() == "cbor")
    {
      try
        {
          CborPsmFileReader cbor_psm_file_reader(this);

          QFile cbor_file(QFileInfo(filename).absoluteFilePath());
          if(!cbor_file.open(QFile::ReadOnly))
            {
              throw pappso::ExceptionNotRecognized(
                QObject::tr("Unable to read CBOR PSM file %1")
                  .arg(QFileInfo(filename).absoluteFilePath()));
            }

          cbor_psm_file_reader.readCbor(&cbor_file, *p_monitor);
          // SageFileReader sage_file_reader(p_monitor, this,
          // QFileInfo(filename));
          cbor_file.close();
          cbor_psm_file_reader.close();

          // return;
          is_ok = true;
        }
      catch(pappso::PappsoException &err)
        {
          throw pappso::PappsoException(
            QObject::tr("Error reading CBOR PSM file %1:\n%2")
              .append(QFileInfo(filename).absoluteFilePath())
              .append(err.qwhat()));
        }
    }
  else
    {
      try
        {
          SageFileReader sage_file_reader(p_monitor, this, QFileInfo(filename));
          is_ok = true;
        }
      catch(pappso::ExceptionNotRecognized &err)
        {
          qWarning() << err.qwhat();
        }
    }

  if(!is_ok)
    {
      try
        {
          MzIdentMlReader mzident_file_reader(
            p_monitor, this, QFileInfo(filename));


          is_ok = true;
        }

      catch(pappso::ExceptionNotRecognized &err)
        {
          qWarning() << err.qwhat();
        }
      catch(pappso::PappsoException &err)
        {
          throw pappso::PappsoException(
            QObject::tr("Error while reading mzIdentML file %1 :\n%2")
              .arg(QFileInfo(filename).absoluteFilePath())
              .arg(err.qwhat()));
        }
    }
  if(!is_ok)
    {
      IdentificationDataSourceSp ident_source =
        _identification_data_source_store.getInstance(filename);

      ident_source.get()->parseTo(p_monitor, this);
    }
  qDebug();
}

ProjectMode
Project::getProjectMode() const
{
  return _project_mode;
}
void
Project::setProjectMode(ProjectMode mode)
{
  _project_mode = mode;
}
std::vector<IdentificationGroup *>
Project::getIdentificationGroupList()
{
  return _identification_goup_list;
}

const std::vector<IdentificationGroup *>
Project::getIdentificationGroupList() const
{
  return _identification_goup_list;
}
GroupingType
Project::getGroupingType() const
{
  return m_groupingType;
}

FastaFileStore &
Project::getFastaFileStore()
{
  return _fasta_file_store;
}

const FastaFileStore &
Project::getFastaFileStore() const
{
  return _fasta_file_store;
}
MsRunStore &
Project::getMsRunStore()
{
  return _msrun_store;
}
const MsRunStore &
Project::getMsRunStore() const
{
  return _msrun_store;
}
PeptideStore &
Project::getPeptideStore()
{
  return _peptide_store;
}

ProteinStore &
Project::getProteinStore()
{
  return _protein_store;
}

const ProteinStore &
Project::getProteinStore() const
{
  return _protein_store;
}

IdentificationDataSourceStore &
Project::getIdentificationDataSourceStore()
{
  return _identification_data_source_store;
}
const IdentificationDataSourceStore &
Project::getIdentificationDataSourceStore() const
{
  return _identification_data_source_store;
}
const AutomaticFilterParameters &
Project::getAutomaticFilterParameters() const
{
  return _automatic_filter_parameters;
}

ContaminantRemovalMode
Project::getContaminantRemovalMode() const
{
  return m_contaminantRemovalMode;
}
void
Project::setContaminantRemovalMode(
  ContaminantRemovalMode contaminant_removal_mode)
{
  m_contaminantRemovalMode = contaminant_removal_mode;
}
void
Project::updateAutomaticFilters(
  const AutomaticFilterParameters &automatic_filter_parameters)
{

  qDebug();
  _automatic_filter_parameters = automatic_filter_parameters;

  if(automatic_filter_parameters.getAutomaticFilterType() ==
     AutomaticFilterType::fdr)
    {
      qDebug();
      // ensure qvalues are computed
      ComputeQvalues values(this);
    }

  for(auto &p_id_group : _identification_goup_list)
    {
      p_id_group->updateAutomaticFilters(_automatic_filter_parameters);
    }
  qDebug();
}
ProjectSp
Project::makeProjectSp() const
{
  return std::make_shared<Project>(*this);
}

IdentificationGroup *
Project::newIdentificationGroup()
{
  _p_current_identification_group = new IdentificationGroup(this);
  _identification_goup_list.push_back(_p_current_identification_group);
  return _p_current_identification_group;
}
void
Project::readXpipFile(WorkMonitorInterface *p_monitor, QFileInfo xpip_fileinfo)
{
  qDebug();
  XpipReader xpip_reader(p_monitor, this);

  qDebug() << " Read XPIP XML result file '" << xpip_fileinfo.absoluteFilePath()
           << "'";

  if(xpip_reader.readFile(xpip_fileinfo.absoluteFilePath()))
    {
    }
  else
    {
      if(xpip_reader.isI2masschroqXpip())
        {
          throw pappso::PappsoException(
            QObject::tr("Error reading %1 XPIP file :\n %2")
              .arg(xpip_fileinfo.absoluteFilePath())
              .arg(xpip_reader.errorString()));
        }
      else
        {
          qDebug();
          throw pappso::PappsoException(
            QObject::tr("Error reading %1 XPIP file :\n it is not an XPIP file")
              .arg(xpip_fileinfo.absoluteFilePath()));
        }
    }
}


void
Project::startGrouping(WorkMonitorInterface *p_work_monitor)
{
  qDebug();
  for(IdentificationGroup *p_id_group : _identification_goup_list)
    {
      p_id_group->startGrouping(
        m_contaminantRemovalMode, m_groupingType, p_work_monitor);
    }
  qDebug();
}
void
Project::setLabelingMethodSp(LabelingMethodSp labeling_method_sp)
{
  _labeling_method_sp = labeling_method_sp;
  _peptide_store.setLabelingMethodSp(labeling_method_sp);
}

LabelingMethodSp
Project::getLabelingMethodSp() const
{
  return _labeling_method_sp;
}

bool
Project::hasPtmExperiment() const
{
  if(getIdentificationGroupList().at(0)->getPtmGroupingExperiment() != nullptr)
    return true;
  return false;
}

void
Project::getSameXicPeptideEvidenceList(
  std::vector<const PeptideEvidence *> &peptide_evidence_list,
  const MsRun *p_msrun,
  const PeptideXtp *p_peptide,
  unsigned int charge) const
{
  for(const IdentificationGroup *p_ident_group : _identification_goup_list)
    {
      p_ident_group->getSameXicPeptideEvidenceList(
        peptide_evidence_list, p_msrun, p_peptide, charge);
    }
}

void
Project::prepareMsrunRetentionTimesForAlignment() const
{
  qDebug();

  for(auto &msrun_sp : this->getMsRunStore().getMsRunList())
    {
      msrun_sp.get()->clearMsRunRetentionTime();
    }

  qDebug();
  bool is_ok = true;
  PeptideEvidenceStore empty_store;
  for(IdentificationDataSourceSp p_ident_data_source :
      this->getIdentificationDataSourceStore()
        .getIdentificationDataSourceList())
    {


      std::map<MsRun *, std::vector<PeptideEvidence *>>
        peptide_evidence_list_by_msrun;
      for(auto &peptide_evidence :
          p_ident_data_source->getPeptideEvidenceStore()
            .getPeptideEvidenceList())
        {
          if(peptide_evidence.get()->isValid())
            {

              MsRun *p_msrun = peptide_evidence.get()->getMsRunPtr();
              auto ret       = peptide_evidence_list_by_msrun.insert(
                std::pair<MsRun *, std::vector<PeptideEvidence *>>(
                  p_msrun, std::vector<PeptideEvidence *>()));
              if(ret.second == false)
                {
                  // "element 'z' already existed";
                }

              ret.first->second.push_back(peptide_evidence.get());
              qDebug() << "msrun=" << p_msrun->getRunId()
                       << " size pe=" << ret.first->second.size();
            }
        }

      for(auto &msrun_list_element : peptide_evidence_list_by_msrun)
        {
          MsRun *p_msrun = msrun_list_element.first;

          qDebug();
          if(p_msrun->getMsRunRetentionTimePtr() == nullptr)
            {
              p_msrun->buildMsRunRetentionTime(empty_store);
            }

          qDebug();
          for(auto &peptide_evidence : msrun_list_element.second)
            {
              qDebug();
              try
                {
                  if(peptide_evidence->isValid())
                    {
                      p_msrun->getMsRunRetentionTimePtr()->addPeptideAsSeamark(
                        peptide_evidence->getPeptideXtpSp()
                          .get()
                          ->getNativePeptideP(),
                        peptide_evidence->getRetentionTime(),
                        1);
                    }
                }
              catch(pappso::PappsoException &error)
                {

                  throw pappso::PappsoException(
                    QString("Error while processing "
                            "peptide_evidence_list_by_msrun at "
                            "evidence %1, size=%2"
                            ":\n %3")
                      .arg(peptide_evidence->getPeptideXtpSp()
                             .get()
                             ->getLiAbsoluteString())
                      .arg(msrun_list_element.second.size())
                      .arg(error.qwhat()));
                }
              qDebug();
            }
          qDebug();
        }
    }
  qDebug();

  if(is_ok)
    {
      for(auto &msrun_sp : this->getMsRunStore().getMsRunList())
        {
          msrun_sp->computeMsRunRetentionTime();
        }
      // find the best reference
    }
  else
    {
      for(auto &msrun_sp : this->getMsRunStore().getMsRunList())
        {
          msrun_sp->clearMsRunRetentionTime();
        }
    }
  qDebug();
}

void
Project::setProjectName(const QString &name)
{
  m_project_name = QFileInfo(name).baseName();
}

QString
Project::getProjectName()
{
  return m_project_name;
}

void
Project::setProjectChangedStatus(bool updated_data)
{
  m_project_changed = updated_data;
}


bool
Project::getProjectChangedStatus() const
{
  return m_project_changed;
}

std::vector<MsRunAlignmentGroupSp>
Project::getMsRunAlignmentGroupList()
{
  return m_msRunAlignmentGroupList;
}

void
Project::addMsRunAlignmentGroupToList(MsRunAlignmentGroupSp new_group)
{
  m_msRunAlignmentGroupList.push_back(new_group);
}

MsRunAlignmentGroupSp
Project::getMsRunAlignmentGroupFromList(QString group_name)
{
  foreach(MsRunAlignmentGroupSp alignment_group, getMsRunAlignmentGroupList())
    {
      if(alignment_group->getMsRunAlignmentGroupName() == group_name)
        {
          return alignment_group;
        }
    }
  return nullptr;
}

MsRunAlignmentGroupSp
Project::getAllMsRunAlignmentGroup()
{
  return m_allMsRunAlignmentGroup;
}

void
Project::setAllMsRunAlignmentGroup(MsRunAlignmentGroupSp all_msrun_grouped)
{
  m_allMsRunAlignmentGroup = all_msrun_grouped;
}

PtmMode
Project::getPtmMode() const
{
  return m_ptmMode;
}

void
Project::setPtmMode(PtmMode mode)
{
  m_ptmMode = mode;
}

bool
Project::isTarget(PeptideEvidence *p_pe) const
{

  for(IdentificationGroup *p_ident_group : getIdentificationGroupList())
    {
      for(auto &&p_protein_match : p_ident_group->getProteinMatchList())
        {
          if(p_protein_match->getProteinXtpSp().get()->isDecoy())
            {
              continue;
            }
          else
            {
              if(p_protein_match->contains(p_pe))
                return true;
            }
        }
    }
  return false;
}

const MasschroqFileParametersSp &
Project::getMasschroqFileParametersSp() const
{
  // msp_masschroqFileParametersSp.get()->load(
  //  getProposedMassSpectrometerParameterFamily());
  return msp_masschroqFileParametersSp;
}

TiddComputingSp
Project::getTiddComputingSp()
{
  if(msp_tiddComputingSp.get() == nullptr)
    {
      msp_tiddComputingSp = std::make_shared<TiddComputing>();
    }
  return msp_tiddComputingSp;
}

ProposedMassSpectrometerParameterFamily
Project::getProposedMassSpectrometerParameterFamily() const
{
  qDebug();
  try
    {
      ProposedMassSpectrometerParameterFamily family =
        ProposedMassSpectrometerParameterFamily::undefined;
      for(auto &msrun : getMsRunStore().getMsRunList())
        {
          qDebug();
          // qDebug() << (int)msrun.get()->getMzFormat();
          if(msrun.get()->getMsDataFormat() ==
             pappso::Enums::MsDataFormat::brukerTims)
            {
              family = ProposedMassSpectrometerParameterFamily::BrukerTimsTOF;
              break;
            }
          if(msrun.get()->getMsDataFormat() ==
             pappso::Enums::MsDataFormat::thermoRaw)
            {
              family = ProposedMassSpectrometerParameterFamily::ThermoQexLumos;
              break;
            }
        }

      if(family == ProposedMassSpectrometerParameterFamily::undefined)
        {
          pappso::ProjectParameters parameters = getProjectParameters();

          QVariant project_param_value =
            parameters.getValue(pappso::ProjectParamCategory::instrument,
                                "cvparam_SpectraDataFileFormat");
          if(!project_param_value.isNull() &&
             project_param_value.toString() == "MS:1002817")
            { // Bruker TDF format
              family = ProposedMassSpectrometerParameterFamily::BrukerTimsTOF;
            }
          project_param_value =
            parameters.getValue(pappso::ProjectParamCategory::instrument,
                                "cvparam_SpectraDataSpectrumIDFormat");
          if(!project_param_value.isNull() &&
             project_param_value.toString() == "MS:1000768")
            { // Thermo nativeID format
              family = ProposedMassSpectrometerParameterFamily::ThermoQexLumos;
            }
        }
      qDebug() << "family:" << (std::uint8_t)family;

      return family;
    }
  catch(const pappso::PappsoException &error)
    {
      throw pappso::PappsoException(QObject::tr("Error in %1 %2 %3 :\n%4")
                                      .arg(__FUNCTION__)
                                      .arg(__FILE__)
                                      .arg(__LINE__)
                                      .arg(error.qwhat()));
    }
}

McqrMetadataSet
Project::getMcqrMetadataSet() const
{
  McqrMetadataSet mcqr_metadata_set;

  for(auto &msrun : getMsRunStore().getMsRunList())
    {
      //<data_file id="msruna1" format="mzxml"
      // path="/gorgone/pappso/data_extraction_pappso/mzXML/20120906_balliau_extract_1_A01_urnb-1.mzXML"
      // type="centroid"/>
      McqrMetadata metadata;
      metadata.m_msrunId = msrun.get()->getXmlId();

      metadata.m_msrunFile = msrun.get()->getSampleName();
      mcqr_metadata_set.push_back(metadata);
    }

  return mcqr_metadata_set;
}

pappso::ProjectParameters
Project::getProjectParameters() const
{
  pappso::ProjectParameters parameters;

  pappso::ProjectParam param({pappso::ProjectParamCategory::general,
                              "i2MassChroQ_VERSION",
                              QVariant(i2MassChroQ_VERSION)});
  parameters.setProjectParam(param);

  getIdentificationDataSourceStore().fillProjectParameters(parameters);


  param.name  = "i2MassChroQ_project_name";
  param.value = m_project_name;
  parameters.setProjectParam(param);

  getIdentificationDataSourceStore().fillProjectParameters(parameters);


  std::vector<IdentificationGroup *> identification_list =
    getIdentificationGroupList();
  for(IdentificationGroup *p_ident : identification_list)
    {
      pappso::ProjectParam project_param(
        {pappso::ProjectParamCategory::filter, "", QVariant()});
      project_param.name = "psm_fdr";
      project_param.value.setValue(p_ident->getPsmFdr());
      parameters.setProjectParam(project_param);

      project_param.name = "peptide_fdr";
      project_param.value.setValue(p_ident->getPeptideMassFdr());
      parameters.setProjectParam(project_param);
      project_param.name = "protein_fdr";
      project_param.value.setValue(p_ident->getProteinFdr());
      parameters.setProjectParam(project_param);
    }

  _automatic_filter_parameters.fillProjectParameters(parameters);
  return parameters;
}
