/**
 * \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"

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 scanNum the total number of scans contained in this frame
   */
  TimsFrameBase(std::size_t timsId, quint32 scanNum);
  /**
   * Copy constructor
   *
   * @param other TODO
   */
  TimsFrameBase(const TimsFrameBase &other);

  /**
   * Destructor
   */
  ~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 scanNum scan number in the frame in the order it lies in binary
   * file, from 0 to N-1
   */
  virtual std::size_t getNbrPeaks(std::size_t scanNum) const;

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

  /** @brief cumulate spectrum given a scan number range
   * need the binary file
   * @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
   */
  virtual Trace cumulateScanToTrace(std::size_t scanNumBegin,
                                    std::size_t scanNumEnd) const;


  /** @brief cumulate scan list into a trace into a raw spectrum map
   * @param rawSpectrum simple map of integers to cumulate raw counts
   * @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
   */
  virtual void cumulateScansInRawMap(std::map<quint32, quint32> &rawSpectrum,
                                     std::size_t scanNumBegin,
                                     std::size_t scanNumEnd) 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 setAccumulationTime(double accumulation_time_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 setTime(double time);
  void setMsMsType(quint8 type);
  unsigned int getMsLevel() const;
  double getTime() 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 getDriftTime(std::size_t scanNum) 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 scanNum) 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 getScanNumFromOneOverK0(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 getTraceFromCumulatedScans(
    std::map<quint32, quint32> &accumulated_scans) const;

  /** @brief transform accumulation of raw scans into a real mass spectrum with
   * a simple centroid on raw integers
   */
  pappso::Trace getTraceFromCumulatedScansBuiltinCentroid(
    std::map<quint32, quint32> &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);

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

  /** @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_timsId;

  /** @brief accumulation time in milliseconds
   */
  double m_accumulationTime = 0;

  quint8 m_msMsType = 0;

  /** @brief retention time
   */
  double m_time = 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
