/**
 * \filed core/msrun.cpp
 * \date 5/4/2017
 * \author Olivier Langella
 * \brief describes an MS run (chemical sample injected in a mass spectrometer)
 */


/*******************************************************************************
 * Copyright (c) 2017 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of XTPcpp.
 *
 *     XTPcpp 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.
 *
 *     XTPcpp 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 XTPcpp.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/


#include "msrun.h"
#include <QFileInfo>
#include <QSettings>
#include <QDebug>
#include <QDir>
#include <pappsomspp/exception/exceptionnotfound.h>
#include <pappsomspp/xicextractor/msrunxicextractorfactory.h>
#include "../utils/msrunstatisticshandler.h"
#include <pappsomspp/msfile/msfileaccessor.h>
#include <pappsomspp/exception/exceptionnotimplemented.h>
#include <pappsomspp/msrun/private/timsmsrunreaderms2.h>
#include <pappsomspp/processing/filters/filterpseudocentroid.h>
#include <pappsomspp/processing/filters/filterchargedeconvolution.h>
#include <pappsomspp/processing/filters/filtertriangle.h>
#include <pappsomspp/precision.h>
#include "peptideevidence.h"
#include "alignmentgroup.h"

MsRun::MsRun(const QString &location) : pappso::MsRunId(location)
{
  QFileInfo fileinfo(location);
}

MsRun::MsRun(const MsRun &other) : pappso::MsRunId(other)
{
  _param_stats = other._param_stats;
  // m_centroidOptions = other.m_centroidOptions;
}

MsRun::~MsRun()
{
  if(mpa_msrunRetentionTime != nullptr)
    {
      delete mpa_msrunRetentionTime;
    }
}
pappso::MsRunReaderSPtr &
MsRun::getMsRunReaderSPtr()
{
  // qDebug() << " filename=" << getFileName();
  if(_msrun_reader_sp == nullptr)
    {
      buildMsRunReaderSp();
    }

  return _msrun_reader_sp;
}

void
MsRun::buildMsRunReaderSp()
{
  qDebug() << " runid=" << getRunId() << " xmlid=" << getXmlId();
  pappso::MsFileAccessor *p_accessor = nullptr;

  try
    {

      p_accessor = new pappso::MsFileAccessor(getFileName(), "runa1");

      p_accessor->setPreferedFileReaderType(pappso::MzFormat::brukerTims,
                                            pappso::FileReaderType::tims_ms2);
    }
  catch(pappso::ExceptionNotFound &error)
    {
      qDebug() << "not found";
      _msrun_reader_sp = nullptr;
      /// p_accessor       = nullptr;
      // throw error;
      // file not found, don't throw exception, we'll try another location
    }
  if(p_accessor != nullptr)
    {
      try
        {
          _msrun_reader_sp =
            p_accessor->getMsRunReaderSPtrByRunId(getRunId(), getXmlId());
          delete p_accessor;
          p_accessor = nullptr;

          pappso::TimsMsRunReaderMs2 *tims2_reader =
            dynamic_cast<pappso::TimsMsRunReaderMs2 *>(_msrun_reader_sp.get());
          if(tims2_reader != nullptr)
            {

              std::shared_ptr<pappso::FilterSuite> ms2filter =
                std::make_shared<pappso::FilterSuite>();

              ms2filter.get()->push_back(
                std::make_shared<pappso::FilterChargeDeconvolution>(
                  pappso::PrecisionFactory::getDaltonInstance(0.01)));

              tims2_reader->setMs2FilterCstSPtr(ms2filter);
            }

          this->setMzFormat(
            _msrun_reader_sp.get()->getMsRunId().get()->getMzFormat());
        }

      catch(pappso::PappsoException &error2)
        {
          _msrun_reader_sp = nullptr;
          if(p_accessor != nullptr)
            {
              delete p_accessor;
              p_accessor = nullptr;
            }
          throw error2;
        }
    }
}


void
MsRun::freeMsRunReaderSp()
{
  _msrun_reader_sp = nullptr;
}

void
MsRun::setMsRunStatistics(MsRunStatistics param, const QVariant &value)
{
  _param_stats.insert(std::pair<MsRunStatistics, QVariant>(param, value));
}

const std::map<MsRunStatistics, QVariant> &
MsRun::getMsRunStatisticsMap() const
{
  return _param_stats;
}

const QVariant
MsRun::getMsRunStatistics(MsRunStatistics param) const
{
  try
    {
      return _param_stats.at(param);
    }
  catch(std::out_of_range &std_error)
    {
      return QVariant();
    }
}

pappso::MsRunReaderSPtr
MsRun::findMsRunFile()
{
  // first look at the file path :)
  qDebug();
  pappso::MsRunReaderSPtr msrun_reader_sp = getMsRunReaderSPtr();
  if(msrun_reader_sp != nullptr)
    {
      QFileInfo real_file(this->getFileName());
      if(real_file.exists())
        {

          qDebug();
          freeMsRunReaderSp();
          qDebug();
          return msrun_reader_sp;
        }
      else
        {
          msrun_reader_sp = nullptr;
        }
    }
  qDebug();
  QFileInfo file_original(this->getFileName());
  QString basename     = file_original.baseName();
  QString onlyfilename = file_original.fileName();
  QSettings settings;
  QString path = settings.value("path/mzdatadir", "").toString();

  QDir dir_search(path);

  QFileInfoList files = dir_search.entryInfoList();
  foreach(QFileInfo file, files)
    {
      if(file.isDir())
        {
          qDebug() << "DIR: " << file.fileName();
        }
      else
        {
          qDebug() << "FILE: " << file.fileName();
          if(onlyfilename == file.fileName())
            {
              this->setFileName(file.absoluteFilePath());
              try
                {
                  if(msrun_reader_sp == nullptr)
                    {

                      pappso::MsFileAccessor accessor(getFileName(), "");
                      accessor.setPreferedFileReaderType(
                        pappso::MzFormat::brukerTims,
                        pappso::FileReaderType::tims_ms2);
                      qDebug()
                        << " runid=" << getRunId() << " xmlid=" << getXmlId();

                      msrun_reader_sp = accessor.getMsRunReaderSPtrByRunId(
                        getRunId(), getXmlId());

                      this->setMzFormat(msrun_reader_sp.get()
                                          ->getMsRunId()
                                          .get()
                                          ->getMzFormat());

                      freeMsRunReaderSp();
                      return msrun_reader_sp;
                    }
                }
              catch(pappso::PappsoException &error)
                {
                }
            }
          if((file.fileName().contains(onlyfilename)) ||
             (file.fileName().contains(basename)) ||
             (file.fileName().contains(this->getSampleName())))
            {
              try
                {
                  this->setFileName(file.absoluteFilePath());

                  if(msrun_reader_sp == nullptr)
                    {

                      pappso::MsFileAccessor accessor(getFileName(), "");

                      accessor.setPreferedFileReaderType(
                        pappso::MzFormat::brukerTims,
                        pappso::FileReaderType::tims_ms2);
                      qDebug()
                        << " runid=" << getRunId() << " xmlid=" << getXmlId();
                      msrun_reader_sp = accessor.getMsRunReaderSPtrByRunId(
                        getRunId(), getXmlId());

                      this->setMzFormat(msrun_reader_sp.get()
                                          ->getMsRunId()
                                          .get()
                                          ->getMzFormat());
                      freeMsRunReaderSp();
                      return msrun_reader_sp;
                    }
                }
              catch(pappso::PappsoException &error)
                {
                  msrun_reader_sp = nullptr;
                }
            }
        }
    }
  this->setFileName(file_original.absoluteFilePath());
  freeMsRunReaderSp();
  qDebug();
  return msrun_reader_sp;
}

void
MsRun::checkMsRunStatistics()
{

  MsRunStatisticsHandler stats;
  checkMsRunStatistics(&stats);
}

void
MsRun::checkMsRunStatistics(MsRunStatisticsHandler *currentHandler)
{

  QVariant msrun_var = getMsRunStatistics(MsRunStatistics::total_spectra);
  if(msrun_var.isNull())
    {
      try
        {

          _msrun_reader_sp = getMsRunReaderSPtr();
          if(this->getMzFormat() == pappso::MzFormat::brukerTims)
            {
              pappso::TimsMsRunReaderMs2 *tims2_reader =
                dynamic_cast<pappso::TimsMsRunReaderMs2 *>(
                  _msrun_reader_sp.get());
              if(tims2_reader != nullptr)
                {
                  pappso::TimsDataSp tims_data =
                    tims2_reader->getTimsDataSPtr();
                  if(currentHandler->shouldStop())
                    { // statistics handler was stopped : don't use collected
                      // statistics because it is only partial
                    }
                  else
                    {
                      setMsRunStatistics(MsRunStatistics::total_spectra, "NA");
                      // not the number of MS1 or MS2 but the number of scans
                      // and
                      // precursors.
                      // Compute from th mobillity results
                      setMsRunStatistics(
                        MsRunStatistics::total_spectra_ms1,
                        (unsigned int)tims_data->getTotalNumberOfPrecursors());
                      setMsRunStatistics(
                        MsRunStatistics::total_spectra_ms2,
                        (unsigned int)tims_data->getTotalNumberOfScans());
                      setMsRunStatistics(MsRunStatistics::total_spectra_ms3, 0);
                      setMsRunStatistics(MsRunStatistics::tic_spectra_ms1,
                                         "NA");
                      setMsRunStatistics(MsRunStatistics::tic_spectra_ms2,
                                         "NA");
                      setMsRunStatistics(MsRunStatistics::tic_spectra_ms3,
                                         "NA");
                    }
                }
            }
          else
            {
              _msrun_reader_sp.get()->readSpectrumCollection(*currentHandler);

              setMsRunStatistics(MsRunStatistics::total_spectra,
                                 (unsigned int)currentHandler->getTotalCount());
              setMsRunStatistics(
                MsRunStatistics::total_spectra_ms1,
                (unsigned int)currentHandler->getMsLevelCount(1));
              setMsRunStatistics(
                MsRunStatistics::total_spectra_ms2,
                (unsigned int)currentHandler->getMsLevelCount(2));
              setMsRunStatistics(
                MsRunStatistics::total_spectra_ms3,
                (unsigned int)currentHandler->getMsLevelCount(3));
              setMsRunStatistics(MsRunStatistics::tic_spectra_ms1,
                                 currentHandler->getMsLevelTic(1));
              setMsRunStatistics(MsRunStatistics::tic_spectra_ms2,
                                 currentHandler->getMsLevelTic(2));
              setMsRunStatistics(MsRunStatistics::tic_spectra_ms3,
                                 currentHandler->getMsLevelTic(3));
            }
        }
      catch(pappso::ExceptionNotFound &error)
        {
          // no file found, no statistics
        }
    }
  freeMsRunReaderSp();
}

pappso::MsRunXicExtractorInterfaceSp
MsRun::getMsRunXicExtractorInterfaceSp()
{
  if(_xic_extractor_sp != nullptr)
    {
      return _xic_extractor_sp;
    }
  qDebug();
  _msrun_reader_sp = this->findMsRunFile();
  qDebug();
  if(_msrun_reader_sp != nullptr)
    {

      qDebug();
      pappso::MsRunXicExtractorFactory::getInstance().setTmpDir(
        QDir::tempPath());

      QSettings settings;
      QString xic_extraction_method =
        settings.value("global/xic_extractor", "pwiz").toString();
      if(xic_extraction_method == "pwiz")
        {
          _xic_extractor_sp = pappso::MsRunXicExtractorFactory::getInstance()
                                .buildMsRunXicExtractorSp(_msrun_reader_sp);
        }
      else
        {
          _xic_extractor_sp =
            pappso::MsRunXicExtractorFactory::getInstance()
              .buildMsRunXicExtractorDiskBufferSp(_msrun_reader_sp);
        }
      //_xic_extractor_sp = pappso::MsRunXicExtractorFactory::getInstance()
      //                      .buildMsRunXicExtractorDiskBufferSp(*this);

      qDebug();
    }
  freeMsRunReaderSp();
  return _xic_extractor_sp;
}

void
MsRun::buildMsRunRetentionTime(
  const PeptideEvidenceStore &peptide_evidence_store)
{
  pappso::MsRunReaderSPtr reader;
  try
    {
      reader = getMsRunReaderSPtr();
    }
  catch(pappso::PappsoException &error)
    {
      throw pappso::ExceptionNotFound(
        QObject::tr(
          "Error while processing MSrun \"%1\" for sample named \"%2\" :\n%3")
          .arg(getXmlId())
          .arg(getSampleName())
          .arg(error.qwhat()));
    }
  if(reader == nullptr)
    {
      throw pappso::ExceptionNotFound(
        QObject::tr(
          "MSrun \"%1\" for sample named \"%2\" file \"%3\" not found")
          .arg(getXmlId())
          .arg(getSampleName())
          .arg(getFileName()));
    }
  if(mpa_msrunRetentionTime == nullptr)
    {
      mpa_msrunRetentionTime =
        new pappso::MsRunRetentionTime<const pappso::Peptide *>(reader);
    }
  for(auto &peptide_evidence : peptide_evidence_store.getPeptideEvidenceList())
    {
      if(peptide_evidence.get()->isValid())
        {
          if(peptide_evidence.get()->getMsRunP() == this)
            {
              mpa_msrunRetentionTime->addPeptideAsSeamark(
                peptide_evidence.get()
                  ->getPeptideXtpSp()
                  .get()
                  ->getNativePeptideP(),
                reader.get()->scanNumber2SpectrumIndex(
                  peptide_evidence.get()->getScanNumber()));
            }
        }
    }
  reader.get()->releaseDevice();
  // mpa_msrunRetentionTime->computePeptideRetentionTimes();
  freeMsRunReaderSp();
}

void
MsRun::computeMsRunRetentionTime()
{
  if(mpa_msrunRetentionTime != nullptr)
    {
      qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
               << " msrun id=" << getXmlId();
      mpa_msrunRetentionTime->computePeptideRetentionTimes();
    }
}

void
MsRun::clearMsRunRetentionTime()
{
  if(mpa_msrunRetentionTime != nullptr)
    {
      delete mpa_msrunRetentionTime;
      mpa_msrunRetentionTime = nullptr;
    }
}


pappso::MsRunRetentionTime<const pappso::Peptide *> *
MsRun::getMsRunRetentionTimePtr()
{
  return mpa_msrunRetentionTime;
}


MsRunAlignmentGroupSp
MsRun::getAlignmentGroup()
{
  return msp_alignmentGroup;
}

void
MsRun::setAlignmentGroup(MsRunAlignmentGroupSp new_group)
{
  msp_alignmentGroup = new_group;
}
