/**
 * \file pappsomspp/vendors/tims/timsdata.h
 * \date 27/08/2019
 * \author Olivier Langella
 * \brief main Tims data handler
 */

/*******************************************************************************
œ* Copyright (c) 2019 Olivier Langella <Olivier.Langella@u-psud.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/>.
 *
 ******************************************************************************/

#pragma once

#include <QDir>
#include <QSqlDatabase>
#include "timsbindec.h"
#include "timsframe.h"
#include "../../massspectrum/qualifiedmassspectrum.h"
#include "../../processing/filters/filterinterface.h"
#include "../../msrun/xiccoord/xiccoordtims.h"
#include "../../msrun/msrunreader.h"
#include <deque>
#include <QMutex>
#include <QSqlQuery>
#include "mzcalibration/mzcalibrationstore.h"

namespace pappso
{

class TimsData;

/** \brief shared pointer on a TimsData object
 */
typedef std::shared_ptr<TimsData> TimsDataSp;

/**
 * @todo write docs
 */
class PMSPP_LIB_DECL TimsData
{
  public:
  /** @brief build using the tims data directory
   */
  TimsData(QDir timsDataDirectory);

  /**
   * Copy constructor
   *
   * @param other TODO
   */
  TimsData(const TimsData &other);

  /**
   * Destructor
   */
  virtual ~TimsData();


  /** @brief get a mass spectrum given its spectrum index
   * @param raw_index a number begining at 0, corresponding to a Tims Scan in
   * the order they lies in the binary data file
   */
  pappso::MassSpectrumCstSPtr
  getMassSpectrumCstSPtrByRawIndex(std::size_t raw_index);

  /** @brief get a mass spectrum given the tims frame database id and scan
   * number within tims frame
   */
  pappso::MassSpectrumCstSPtr getMassSpectrumCstSPtr(std::size_t timsId,
                                                     std::size_t scanNum);

  /** @brief get the total number of scans
   */
  std::size_t getTotalNumberOfScans() const;

  /** @brief get the number of precursors analyzes by PASEF
   */
  std::size_t getTotalNumberOfPrecursors() const;

  /** @brief guess possible precursor ids given a charge, m/z, retention time
   * and k0
   * @return a list of possible precursor ids
   */
  std::vector<std::size_t> getPrecursorsFromMzRtCharge(int charge,
                                                       double mz_val,
                                                       double rt_sec,
                                                       double k0);

  unsigned int getMsLevelBySpectrumIndex(std::size_t spectrum_index);

  void getQualifiedMassSpectrumByRawIndex(const MsRunIdCstSPtr &msrun_id,
                                          QualifiedMassSpectrum &mass_spectrum,
                                          std::size_t spectrum_index,
                                          bool want_binary_data);

  Trace getTicChromatogram();

  struct SpectrumDescr
  {
    std::size_t parent_frame        = 0;
    std::size_t precursor_id        = 0;
    std::size_t scan_mobility_start = 0;
    std::size_t scan_mobility_end   = 0;
    std::size_t ms1_index           = 0;
    std::size_t ms2_index           = 0;
    double isolationMz              = 0;
    double isolationWidth           = 0;
    float collisionEnergy           = 0;
    std::vector<std::size_t> tims_frame_list;
    PrecursorIonData precursor_ion_data;
  };

  void
  getQualifiedMs2MassSpectrumByPrecursorId(const MsRunIdCstSPtr &msrun_id,
                                           QualifiedMassSpectrum &mass_spectrum,
                                           SpectrumDescr &spectrum_descr,
                                           bool want_binary_data);

  void
  getQualifiedMs1MassSpectrumByPrecursorId(const MsRunIdCstSPtr &msrun_id,
                                           QualifiedMassSpectrum &mass_spectrum,
                                           SpectrumDescr &spectrum_descr,
                                           bool want_binary_data);

  /** @brief filter interface to apply just after raw MS2 specturm extraction
   * the filter can be a list of filters inside a FilterSuite object
   */
  void setMs2FilterCstSPtr(pappso::FilterInterfaceCstSPtr &filter);

  /** @brief filter interface to apply just after raw MS1 specturm extraction
   * the filter can be a list of filters inside a FilterSuite object
   */
  void setMs1FilterCstSPtr(pappso::FilterInterfaceCstSPtr &filter);

  /** @brief enable or disable simple centroid filter on raw tims data for MS2
   */
  void setMs2BuiltinCentroid(bool centroid);


  /** @brief tells if simple centroid filter on raw tims data for MS2 is enabled
   * or not
   */
  bool getMs2BuiltinCentroid() const;


  std::vector<std::size_t> getTimsMS1FrameIdRange(double rt_begin,
                                                  double rt_end) const;


  /** @brief get a Tims frame with his database ID
   */
  TimsFrameCstSPtr getTimsFrameCstSPtr(std::size_t timsId);

  XicCoordTims getXicCoordTimsFromPrecursorId(std::size_t precursor_id,
                                              PrecisionPtr precision_ptr);


