/**
 * \file pappsomspp/vendors/tims/timsframebase.h
 * \date 16/12/2019
 * \author Olivier Langella
 * \brief handle a single Bruker's TimsTof frame without binary data
 */

/*******************************************************************************
 * 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 <memory>
#include <vector>
#include <QtGlobal>
#include "../../massspectrum/massspectrum.h"
#include "mzcalibration/mzcalibrationinterface.h"
#include "timsdatafastmap.h"

namespace pappso
{

class TimsFrameBase;
typedef std::shared_ptr<TimsFrameBase> TimsFrameBaseSPtr;
typedef std::shared_ptr<const TimsFrameBase> TimsFrameBaseCstSPtr;


/**
 * @todo write docs
 */
class TimsFrameBase
{
  public:
  /** @brief constructor for binary independant tims frame
   * @param timsId tims frame identifier in the database
   * @param scanCount the number of scans in this frame
   */
  TimsFrameBase(std::size_t frameId, quint32 scanCount);
  /**
   * Copy constructor
   *
   * @param other TODO
   */
  TimsFrameBase(const TimsFrameBase &other);

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

  /** @brief tells if 2 tims frame has the same calibration data
   * Usefull to know if raw data can be handled between frames
   */
  virtual bool hasSameCalibrationData(const TimsFrameBase &other) const;

  /** @brief get the number of peaks in this spectrum
   * need the binary file
   * @param scanIndex scan index in the frame in the order it lies in
frame's binary data, from 0 to N-1
   */
  virtual std::size_t getScanPeakCount(std::size_t scanIndex) const;

  /** @brief get the number of scans contained in this frame
   * each scan represents an ion mobility slice
   */

  virtual std::size_t getTotalNumberOfScans() const;


  /** @brief get the maximum raw mass index contained in this frame
   */
  virtual quint32 getMaximumRawMassIndex() const;

  /** @brief get Mass spectrum with peaks for this scan index
   * need the binary file
   * @param scan_index scan index in the frame in the order it lies in binary
   * file, from 0 to N-1 (this is the mobility index)
   */
  virtual MassSpectrumSPtr getMassSpectrumSPtr(std::size_t scan_index) const;


  /** @brief get the mass spectrum corresponding to a scan index
   * @param scan_index the scan index of the mass spectrum to retrieve
   * */
  virtual pappso::MassSpectrumCstSPtr
  getMassSpectrumCstSPtr(std::size_t scan_index) const final;

  /** @brief cumulate spectrum given a scan number range
   * need the binary file
   * The intensities are normalized with respect to the frame accumulation time
   *
   * @param scanIndexBegin scan index in the frame in the order it lies in the
frame in the binary file, from 0 to N-1
   * @param scanIndexEnd scan number in the frame in the order it lies in the
frame in the binary file, from 0 to N-1
   */
  virtual Trace cumulateScansToTrace(std::size_t scanIndexBegin,
                                     std::size_t scanIndexEnd) const;


  /** @brief cumulate spectrum given a scan index range
   * need the binary file
   * The intensities are normalized with respect to the frame accumulation time
   * to leverage computing performance, this function decreases the mz
   * resolution
   *
   * @param tof_index_merge_window width of the TOF index window used to merge
all
   * intensities into a single point. This results in faster computing.
   * @param scanIndexBegin scan index in the frame in the order it lies in
binary
   * file, from 0 to N-1
   * @param scanIndexEnd scan index in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @param minimum_tof_index_out report the minimum mz index contained in the
   * resulting trace
   * @param maximum_tof_index_out report the maximum mz index contained in the
   * resulting trace
   *
   */
  virtual Trace combineScansToTraceWithDowngradedMzResolution(
    std::size_t tof_index_merge_window,
    std::size_t scanIndexBegin,
    std::size_t scanIndexEnd,
    quint32 &minimum_tof_index_out,
    quint32 &maximum_tof_index_out) const;


