// Filippo Rusconi 2019

#pragma once

#include <map>

#include "types.h"

namespace pappso
{


class MassSpectrumCombinerFactory
{

  using MapDaltonPrecision = std::map<pappso_double, DaltonPrecision *>;
  using MapPpmPrecision    = std::map<pappso_double, PpmPrecision *>;
  using MapResPrecision    = std::map<pappso_double, ResPrecision *>;
  using MapMzPrecision     = std::map<pappso_double, MzPrecision *>;

  private:
  static MapDaltonPrecision m_mapDalton;
  static MapPpmPrecision m_mapPpm;
  static MapResPrecision m_mapRes;
  static MapMzPrecision m_mapMz;

  public:
  MassDataCombiner getPlusCombiner(MzIntegrationParams &integration_params);
  getPlusCombiner(MzIntegrationParams &integration_params);

  static PrecisionPtr fromString(const QString &str);
  static PrecisionPtr getDaltonInstance(pappso_double value);
  static PrecisionPtr getPpmInstance(pappso_double value);
  static PrecisionPtr getResInstance(pappso_double value);
  static PrecisionPtr getMzInstance(pappso_double value, int charge);
};


class PrecisionBase
{
  protected:
  const pappso_double m_nominal;

  PrecisionBase(pappso_double nominal) : m_nominal(nominal)
  {
  }

  public:
  virtual PrecisionUnit unit() const = 0;
  virtual pappso_double getNominal() const final;
  virtual pappso_double delta(pappso_double value) const = 0;
  virtual QString toString() const                       = 0;
};


/** \def specific type for a dalton precision
 *
 */
class DaltonPrecision : public PrecisionBase
{
  friend class PrecisionFactory;

  protected:
  DaltonPrecision(pappso_double x);

  public:
  virtual ~DaltonPrecision();

  virtual PrecisionUnit unit() const override;

  virtual pappso_double delta(pappso_double value) const override;

  virtual QString toString() const override;
};

/** \def specific type for a ppm precision
 *
 */
class PpmPrecision : public PrecisionBase
{
  friend class PrecisionFactory;

  protected:
  PpmPrecision(pappso_double x);

  public:
  virtual ~PpmPrecision();

  virtual PrecisionUnit unit() const override;

  virtual pappso_double delta(pappso_double value) const override;

  virtual QString toString() const override;
};


/** \def specific type for a res precision
 *
 */
class ResPrecision : public PrecisionBase
{
  friend class PrecisionFactory;

  protected:
  ResPrecision(pappso_double x);

  public:
  virtual ~ResPrecision();

  virtual PrecisionUnit unit() const override;

  virtual pappso_double delta(pappso_double value) const override;

  virtual QString toString() const override;
};


/** \def specific type for a m/z precision
 *
 */
class MzPrecision : public PrecisionBase
{
  friend class PrecisionFactory;

  protected:
  const int m_charge = 1;
  MzPrecision(pappso_double value, int charge);

  public:
  virtual ~MzPrecision();

  virtual PrecisionUnit unit() const override;

  virtual pappso_double delta(pappso_double value) const override;

  int charge() const;

  virtual QString toString() const override;
};


typedef const PrecisionBase *PrecisionPtr;


// was class Precision
class PrecisionFactory
{
  using MapDaltonPrecision = std::map<pappso_double, DaltonPrecision *>;
  using MapPpmPrecision    = std::map<pappso_double, PpmPrecision *>;
  using MapResPrecision    = std::map<pappso_double, ResPrecision *>;
  using MapMzPrecision     = std::map<pappso_double, MzPrecision *>;

  private:
  static MapDaltonPrecision m_mapDalton;
  static MapPpmPrecision m_mapPpm;
  static MapResPrecision m_mapRes;
  static MapMzPrecision m_mapMz;

  public:
  static PrecisionPtr fromString(const QString &str);
  static PrecisionPtr getDaltonInstance(pappso_double value);
  static PrecisionPtr getPpmInstance(pappso_double value);
  static PrecisionPtr getResInstance(pappso_double value);
  static PrecisionPtr getMzInstance(pappso_double value, int charge);
};

} // namespace pappso
