/**
 * \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 <deque>
#include <QMutex>
#include <QSqlQuery>
#include "mzcalibration/mzcalibrationstore.h"
#include "../../msrun/spectrumcollectionhandlerinterface.h"

namespace pappso
{

class TimsData;
class TimsDdaPrecursors;
class TimsDiaSlices;

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


struct PMSPP_LIB_DECL FrameIdDescr
{
  std::size_t m_frameId; // frame id
  // FIXME: we use size to define the number of peaks in spectra
  // should not this variable be m_scanCount ? Same below.
  std::size_t m_scanCount; // frame size (number of TOF scans in frame)
  // FIXME: m_cumulatedScanCount ? Or global scan index, in the end, or scan
  // position, since it is from the beginning of the binary data?
  std::size_t m_globalScanIndex; // cumulative size
};

/**
 * @todo write docs
 */
class PMSPP_LIB_DECL TimsData
{
  friend TimsDdaPrecursors;
  friend TimsDiaSlices;

  public:
  /** @brief build using the tims data directory
   */
  TimsData(QDir timsDataDirectory);

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

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

  /** @brief tells if this MS run is a DDA run
   * @result true if it contains DDA precursors
   */
  bool isDdaRun() const;


  /** @brief tells if this MS run is a DIA run
   * @result true if it contains DIA slices
   */
  bool isDiaRun() const;

  /** @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
   */
  [[deprecated("Please use getMassSpectrumCstSPtrByGlobalScanIndex")]] pappso::
    MassSpectrumCstSPtr
    getMassSpectrumCstSPtrByRawIndex(std::size_t raw_index);
  // FIXME NAMING: Identical to above
  // Not used in i2mcq
  pappso::MassSpectrumCstSPtr
  getMassSpectrumCstSPtrByGlobalScanIndex(std::size_t 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 total number of frames
   */
  [[deprecated("Please use getFrameCount")]] std::size_t
  getTotalNumberOfFrames() const;
  // FIXME NAMING : identical to above
  // Not used in i2mcq
  std::size_t getFrameCount() const;

  /** @brief get the total number of scans
   */
  [[deprecated("Please use getTotalScanCount")]] std::size_t
  getTotalNumberOfScans() const;

  // FIXME NAMING : identical to above
  // Not used in i2mcq
  std::size_t getTotalScanCount() const;


  [[deprecated("Please use getMsLevelByGlobalScanIndex")]] unsigned int
  getMsLevelBySpectrumIndex(std::size_t index);
  // FIXME NAMING : identical to above
  // Not used in i2mcq
  unsigned int getMsLevelByGlobalScanIndex(std::size_t index);

  [[deprecated("Please use getQualifiedMassSpectrumByGlobalScanIndex")]] void
  getQualifiedMassSpectrumByRawIndex(const MsRunIdCstSPtr &msrun_id,
                                     QualifiedMassSpectrum &mass_spectrum,
                                     std::size_t global_scan_index,
                                     bool want_binary_data);
  // FIXME NAMING : identical to above
  // Not used in i2mcq
  void getQualifiedMassSpectrumByGlobalScanIndex(
    const MsRunIdCstSPtr &msrun_id,
    QualifiedMassSpectrum &mass_spectrum,
    std::size_t global_scan_index,
    bool want_binary_data);
  Trace getTicChromatogram() const;


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

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


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

  /** @brief get a Tims frame with his database ID
   *
   * this function is not thread safe
   */
  TimsFrameCstSPtr getTimsFrameCstSPtr(std::size_t timsId);


  /** @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
   */
  [[deprecated("Please use getScanByGlobalScanIndex")]] TimsDataFastMap &
  getRawMsBySpectrumIndex(std::size_t index);
  // FIXME NAMING: identical to above
  // Not used in i2mcq
  TimsDataFastMap &getScanByGlobalScanIndex(std::size_t index);

  /** @brief retention timeline
   * get retention times along the MSrun in seconds
   * @return vector of retention times (seconds)
   */
  [[deprecated(
    "Please use getRetentionTimeLineInSeconds")]] virtual std::vector<double>
  getRetentionTimeLine() const;
  // FIXME NAMING: identical to above
  virtual std::vector<double> getRetentionTimeLineInSeconds() const;


  const std::vector<FrameIdDescr> &getFrameIdDescrList() const;
  const std::vector<TimsFrameRecord> &getTimsFrameRecordList() const;


  const QDir &getTimsDataDirectory() const;

  TimsDdaPrecursors *getTimsDdaPrecursorsPtr() const;
  TimsDiaSlices *getTimsDiaSlicesPtr() const;


  protected:
  QSqlDatabase openDatabaseConnection() const;

  TimsBinDec *getTimsBinDecPtr() const;


  private:
  [[deprecated(
    "Please use getScanCoordinatesByGlobalScanIndex")]] std::pair<std::size_t,
                                                                  std::size_t>
  getScanCoordinateFromRawIndex(std::size_t spectrum_index) const;
  // FIXME NAMING: identical to above
  // Not used in i2mcq
  std::pair<std::size_t, std::size_t>
  getScanCoordinatesByGlobalScanIndex(std::size_t index) const;

  [[deprecated("Please use getGlobalScanIndexByScanCoordinates")]] std::size_t
  getRawIndexFromCoordinate(std::size_t frame_id, std::size_t scan_num) const;

  // FIXME NAMING: identical to above
  // Not used in i2mcq
  std::size_t getGlobalScanIndexByScanCoordinates(std::size_t frame_id,
                                                  std::size_t index) const;


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


  TimsFrameBaseCstSPtr getTimsFrameBaseCstSPtrCached(std::size_t timsId);


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


  QDir m_timsDataDirectory;
  TimsBinDec *mpa_timsBinDec = nullptr;

  TimsDdaPrecursors *mpa_timsDdaPrecursors = nullptr;
  TimsDiaSlices *mpa_timsDiaSlices         = nullptr;
  // QSqlDatabase *mpa_qdb      = nullptr;
  std::size_t m_totalScanCount;
  std::size_t m_frameCount;
  std::size_t m_cacheSize = 60;
  std::deque<TimsFrameCstSPtr> m_timsFrameCache;
  std::deque<TimsFrameBaseCstSPtr> m_timsFrameBaseCache;


  std::map<int, QSqlRecord> m_mapMzCalibrationRecord;
  std::map<int, QSqlRecord> m_mapTimsCalibrationRecord;
  std::vector<TimsFrameRecord> m_mapFramesRecord;

  MzCalibrationStore *mpa_mzCalibrationStore;


  /** @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
   * @key thousands of TOF scans
   * @value corresponding m_frameIdDescrList index
   */
  std::map<std::size_t, std::size_t> m_thousandIndexToFrameIdDescrListIndex;


  /** @brief tells if someone is loading a frame id
   */
  std::vector<std::size_t> m_someoneIsLoadingFrameId;

  QMutex m_mutex;
};
} // namespace pappso