  /** @brief function to visit an MsRunReader and get each Spectrum in a
   * spectrum collection handler by Ms Levels
   *
   * this function will retrieve processed qualified spectrum depending on each
   * Bruker precursors
   */
  void ms2ReaderSpectrumCollectionByMsLevel(
    const MsRunIdCstSPtr &msrun_id,
    SpectrumCollectionHandlerInterface &handler,
    unsigned int ms_level);


  /** @brief function to visit an MsRunReader and get each raw Spectrum in a
   * spectrum collection handler by Ms Levels
   *
   * this function will retrieve every scans as a qualified mass spectrum
   */
  void rawReaderSpectrumCollectionByMsLevel(
    const MsRunIdCstSPtr &msrun_id,
    SpectrumCollectionHandlerInterface &handler,
    unsigned int ms_level);

  /** @brief get cumulated raw signal for a given precursor
   * only to use to see the raw signal
   *
   * @param precursor_index precursor index to extract signal from
   * @result a map of integers, x=time of flights, y= intensities
   */
  std::map<quint32, quint32>
  getRawMs2ByPrecursorId(std::size_t precursor_index);

  /** @brief get raw signal for a spectrum index
   * only to use to see the raw signal
   *
   * @param spectrum_index spcetrum index
   * @result a map of integers, x=time of flights, y= intensities
   */
  std::map<quint32, quint32>
  getRawMsBySpectrumIndex(std::size_t spectrum_index);


  /** @brief retention timeline
   * get retention times along the MSrun in seconds
   * @return vector of retention times (seconds)
   */
  virtual std::vector<double> getRetentionTimeLine() const;

  /** @brief get an intermediate structure describing a spectrum
   */
  SpectrumDescr getSpectrumDescrWithPrecursorId(std::size_t precursor_id);


  private:
  SpectrumDescr getSpectrumDescrWithScanCoordinate(
    const std::pair<std::size_t, std::size_t> &scan_coordinate);


  std::pair<std::size_t, std::size_t>
  getScanCoordinateFromRawIndex(std::size_t spectrum_index) const;

  std::size_t getRawIndexFromCoordinate(std::size_t frame_id,
                                        std::size_t scan_num) const;

  QSqlDatabase openDatabaseConnection() const;


  /** @brief get a Tims frame base (no binary data file access) with his
   * database ID
   */
  TimsFrameBaseCstSPtr getTimsFrameBaseCstSPtr(std::size_t timsId);


  /** @brief get a Tims frame with his database ID
   * but look in the cache first
   */
  TimsFrameCstSPtr getTimsFrameCstSPtrCached(std::size_t timsId);

  TimsFrameBaseCstSPtr getTimsFrameBaseCstSPtrCached(std::size_t timsId);


  std::vector<std::size_t>
  getMatchPrecursorIdByKo(std::vector<std::vector<double>> ids,
                          double ko_value);

  /** @todo documentation
   */
  std::vector<std::size_t>
  getClosestPrecursorIdByMz(std::vector<std::vector<double>> ids,
                            double mz_value);


  /** @brief private function to fill m_frameIdDescrList
   */
  void fillFrameIdDescrList();


  private:
  void
  ms2ReaderGenerateMS1MS2Spectrum(const MsRunIdCstSPtr &msrun_id,
                                  SpectrumCollectionHandlerInterface &handler,
                                  SpectrumDescr &spectrum_descr,
                                  unsigned int ms_level);

  void fillSpectrumDescriptionWithSqlRecord(SpectrumDescr &spectrum_descr,
                                            QSqlQuery &qprecursor_list);

  private:
  QDir m_timsDataDirectory;
  TimsBinDec *mpa_timsBinDec = nullptr;
  // QSqlDatabase *mpa_qdb      = nullptr;
  std::size_t m_totalNumberOfScans;
  std::size_t m_totalNumberOfPrecursors;
  std::size_t m_cacheSize = 60;
  std::deque<TimsFrameCstSPtr> m_timsFrameCache;
  std::deque<TimsFrameBaseCstSPtr> m_timsFrameBaseCache;

  pappso::FilterInterfaceCstSPtr mcsp_ms2Filter = nullptr;
  pappso::FilterInterfaceCstSPtr mcsp_ms1Filter = nullptr;

  /** @brief enable builtin centroid on raw tims integers by default
   */
  bool m_builtinMs2Centroid = true;


  std::map<int, QSqlRecord> m_mapMzCalibrationRecord;
  std::map<int, QSqlRecord> m_mapTimsCalibrationRecord;
  std::map<std::size_t, QSqlRecord> m_mapFramesRecord;
  std::map<std::size_t, QSqlRecord> m_mapXicCoordRecord;

  MzCalibrationStore *mpa_mzCalibrationStore;


  struct FrameIdDescr
  {
    std::size_t m_frameId;   // frame id
    std::size_t m_size;      // frame size
    std::size_t m_cumulSize; // cumulative size
  };


  /** @brief store every frame id and corresponding sizes
   */
  std::vector<FrameIdDescr> m_frameIdDescrList;

  /** @brief index to find quickly a frameId in the description list with the
   * raw index of spectrum modulo 1000
   */
  std::map<std::size_t, std::size_t> m_thousandIndexToFrameIdDescrListIndex;

  QMutex m_mutex;
};
} // namespace pappso
