//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Correlation/IPeakShape.h
//! @brief     Defines the interface IPeakShape and subclasses.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_SAMPLE_CORRELATION_IPEAKSHAPE_H
#define BORNAGAIN_SAMPLE_CORRELATION_IPEAKSHAPE_H

#include "Base/Type/ICloneable.h"
#include "Param/Node/INode.h"
#include <heinz/Vectors3D.h>

//! Abstract base class class that defines the peak shape of a Bragg peak.

class IPeakShape : public ICloneable, public INode {
public:
    IPeakShape() = default;
    IPeakShape(const std::vector<double>& PValues);

    ~IPeakShape() override;

#ifndef SWIG
    IPeakShape* clone() const override = 0;
#endif // SWIG

    //! Peak shape at q from a reciprocal lattice point at q_lattice_point
    virtual double peakDistribution(const R3& q, const R3& q_lattice_point) const = 0;

    //! Indicates if the peak shape encodes angular disorder, in which case all peaks in a
    //! spherical shell are needed
    virtual bool angularDisorder() const { return false; }
};

//! Class that implements an isotropic Gaussian peak shape of a Bragg peak.

class IsotropicGaussPeakShape : public IPeakShape {
public:
    IsotropicGaussPeakShape(double max_intensity, double domainsize);
    ~IsotropicGaussPeakShape() override;

#ifndef SWIG
    IsotropicGaussPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "IsotropicGaussPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"DomainSize", "nm"}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

private:
    double peakDistribution(const R3& q) const;
    double m_max_intensity;
    double m_domainsize;
};

//! An isotropic Lorentzian peak shape of a Bragg peak.

class IsotropicLorentzPeakShape : public IPeakShape {
public:
    IsotropicLorentzPeakShape(double max_intensity, double domainsize);
    ~IsotropicLorentzPeakShape() override;

#ifndef SWIG
    IsotropicLorentzPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "IsotropicLorentzPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"DomainSize", "nm"}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

private:
    double peakDistribution(const R3& q) const;
    double m_max_intensity;
    double m_domainsize;
};

//! A peak shape that is Gaussian in the radial direction and
//! uses the Mises-Fisher distribution in the angular direction.

class GaussFisherPeakShape : public IPeakShape {
public:
    GaussFisherPeakShape(double max_intensity, double radial_size, double kappa);
    ~GaussFisherPeakShape() override;

#ifndef SWIG
    GaussFisherPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "GaussFisherPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"DomainSize", "nm"}, {"Kappa", ""}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

    bool angularDisorder() const override { return true; }

private:
    double m_max_intensity;
    double m_radial_size;
    double m_kappa;
};

//! A peak shape that is Lorentzian in the radial direction and uses the
//! Mises-Fisher distribution in the angular direction.

class LorentzFisherPeakShape : public IPeakShape {
public:
    LorentzFisherPeakShape(double max_intensity, double radial_size, double kappa);
    ~LorentzFisherPeakShape() override;

#ifndef SWIG
    LorentzFisherPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "LorentzFisherPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"DomainSize", "nm"}, {"Kappa", ""}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

    bool angularDisorder() const override { return true; }

private:
    double m_max_intensity;
    double m_radial_size;
    double m_kappa;
};

//! A peak shape that is Gaussian in the radial direction and a convolution of a
//! Mises-Fisher distribution with a Mises distribution on the two-sphere.

class MisesFisherGaussPeakShape : public IPeakShape {
public:
    MisesFisherGaussPeakShape(double max_intensity, double radial_size, const R3& zenith,
                              double kappa_1, double kappa_2);
    ~MisesFisherGaussPeakShape() override;

#ifndef SWIG
    MisesFisherGaussPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "MisesFisherGaussPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"Radial Size", "nm"}, {"Kappa1", ""}, {"Kappa2", ""}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

    bool angularDisorder() const override { return true; }

private:
    double m_max_intensity;
    double m_radial_size;
    R3 m_zenith;
    double m_kappa_1, m_kappa_2;
};

//! A peak shape that is a convolution of a Mises-Fisher distribution with a 3d Gaussian.

class MisesGaussPeakShape : public IPeakShape {
public:
    MisesGaussPeakShape(double max_intensity, double radial_size, const R3& zenith, double kappa);
    ~MisesGaussPeakShape() override;

#ifndef SWIG
    MisesGaussPeakShape* clone() const override;
#endif // SWIG

    std::string className() const final { return "MisesGaussPeakShape"; }
    std::vector<ParaMeta> parDefs() const final
    {
        return {{"MaxIntensity", ""}, {"Radial Size", "nm"}, {"Kappa", ""}};
    }

    double peakDistribution(const R3& q, const R3& q_lattice_point) const override;

    bool angularDisorder() const override { return true; }

private:
    double m_max_intensity;
    double m_radial_size;
    R3 m_zenith;
    double m_kappa;
};

#endif // BORNAGAIN_SAMPLE_CORRELATION_IPEAKSHAPE_H
