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


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


/////////////////////// pappsomspp includes


/////////////////////// Local includes
#include "MsXpS/libXpertMassCore/MassPeakShaperConfig.hpp"

namespace MsXpS
{
namespace libXpertMassCore
{


/*!
\variable MsXpS::libXpertMassCore::FWHM_PEAK_SPAN_FACTOR

\brief The compounding factor to account for when shaping the sides of the
peak.

The shape of the peak needs to reflect a real mass peak. In particular,
the shape of the peak has to return, \e{on each one of both sides}, to the
baseline, thus mimicking the baseline for a m/z distance equivalent to
FWHM_PEAK_SPAN_FACTOR times the FWHM m/z range from left to right. That means
that the simulated peak region at the left hand side of the centroid value will
span (FWHM_PEAK_SPAN_FACTOR/2) times the FWHM and the same one right.
Empirically, a good FWHM_PEAK_SPAN_FACTOR is \c 4, meaning that the left half
of the peak (that is centered on the centroid) will have a size corresponding
to two times the FHWM, and the same for the right half of the peak.

This is best exemplified as follows:

\code
                    Centroid value
                     ^
                     |
[ m/z - (2 * FWHM) <-|-> m/z + (2 * FWHM) ]
<----- width of the whole peak shape ----->
\endcode
*/

int FWHM_PEAK_SPAN_FACTOR = 4;

/*!
\class MsXpS::libXpertMassCore::MassPeakShaperConfig
\inmodule libXpertMassCore
\ingroup XpertMassCoreMassCalculations
\inheaderfile MassPeakShaperConfig.hpp

\brief The MassPeakShaperConfig class provides the features required to
configure the shaping of a mass peak centroid into a Gaussian or a Lorentzian
fully profiled shape.

\e{Shaping a peak} means creating a shape around a centroid m/z value such that
the m/z peak is represented like it appears in a mass spectrum displayed in
"profile" mode.

\details{The peak shaping process}

The model used to craft the "profiled" mass peak can be of two types:

\list
\li \l{https://en.wikipedia.org/wiki/Gaussian_function}{Gaussian}
\li \l{https://en.wikipedia.org/wiki/Cauchy_distribution}{Lorentzian}
\endlist

The starting parameters are the (m/z,intensity) values of the peak centroid and
the full width at half maximum (FWHM) value that will govern the width of the
shape at 50% height of the peak. The width of the peak will be an inverse
representation of the resolving power of the instrument: the instrument with
the greatest resolving power will yield mass peaks with the smallest width.
Conversely, a very old instrument will produce mass spectra where the peaks
will be broad.

There are thus two possiblities for configuring the peak shape:

\list

\li Provide the FWHM value itself.
\li Provide the resolving power of the instrument that the peak simulation
should emulate.

 \endlist

\enddetails
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_resolution

\brief Resolving power of the instrument.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_fwhm

\brief Full width at half maximum of the shaped peak.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_massPeakWidthLogic

\brief Describes the logic used to compute the peak shape width.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_referencePeakMz

\brief The m/z value to be used when computing the FWHM value starting from a
mass spectrometer resolving power.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_pointCount

\brief The count of points used to shape the peak. Typically between 150 and
300.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_withBins

\brief Tells if bins are requested for the computation.

There is no need in the context of the calculations performed by the
MassPeakShaperConfig class to know if bins are required or not. This variable
is useful when multiple peak shapes are combined into a more complex trace or
mass spectrum, typically when simulating an isotopic cluster. In this case,
each cluster peak centroid is shaped in sequence and then they are all merged
into a single trace (no bins) or mass spectrum (binning). The width of the bins
is defined using this m_withBins variable.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_binSizeDivisor

\brief Empirical division factor to apply to \l m_binSize to reduce the size of
the bins after the bin size calculation.

Empirically, a good value is \c 6.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_binSize

\brief The size of the m/z bins.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_isBinSizeFixed

\brief Tells if the size of the m/z bins is fixed.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_mzStep

\brief The m/z distance (a Delta) between to consecutive data points in the
shaped mass peak.
*/

/*!
\variable MsXpS::libXpertMassCore::MassPeakShaperConfig::m_massPeakShapeType

\brief The requested shape of the mass peak.
*/


/*!
\brief Constructs a MassPeakShaperConfig instance.
*/
MassPeakShaperConfig::MassPeakShaperConfig(QObject *parent): QObject(parent)
{
  reset();
}

/*!
\brief Constructs a MassPeakShaperConfig instance as a copy of \a other.
*/
MassPeakShaperConfig::MassPeakShaperConfig(const MassPeakShaperConfig &other,
                                           QObject *parent)
  : QObject(parent),
    m_resolution(other.m_resolution),
    m_fwhm(other.m_fwhm),
    m_massPeakWidthLogic(other.m_massPeakWidthLogic),
    m_referencePeakMz(other.m_referencePeakMz),
    m_pointCount(other.m_pointCount),
    m_withBins(other.m_withBins),
    m_binSizeDivisor(other.m_binSizeDivisor),
    m_binSize(other.m_binSize),
    m_isBinSizeFixed(other.m_isBinSizeFixed),
    m_mzStep(other.m_mzStep),
    m_massPeakShapeType(other.m_massPeakShapeType)
{
}

/*!
\brief Destructs this MassPeakShaperConfig instance.
*/
MassPeakShaperConfig::~MassPeakShaperConfig()
{
}

/*!
\brief Assigns \a other to this MassPeakShaperConfig instance.
*/
MassPeakShaperConfig &
MassPeakShaperConfig::initialize(const MassPeakShaperConfig &other)
{
  if(this == &other)
    return *this;

  m_resolution         = other.m_resolution;
  m_fwhm               = other.m_fwhm;
  m_massPeakWidthLogic = other.m_massPeakWidthLogic;
  m_referencePeakMz    = other.m_referencePeakMz;
  m_pointCount         = other.m_pointCount;
  m_withBins           = other.m_withBins;
  m_binSizeDivisor     = other.m_binSizeDivisor;
  m_binSize            = other.m_binSize;
  m_isBinSizeFixed     = other.m_isBinSizeFixed;
  m_mzStep             = other.m_mzStep;
  m_massPeakShapeType  = other.m_massPeakShapeType;

  return *this;
}

/*!
\brief Returns a newly allocated MassPeakShaperConfig instance initialized using
\a other and with parent set to \a parent.
*/
MassPeakShaperConfig *
MassPeakShaperConfig::clone(const MassPeakShaperConfig &other,
                            QObject *parent) const
{
  MassPeakShaperConfig *copy_p = new MassPeakShaperConfig(parent);
  copy_p->initialize(other);

  return copy_p;
}

/*!
\brief Sets the \a resolution (the resolving power of the instrument).
*/
void
MassPeakShaperConfig::setResolution(int resolution)
{
  m_resolution = resolution;
}

/*!
\brief Returns the resolution (the resolving power of the instrument).
*/
double
MassPeakShaperConfig::getResolution() const
{
  return m_resolution;
}

/*!
\brief Calculates the resolution (resolving power of the instrument).

The calculation involves using the FWHM (m_fwhm) member datum and the m/z value
that is considered to be the reference for the calculation (m_referencePeakMz).

If any of these two values is not set, then this function returns 0 and sets \a
ok to false.

The calculation is that simple:

\code
m_resolution = m_referencePeakMz / m_fwhm;
\endcode

Because we used the FWHM to compute the resolution and set the calculated value
to the \l m_resolution member datum, we keep a record of this by:

\code
m_massPeakWidthLogic = Enums::MassPeakWidthLogic::FWHM;
\endcode

Returns the resolving power as set in \l m_resolution and sets \a ok to true.
*/
int
MassPeakShaperConfig::resolution(bool *ok)
{
  if(ok == nullptr)
    qFatal("The pointer cannot be nullptr.");

  // If we want to compute the resolution that means that we have to have
  // m_fwhm.
  if(!m_fwhm)
    {
      *ok = false;
      return 0;
    }

  if(!m_referencePeakMz)
    {
      *ok = false;
      return 0;
    }

  m_resolution = m_referencePeakMz / m_fwhm;

  *ok = true;

  // We used the FWHM to compute the resolving power.
  m_massPeakWidthLogic = Enums::MassPeakWidthLogic::FWHM;

  return m_resolution;
}

/*!
\brief Sets the \a fwhm (full width at half maximum).
*/
void
MassPeakShaperConfig::setFwhm(double fwhm)
{
  m_fwhm = fwhm;
}

/*!
\brief Gets the fwhm (full width at half maximum).
*/
double
MassPeakShaperConfig::getFwhm() const
{
  return m_fwhm;
}

/*!
\brief Calculates the fwhm (full width at half maximum).

The calculation involves using the instrument's resolving power (m_resolution)
member datum and the m/z value that is considered to be the reference for the
calculation (m_referencePeakMz).

If any of these two values is not set, then this function returns 0 and sets \a
ok to false.

The calculation is that simple:

\code
  m_fwhm = m_referencePeakMz / m_resolution;
\endcode

Because we used the resolution to compute the FWHM and set the calculated
value
to the \l m_fwhm member datum, we keep a record of this by:

\code
m_massPeakWidthLogic = Enums::MassPeakWidthLogic::RESOLUTION;
\endcode

Returns the FWHM value as set in \l m_fwhm and sets \a ok to true.
*/
double
MassPeakShaperConfig::fwhm(bool *ok)
{
  if(ok == nullptr)
    qFatal("The pointer cannot be nullptr.");

  // Or we need to compute it using the mz passed as parameter and the
  // resolution.
  if(!m_resolution)
    {
      *ok = false;
      return 0;
    }

  if(!m_referencePeakMz)
    {
      // qDebug() << "There is no reference peak centroid!";

      *ok = false;
      return 0;
    }

  m_fwhm = m_referencePeakMz / m_resolution;
  *ok    = true;

  // We used the resolving power to compute the FWHM.
  m_massPeakWidthLogic = Enums::MassPeakWidthLogic::RESOLUTION;

  return m_fwhm;
}

/*!
\brief Returns the half of the FWMH (m_fwhm) for use in the Lorentzian shape
calculation.

Uses the \l fwhm() function to compute the FWHM value. If the
FWHM calculation fails, returns 0 and \a ok is set to false, otherwise returns
half of FWHM and \a ok is set to true.

\sa fwhm()
*/
double
MassPeakShaperConfig::halfFwhm(bool *ok)
{
  double fwhm_value = fwhm(ok);

  if(!*ok)
    return 0;

  return (fwhm_value / 2);
}

/*!
\brief Sets the reference m/z value required for calculations to \a mz.
*/
void
MassPeakShaperConfig::setReferencePeakMz(double mz)
{
  m_referencePeakMz = mz;
}

/*!
\brief Gets the reference m/z value required for calculations.
*/
double
MassPeakShaperConfig::getReferencePeakMz() const
{
  return m_referencePeakMz;
}

/*!
\brief Sets the requirement to prepare m/z bins at a later computing stage to
\a with_bins.

\sa IsotopicClusterShaper::run(), isWithBins()
*/
void
MassPeakShaperConfig::setWithBins(bool with_bins)
{
  m_withBins = with_bins;
}

/*!
\brief Returns the requirement to prepare m/z bins at a later computing stage.

\sa IsotopicClusterShaper::run(), setWithBins()
*/
bool
MassPeakShaperConfig::isWithBins() const
{
  return m_withBins;
}

/*!
\brief Sets the \a bin_size.
*/
void
MassPeakShaperConfig::setBinSize(double bin_size)
{
  m_binSize = bin_size;
}

/*!
\brief Computes the bin size.

If there is no requirement for bins (m_withBins is false), returns 0 and set \a
ok to true.

In order to compute the bin size we need that at least the FWHM (m_fwhm) or the
resolving power (m_resolution) be set. If none of these values are set, this
function returns 0 and sets \a ok to false.

If the bin size was fixed (m_isBinSizeFixed is true), then the bin size
(m_binSize) must be set. If that is not the case, the function returns 0 and
sets \a ok to false.

If the FWHM value (m_fwhm) is set, then it is used right away. Otherwise, the
FWHM is computed starting from the resolving power (m_resolution). If that
computation fails, the function returns 0 and sets \a ok to false.

Starting from the FWHM value (m_fwhm), the bin size calculation requires that
the m_binSizeDivisor be > 1. The bin size is thus computed as the ratio FWHM /
m_binSizeDivisor, and the obtained value is set to m_binSize.

Empirically, a value of 6 for m_binSizeDivisor, yields bin sizes that allow
combining correctly multiple peak shapes into a mass spectrum. Essentially,
that means that 6 individual m/z bins are required to combine nicely all the
peaks shapes obtained for all the peak centroids in a given isotopic cluster,
for example.

At this point, the function returns the bin size and sets \a ok to true.

\sa setBinSizeDivisor()
*/
double
MassPeakShaperConfig::binSize(bool *ok)
{
  if(ok == nullptr)
    qFatal("The pointer cannot be nullptr.");

  // If the bin size was set before, then, just return it.
  // if(m_binSize)
  //{
  //*ok = true;
  // return m_binSize;
  //}

  if(!m_withBins)
    {
      // qDebug() << "Bins are not requested, just return 0 and set true.";
      *ok = true;
      return 0;
    }

  // In order to compute the bin Size, we need the FWHM and the number of
  // points.

  if(!m_resolution && !m_fwhm)
    {
      // qDebug() << "That's an error when neither resolution nor FWHM is set.";
      *ok = false;
      return 0;
    }

  if(m_fwhm)
    {
      // FWHM is fine, we can use that immediately.

      // qDebug() << "FWHM:" << m_fwhm;

      if(!m_pointCount)
        {
          // qDebug() << "That's an error that the point count is 0.";
          *ok = false;
          return 0;
        }
    }
  else
    {
      // We have to work with the resolution.

      // qDebug() << "Resolution:" << m_resolution;

      fwhm(ok);

      if(!*ok)
        {
          // qDebug()
          //<< "Could not compute FWHM on the basis of the resolution.";

          if(!m_pointCount)
            {
              // qDebug() << "That's an error that the point count is 0.";
              *ok = false;
              return 0;
            }
        }
    }

  if(m_isBinSizeFixed)
    {
      // The bin size has to be set and must not be changed, as the user has
      // set it manually.

      if(!m_binSize)
        {
          // qDebug() << "The bin size should be set manually but is not set.";
          *ok = false;
          return 0;
        }
    }
  else
    {
      m_binSize = m_fwhm / m_binSizeDivisor;

      // qDebug() << "The bin size was computed:" << m_binSize;
    }

  *ok = true;
  return m_binSize;
}

/*!
\brief Returns (no computation) the bin size.
*/
double
MassPeakShaperConfig::getBinSize() const
{
  return m_binSize;
}

/*!
\brief Sets the requirement that the bin size be set and fixed (not modifiable
by computations) to \a is_fixed.

When \a is_fixed is true, the \l binSize() function returns the bin size
without trying to compute it.

\sa binSize()
*/
void
MassPeakShaperConfig::setBinSizeFixed(bool is_fixed)
{
  m_isBinSizeFixed = is_fixed;
}

/*
\brief Gets the requirement that the bin size be set and fixed (not modifiable
by computations).
*/
bool
MassPeakShaperConfig::getBinSizeFixed()
{
  return m_isBinSizeFixed;
}

/*!
\brief Sets the bin size computation dividing \a factor.

When the bin size is computed (m_isBinSizeFixed is false), that value is
determined by dividing the FWHM value by this \a factor (m_binSizeDivisor).

Empirically, a value of 6 for m_binSizeDivisor, yields bin sizes that allow
combining correctly multiple peak shapes into a mass spectrum. Essentially,
that means that 6 individual m/z bins are required to combine nicely all the
peaks shapes obtained for all the peak centroids in a given isotopic cluster,
for example.
*/
void
MassPeakShaperConfig::setBinSizeDivisor(int factor)
{
  if(std::abs(factor) < 1)
    qFatal("Programming error.");

  m_binSizeDivisor = std::abs(factor);
}

/*!
\brief Returns the bin size computation dividing factor.

\sa setBinSizeDivisor()
*/
int
MassPeakShaperConfig::getBinSizeDivisor() const
{
  return m_binSizeDivisor;
}

/*!
\brief Sets the \a point_count to be used to shape the mass peak
around the m/z centroid value.

Typically, a value between 150 and 200 points is fine to nicely shape a m/z
peak centroid.
*/
void
MassPeakShaperConfig::setPointCount(int point_count)
{
  m_pointCount = point_count;
}

/*!
\brief Gets the count of points to be used to shape the mass peak around the
m/z centroid value.
*/
int
MassPeakShaperConfig::getPointCount() const
{
  return m_pointCount;
}

/*!
\brief Sets the type of mass peak shape to \a mass_peak_shape_type.

\sa MsXpS::libXpertMassCore::Enums::MassPeakShapeType
*/
void
MassPeakShaperConfig::setMassPeakShapeType(
  Enums::MassPeakShapeType mass_peak_shape_type)
{
  m_massPeakShapeType = mass_peak_shape_type;
}

/*!
\brief Gets the type of mass peak shape.

\sa MsXpS::libXpertMassCore::Enums::MassPeakShapeType
*/
Enums::MassPeakShapeType
MassPeakShaperConfig::getMassPeakShapeType() const
{
  return m_massPeakShapeType;
}

/*!
\brief Sets the mass peak width calculation \a logic.

The full mass peak width at half peak maximum (FWHM) can either be set manually
or be computed from a reference m/z value and the instrument's resolving power.

\sa MsXpS::libXpertMassCore::Enums::MassPeakWidthLogic
*/
void
MassPeakShaperConfig::setMassPeakWidthLogic(Enums::MassPeakWidthLogic logic)
{
  m_massPeakWidthLogic = logic;
}

/*!
\brief Gets the mass peak width calculation logic.

\sa MsXpS::libXpertMassCore::Enums::MassPeakWidthLogic
*/
Enums::MassPeakWidthLogic
MassPeakShaperConfig::getMassPeakWidthLogic() const
{
  return m_massPeakWidthLogic;
}

double
MassPeakShaperConfig::c(bool *ok)
{
  // c in the Gaussian curve is related to the fwhm value:

  fwhm(ok);

  if(!*ok)
    {
      return 0;
    }

  double c = m_fwhm / (2 * sqrt(2 * log(2)));

  // qDebug() << "c:" << c;

  *ok = true;

  return c;
}

double
MassPeakShaperConfig::a(bool *ok)
{
  //  double pi = 3.1415926535897932384626433832795029;

  double c_value = c(ok);

  if(!*ok)
    {
      return 0;
    }

  double a = (1 / (c_value * sqrt(2 * M_PI)));

  // qDebug() << "a:" << a;

  *ok = true;

  return a;
}

double
MassPeakShaperConfig::gamma(bool *ok)
{
  fwhm(ok);

  if(!*ok)
    {
      return 0;
    }

  double gamma = m_fwhm / 2;

  // qDebug() << "gamma:" << gamma;

  *ok = true;

  return gamma;
}

/*!
\brief Sets the \a mz_step.

The m/z step value is the distance between two consecutive points in the peak
shape.

\sa getMzStep()
*/
void
MassPeakShaperConfig::setMzStep(double mz_step)
{
  m_mzStep = mz_step;
}

/*!
\brief Gets the m/z step.

The m/z step value is the distance between two consecutive points in the peak
shape.

\sa setMzStep()
*/
double
MassPeakShaperConfig::getMzStep() const
{
  return m_mzStep;
}

/*!
\brief Calculates the m/z step.


The m/z step value is the distance between two consecutive points in the peak
shape. To compute that value, the full m/z width of the calculated shape (not
only the width at half maximum, FWHM) is divided by the total number of data
points that make the final shape of the m/z peak.

The first variable is FWHM_PEAK_SPAN_FACTOR * m_fwhm, and the second
variable is m_pointCount. The calculation is thus:

\code
m_mzStep = (FWHM_PEAK_SPAN_FACTOR * m_fwhm) / m_pointCount;
\endcode

The first step is to compute the FWHM and then to check the value of
m_pointCount (that cannot be 0). If any of these fails, the function returns 0
and set \a ok to false, otherwise the computed value is set to m_mzStep and
returned, while \a ok is set to true.

\sa FWHM_PEAK_SPAN_FACTOR
*/
double
MassPeakShaperConfig::mzStep(bool *ok)
{
  // But what is the mz step ?
  //
  // We want the shape to be able to go down to baseline. Thus we want that
  // the shape to have a "basis" (or, better, a "ground") corresponding to
  // twice the FWHM on the left of the centroid and to twice the FWHM on the
  // right (that makes in total FWHM_PEAK_SPAN_FACTOR * FWHM, that is,
  // FWHM_PEAK_SPAN_FACTOR = 4).

  fwhm(ok);

  if(!*ok)
    {
      return 0;
    }

  if(!m_pointCount)
    {
      *ok = false;
      return 0;
    }

  m_mzStep = (FWHM_PEAK_SPAN_FACTOR * m_fwhm) / m_pointCount;

  return m_mzStep;
}

/*!
\brief Resets this MassPeakShaperConfig instance to default values like at
construction time.
*/
void
MassPeakShaperConfig::reset()
{
  // Values to start over.

  m_resolution        = 0;
  m_fwhm              = 0;
  m_referencePeakMz   = 0;
  m_pointCount        = 0;
  m_withBins          = false;
  m_binSizeDivisor    = 6;
  m_binSize           = 0;
  m_isBinSizeFixed    = false;
  m_mzStep            = 0;
  m_massPeakShapeType = Enums::MassPeakShapeType::NOT_SET;
}

/*!
\brief Calculates the various parameters of the peak shape using member data.

This function first checks that all the required data are set. Then this
functions computes the FWHM value required to shape the peak centroid. Once the
basic data have been computed or set, the parameters of the shape are computed:
c, a, gamma.

Returns true if the computation was successful, false otherwise.
*/
bool
MassPeakShaperConfig::resolve(libXpertMassCore::ErrorList &error_list)
{
  // We need to try to set all the relevant parameters by calculation.

  bool ok = false;

  // These are the essential parameters:

  if(!m_referencePeakMz)
    {
      error_list.append("Missing reference peak m/z value");
      return false;
    }

  if(!m_fwhm && !m_resolution)
    {
      error_list.append("Both FWHM and resolution are naught");
      return false;
    }

  if(!m_pointCount)
    {
      error_list.append("The count of points to shape the peak is naught");
      return false;
    }

  // At this point we should be able to compute the relevant data.

  // The FWHM is the *leading* value for the determination of the peak shape's
  // width at half maximum. If that FWHM value is 0, then we resort to the
  // resolution. Both R and FWHM cannot be 0!
  if(m_fwhm)
    {
      // If we have FWHM, immediately try to compute the resolution as we'll
      // need it later. FWHM takes precedence over resolution!
      resolution(&ok);

      if(!ok)
        {
          error_list.append("Failed to compute resolution value");
          return false;
        }
    }
  else
    {
      // We should be able to compute FWHM by resorting to the resolution.

      fwhm(&ok);

      if(!ok)
        {
          error_list.append("Failed to compute FWHM value");
          return false;
        }
    }

  // Now check if we have and can compute the bins.
  // But we do this only if the user has not stated that the bin size has to
  // be set manually.

  // qDebug() << "In the resolve, check the bin size";

  binSize(&ok);

  if(!ok)
    {
      error_list.append("Failed to compute the bin size");
      return false;
    }

  mzStep(&ok);

  if(!ok)
    {
      error_list.append("Failed to compute the m/z step");
      return false;
    }
  // Now the other parameters for the shape.

  c(&ok);

  if(!ok)
    {
      error_list.append("Failed to compute the c parameter");
      return false;
    }

  a(&ok);

  if(!ok)
    {
      error_list.append("Failed to compute the a parameter");
      return false;
    }

  gamma(&ok);

  if(!ok)
    {
      error_list.append("Failed to compute the gamma parameter");
      return false;
    }

  return true;
}

/*!
\brief Returns a string representing this MassPeakShaperConfig instance.

The configuration of the MassPeakShaper is first resolved. If that step fails,
an empty string is returned, otherwise the configuration is returned as a
string.
*/
QString
MassPeakShaperConfig::toString()
{
  QString string;

  libXpertMassCore::ErrorList error_list;
  bool ok = resolve(error_list);

  if(!ok)
    return QString();

  QString peak_shape_text;
  if(m_massPeakShapeType == Enums::MassPeakShapeType::GAUSSIAN)
    peak_shape_text = "Gaussian";
  if(m_massPeakShapeType == Enums::MassPeakShapeType::LORENTZIAN)
    peak_shape_text = "Lorentzian";

  QString with_bins_text;
  if(m_withBins)
    with_bins_text +=
      QString("With bins of size: %1 m/z.\n").arg(m_binSize, 0, 'f', 10);
  else
    with_bins_text = "Without bins.\n";

  string = QString(
             "%1 peak shaping:\n"
             "Configuration for reference m/z value: %2\n"
             "Resolution: %3\n"
             "FWHM: %4\n"
             "%5\n"
             "Number of points to shape the peak: %6\n"
             "c: %7\n"
             "c^2: %8\n"
             "mz step: %9\n\n")
             .arg(peak_shape_text)
             .arg(m_referencePeakMz, 0, 'f', 5)
             .arg(m_resolution)
             .arg(m_fwhm, 0, 'f', 5)
             .arg(with_bins_text)
             .arg(m_pointCount)
             .arg(c(&ok), 0, 'f', 5)
             .arg(c(&ok) * c(&ok), 0, 'f', 5)
             .arg(m_mzStep, 0, 'f', 5);

  return string;
}

void
MassPeakShaperConfig::registerJsConstructor(QJSEngine *engine)

{
  if(!engine)
    {
      qDebug() << "Cannot register MassPeakShaperConfig class: engine is null";
      return;
    }

  // Register the meta object as a constructor

  QJSValue jsMetaObject =
    engine->newQMetaObject(&MassPeakShaperConfig::staticMetaObject);
  engine->globalObject().setProperty("MassPeakShaperConfig", jsMetaObject);
}


} // namespace libXpertMassCore

} // namespace MsXpS