  /** @brief cumulate spectrum given a scan number range
   * need the binary file
   * The intensities are normalized with respect to the frame accumulation time
   * to leverage computing performance, this function decreases the mz
   * resolution
   *
   * @param mzindex_merge_window width of the mzindex window used to merge all
   * intensities into a single point. This results in faster computing.
   * @param mz_range_begin
   * @param mz_range_end
   * @param scanNumBegin scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @param scanNumEnd scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @param mz_minimum_index report the minimum mz index contained in the
   * resulting trace (constrained by the mz_range_begin)
   * @param mz_maximum_index report the maximum mz index contained in the
   * resulting trace (constrained by the mz_range_end)
   *
   */
  virtual Trace combineScansToTraceWithDowngradedMzResolution2(
    std::size_t mz_index_merge_window,
    double mz_range_begin,
    double mz_range_end,
    std::size_t mobility_scan_begin,
    std::size_t mobility_scan_end,
    quint32 &mz_minimum_index_out,
    quint32 &mz_maximum_index_out) const;


  /** @brief get a single mobility scan m/z + intensities
   *
   * @param scanIndex scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @param tof_index_merge_window width of the TOF index window used to merge
all
   * intensities into a single point. This results in faster computing.
   * @param mz_range_begin
   * @param mz_range_end
   * @param mz_minimum_index report the minimum mz index contained in the
   * resulting trace (constrained by the mz_range_begin)
   * @param mz_maximum_index report the maximum mz index contained in the
   * resulting trace (constrained by the mz_range_end)
   *
   */
  virtual Trace getMobilityScan(std::size_t scan_index,
                                std::size_t tof_index_merge_window,
                                double mz_range_begin,
                                double mz_range_end,
                                quint32 &mz_minimum_index_out,
                                quint32 &mz_maximum_index_out) const;

  /** @brief cumulate scan list into a trace into a raw spectrum map
   * The intensities are NOT normalized with respect to the frame accumulation
   * time
   *
   * @param tof_index_intensity_map simple map of integers to cumulate raw
counts
   * @param scan_index_begin scan index in the frame in the order it lies in
binary
   * file, from 0 to N-1
   * @param scan_index_end scan index in the frame in the order it lies in
binary
   * file, from 0 to N-1
   */
  virtual void
  combineScansInTofIndexIntensityMap(TimsDataFastMap &tof_index_intensity_map,
                                     std::size_t scan_index_begin,
                                     std::size_t scan_index_end) const;


  /** @brief cumulate scan list into a trace into a raw spectrum map
   * The intensities are NOT normalized with respect to the frame accumulation
   * time
   *
   * @param tof_index_intensity_map simple map of integers to cumulate raw
counts
   * @param scan_index_begin scan index in the frame in the order it lies in
binary
   * file, from 0 to N-1
   * @param scan_index_end scan index in the frame in the order it lies in
binary
   * file, from 0 to N-1
   * @param tof_index_begin
   * @param tof_index_end
   */
  virtual void
  combineScansInTofIndexIntensityMap(TimsDataFastMap &tof_index_intensity_map,
                                     std::size_t scan_index_begin,
                                     std::size_t scan_index_end,
                                     quint32 tof_index_begin,
                                     quint32 tof_index_end) const;

  virtual quint64 cumulateScanIntensities(std::size_t scan_index) const;

  virtual quint64
  cumulateScanRangeIntensities(std::size_t scan_index_begin,
                               std::size_t scan_index_end) const;

  /** @brief check that this scan number exists
   * @param scanNum scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   */
  bool checkScanNum(std::size_t scanNum) const;


  void setAcqDurationInMilliseconds(double acquisition_duration_ms);
  void setMzCalibration(double T1_frame,
                        double T2_frame,
                        double digitizerTimebase,
                        double digitizerDelay,
                        double C0,
                        double C1,
                        double C2,
                        double C3,
                        double C4,
                        double T1_ref,
                        double T2_ref,
                        double dC1,
                        double dC2);
  void setTimsCalibration(int tims_model_type,
                          double C0,
                          double C1,
                          double C2,
                          double C3,
                          double C4,
                          double C5,
                          double C6,
                          double C7,
                          double C8,
                          double C9);
  void setRtInSeconds(double time);
  void setMsMsType(quint8 type);
  unsigned int getMsLevel() const;
  double getRtInSeconds() const;

  std::size_t getId() const;

  /** @brief get drift time of a scan number in milliseconds
   * @param scanNum scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @return time in milliseconds of mobility delay (drift time)
   * */
  double getDriftTimeInMilliseconds(std::size_t scan_index) const;

