/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the msXpertSuite project.
 *
 * The msXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * END software license
 */


#pragma once


/////////////////////// Qt includes
#include <QObject>
#include <QList>


/////////////////////// Local includes
#include <globals/globals.hpp>
#include <libmass/Trace.hpp>
#include <libmass/DataPoint.hpp>


namespace msXpSlibmass
{


//! The MassSpectrum class provides a mass spectrum.
/*!

  Since a mass spectrum is a collection of DataPoint instances acquired at a
  specific point in time, it needs to be characterized by a retention time
  member datum (\c m_rt). In experiments of ion mobility mass spectrometry,
  another value is needed to fully characterize the mass spectrum, the drift
  time value (\c m_dt).

  The MassSpectrum class derives from the Trace class. As such it inherits a
  set of methods required to combine traces into a single trace. Other
  specialized functions are specific of MassSpectrum.

  The key value of the DataPoint instances of the MassSpectrum should be
  construed as m/z values, and the value of the DataPoint instances should be
  construed as the intensity. Finally, in a MassSpectrum, DataPoints instances
  may be construed as mass peaks.

*/
class MassSpectrum : public Trace
{
  Q_OBJECT

  friend class MassSpectrumJs;

  private:
  //! Retention time at which this mass spectrum was acquired.
  double m_rt = 0.0;

  //! Drift time at which this mass spectrum was acquired.
  double m_dt = 0.0;

  //! Type of the binning.
  msXpS::BinningType m_binningType = msXpS::BinningType::BINNING_TYPE_NONE;

  //! Number of bins in the spectrum.
  int m_binCount = 0;

  //! Index of the bin that was last modified.
  int m_lastBinIdx = 0;

  double m_binSize = 0.005;
  //<! The bin size for the integrations to MZ.

  int m_binSizeType = msXpS::MassToleranceType::MASS_TOLERANCE_NONE;
  //<! The bin size type for the integrations to MZ.

  //! The smallest m/z value contained in the spectrum list being processed.
  double m_minMz = std::numeric_limits<double>::max();

  //! The greatest m/z value contained in the spectrum list being processed.
  double m_maxMz = std::numeric_limits<double>::min();

  //! Tells if the mass spectra should be aligned.
  bool m_applyMzShift = false;

  //! Value by which the mass spectra need to be shifted when aligned.
  double m_mzShift = 0;

  bool m_isOperationCancelled = false;

  public:
  MassSpectrum();
  MassSpectrum(const MassSpectrum &other);
  MassSpectrum(const Trace &other);
  MassSpectrum(const QString &title);
  virtual ~MassSpectrum();

  MassSpectrum &operator=(const MassSpectrum &other);

  void reset() override;


  double rt() const;
  double &rrt();

  double dt() const;
  double &rdt();

  void setMinMz(double value);
  void setMaxMz(double value);

  void setBinningType(msXpS::BinningType binningType);
  msXpS::BinningType binningType();

  void setBinSize(double value);
  double binSize() const;

  void setBinSizeType(int type);
  int binSizeType() const;

  void applyMzShift(bool applyMzShift);
  bool applyMzShift() const;

  void setRemoveZeroValDataPoints(bool remove);
  bool isRemoveZeroValDataPoints();

  // The overriding functions below cannot shadow the base class ones.
  using Trace::initialize;

  int initializeRt(const QList<double> &mzList,
                   const QList<double> &iList,
                   double rt);

  int initializeDt(const QList<double> &mzList,
                   const QList<double> &iList,
                   double dt);

  int initializeRt(const QList<double> &mzList,
                   const QList<double> &iList,
                   double rt,
                   double mzStart,
                   double mzEnd);

  int initializeDt(const QList<double> &mzList,
                   const QList<double> &iList,
                   double dt,
                   double mzStart,
                   double mzEnd);

  int initialize(const QByteArray *keyByteArray,
                 const QByteArray *valByteArray,
                 int compressionType,
                 double keyStart = qSNaN(),
                 double keyEnd   = qSNaN()) override;

  int initialize(const Trace &trace);

  /************************** BINNING METHODS **************************/
  bool setupBinnability(const QList<MassSpectrum *> &massSpectra);
  bool setupBinnability(const MassSpectrum &massSpectrum);
  int populateSpectrumWithArbitraryBins();
  int populateSpectrumWithDataBasedBins(const MassSpectrum &massSpectrum);
  int binIndex(double mz);

  double determineMzShift(const MassSpectrum &massSpectrum);
  double determineMzShift(double otherMzValue);

  int
  combine(const MassSpectrum &other, double mzStart = -1, double mzEnd = -1);
  int
  subtract(const MassSpectrum &other, double mzStart = -1, double mzEnd = -1);

  // We cannot let ::Trace to the job because it does not combine. So we need
  // these two functions.
  int combine(const Trace &trace, double mzStart = -1, double mzEnd = -1);
  int subtract(const Trace &trace, double mzStart = -1, double mzEnd = -1);


  /***** The MonoT overloads are mono-thread versions of the function *****/
  int combineMonoT(const QList<MassSpectrum *> &massSpectra);
  int combine(const QList<MassSpectrum *> &massSpectra,
              double mzStart = -1,
              double mzEnd   = -1);
  int combineSlice(const QList<MassSpectrum *> &massSpectra,
                   int startIdx,
                   int endIdx,
                   double mzStart = -1,
                   double mzEnd   = -1);

  int subtractMonoT(const QList<MassSpectrum *> &massSpectra);
  int subtract(const QList<MassSpectrum *> &massSpectra,
               double mzStart = -1,
               double mzEnd   = -1);
  int subtractSlice(const QList<MassSpectrum *> &massSpectra,
                    int startIdx,
                    int endIdx,
                    double mzStart = -1,
                    double mzEnd   = -1);

  int combine(const QList<double> &mzList,
              const QList<double> &iList,
              double mzStart = -1,
              double mzEnd   = -1);
  int subtract(const QList<double> &mzList,
               const QList<double> &iList,
               double mzStart = -1,
               double mzEnd   = -1);

  // The combinations, these function than hand over to Trace::combine (if no
  // binning) or to combineBinned (if binning)
  int combine(const DataPoint &dataPoint);
  int subtract(const DataPoint &dataPoint);

  // The combinations with binning.
  int combineBinned(const DataPoint &dataPoint);
  int subtractBinned(const DataPoint &dataPoint);

  double tic();
  double tic(double mzStart, double mzEnd);
  static double tic(const QList<MassSpectrum *> &massSpectra);

  void cancelOperation();

  signals:
  void counterSignal(int count);
};

} // namespace msXpSlibmass


Q_DECLARE_METATYPE(msXpSlibmass::MassSpectrum);
Q_DECLARE_METATYPE(msXpSlibmass::MassSpectrum *);

extern int massSpectrumMetaTypeId;
