/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright (C) 2009--2024 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
 */


////////////////////////////// Stdlib includes
// #include <math.h>
#include <cstdio>
#include <cmath>


////////////////////////////// Qt includes
#include <QDebug>


////////////////////////////// Local includes
#include "MsXpS/libXpertMassCore/Math/Generic.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{

namespace Math
{

/*!
\class MsXpS::libXpertMassCore::Math::Generic
\inmodule libXpertMassCore
\ingroup XpertMassCoreMath
\inheaderfile MsXpS/libXpertMassCore/Math/Generic.hpp

\brief The Generic class provides a number of generic features for math
calculations.
*/

/*!
\brief Calculates the weighted center of mass or the curve \a input_trace.

The returned tuple has four members:

\list

\li \e {Member 0}: center of mass, the abscissa of the center of mass of the
input curve;
\li \e {Member 1}: y max, the intensity at the center of mass
\li \e {Member 2}: sigma [related to the FWHM according to:  FWHM = 2 * sqrt(2 *
std::log(2)) * sigma]
\li \e {Member 3}: b, the baseline correction (0, at the moment)

\endlist

If \a input_trace is empty, the returned tuples has all its members set to 0.0.
*/
std::tuple<double, double, double, double>
Generic::weightedCenterOfMass(const pappso::Trace &input_trace)
{
  // qDebug() << "";

  // This is the weighted center of mass computation.

  if(!input_trace.size())
    {
      return {0, 0, 0, 0};
    }

  double sum_intensities             = 0.0;
  double sum_mz_weighted_intensities = 0.0;
  double y_max                       = 0;
  double x_min                       = input_trace.front().x;
  double x_max                       = x_min;

  for(const pappso::DataPoint &dp : input_trace)
    {
      sum_intensities             += dp.y;
      sum_mz_weighted_intensities += dp.y * dp.x;

      x_max = std::max(x_max, dp.x);
      x_min = std::min(x_min, dp.x);

      y_max = std::max(y_max, dp.y);
    }

  if(sum_intensities == 0)
    {
      return {0, 0, 0, 0};
    }

  double center_mass = sum_mz_weighted_intensities / sum_intensities;

  // Calculate Variance (2nd Central Moment)
  double sum_weighted_squared_diff = 0.0;

  for(const pappso::DataPoint &dp : input_trace)
    {
      double diff                = dp.x - center_mass;
      sum_weighted_squared_diff += dp.y * (diff * diff);
    }

  double variance = sum_weighted_squared_diff / sum_intensities;

  // Protect against negative variance due to floating point noise
  variance = std::max(0.0, variance);

  // Standard deviation, that is related to the FWHM
  double sigma = std::sqrt(variance);

  // Convert Sigma to Full Width at Half Maximum (FWHM)
  // FWHM = 2 * sqrt(2 * log(2)) * sigma ≈ 2.35482 * sigma
  // log() is natural log (base e log), not base 10 log.
  double fwhm = 2 * sqrt(2 * std::log(2)) * sigma;

  qDebug() << qSetRealNumberPrecision(10)
           << "Returning center mass:" << center_mass << "sigma:" << sigma
           << "FWHM:" << fwhm;

  // FIXME: baseline correction -> compute the median of the lowest 10% of the y
  // values.
  double b = 0.0;

  return {center_mass, y_max, sigma, b};
}

} // namespace Math
} // namespace libXpertMassCore
} // namespace MsXpS