  /** @brief get 1/K0 value of a given scan (mobility value)
   * @param scanNum scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * */
  double getOneOverK0Transformation(std::size_t scan_index) const;


  /** @brief get the scan number from a given 1/Ko mobility value
   * @param one_over_k0 the mobility value to tranform
   * @return integer the scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   */
  std::size_t getScanIndexFromOneOverK0(double one_over_k0) const;

  /** @brief get voltage for a given scan number
   * @param scanNum scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   * @return double volt measure
   * */
  double getVoltageTransformation(std::size_t scanNum) const;

  /** @brief transform accumulation of raw scans into a real mass spectrum
   */
  pappso::Trace
  getTraceFromTofIndexIntensityMap(TimsDataFastMap &accumulated_scans) const;


  /** @brief get the MzCalibration model to compute mz and TOF for this frame
   */
  virtual const MzCalibrationInterfaceSPtr &
  getMzCalibrationInterfaceSPtr() const final;

  void setMzCalibrationInterfaceSPtr(MzCalibrationInterfaceSPtr mzCalibration);


  /** @brief get raw index list for one given scan
   * index are not TOF nor m/z, just index on digitizer
   */
  virtual std::vector<quint32>
  getScanTofIndexList(std::size_t scan_index) const;

  /** @brief get raw intensities without transformation from one scan
   * it needs intensity normalization
   */
  virtual std::vector<quint32>
  getScanIntensityList(std::size_t scan_index) const;

  /** @brief get a mobility trace cumulating intensities inside the given mass
   * index range
   * @param tof_index_begin raw mass index lower bound
   * @param tof_index_end raw mass index upper bound
   * @param method max or sum intensities
   * @param scan_index_begin first mobility scan to integrate
   * @param scan_index_end last mobility scan to integrate
   */
  virtual Trace
  getIonMobilityTraceByTofIndexRange(std::size_t tof_index_begin,
                                     std::size_t tof_index_end,
                                     XicExtractMethod method,
                                     std::size_t scan_index_begin,
                                     std::size_t scan_index_end) const;

  protected:
  struct TofIndexIntensityPair
  {
    quint32 tof_index;
    quint32 intensity_index;
  };

  /** @brief Downgrade the TOF index resolution to lower the number of real m/z
computations

    This function merges together into a single TOF index bin all the TOF
indices contained in the \a tof_index_merge_window. Then the window is shifted
and the operation is performed again. When all the list of
\l{TofIndexIntensityPair}s has been gone through, the resolution of the data
has effectively been downgrade by a factor corresponding to \a
tof_index_merge_window.
   *
   * @param tof_index_merge_window width of the TOF index window used to merge
all
   * intensities into a single point. This results in faster computing.
   * @param rawSpectrum the spectrum to shrink
   */
  virtual std::vector<TofIndexIntensityPair> &
  downgradeResolutionOfTofIndexIntensityPairList(
    std::size_t tof_index_merge_window,
    std::vector<TofIndexIntensityPair> &spectrum) const;

  protected:
  /** @brief total number of scans contained in this frame
   */
  quint32 m_scanCount;

  /** @brief Tims frame database id (the SQL identifier of this frame)
   * @warning in sqlite, there is another field called TimsId : this is not
   * that, because it is in fact an offset in bytes in the binary file.
   * */
  std::size_t m_frameId;

  /** @brief acquisition duration in milliseconds
   */
  double m_acqDurationInMilliseconds = 0;

  quint8 m_msMsType = 0;

  /** @brief retention time
   */
  double m_rtInSeconds = 0;

  double m_timsDvStart = 0; // C2 from TimsCalibration
  double m_timsSlope =
    0; // (dv_end - dv_start) / ncycles  //C3 from TimsCalibration // C2 from
       // TimsCalibration // C1 from TimsCalibration
  double m_timsTtrans = 0; // C4 from TimsCalibration
  double m_timsNdelay = 0; // C0 from TimsCalibration
  double m_timsVmin   = 0; // C8 from TimsCalibration
  double m_timsVmax   = 0; // C9 from TimsCalibration
  double m_timsC6     = 0;
  double m_timsC7     = 0;

  MzCalibrationInterfaceSPtr msp_mzCalibration = nullptr;
};
} // namespace pappso
