//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Correlation/Profiles2D.h
//! @brief     Defines class interface Profile2D, and children thereof.
//!
//! @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_PROFILES2D_H
#define BORNAGAIN_SAMPLE_CORRELATION_PROFILES2D_H

#include "Base/Type/Cloneable.h"
#include <memory>
#include <numbers>

#ifndef SWIG
#include "Sample/Correlation/Distribution2DSampler.h"
#endif // SWIG

using std::numbers::pi;

//! Interface for two-dimensional distributions in Fourier space.

class Profile2D : public Cloneable {
public:
    Profile2D(double omega_x, double omega_y, double gamma);

#ifndef SWIG
    Profile2D* clone() const override = 0;
    virtual void validate() const;

    double omegaX() const { return m_omega_x; }
    double omegaY() const { return m_omega_y; }
    double decayLengthX() const { return m_omega_x; }
    double decayLengthY() const { return m_omega_y; }
    double gamma() const { return m_gamma; }

    //! Angle in direct space between X- and Y-axis of distribution.
    double delta() const { return (pi / 2); }

    //! Fourier transformed distribution for q in X,Y coordinates
    //! the original distribution (in real space) is assumed to be normalized:
    //! total integral is equal to 1
    virtual double standardizedFT2D(double qx, double qy) const = 0;
    virtual double decayFT2D(double qx, double qy) const = 0;

    virtual std::unique_ptr<Distribution2DSampler> createSampler() const = 0;

protected:
    mutable bool m_validated{false};

    double sumsq(double qx, double qy) const;

    double m_omega_x; //!< Half-width along x axis
    double m_omega_y; //!< Half-width along y axis
    double m_gamma;   //!< direct-space orientation with respect to the first lattice vector

#endif // SWIG
};


//! Two-dimensional Cauchy distribution in Fourier space;
//! corresponds to a normalized exp(-r) in real space,
//! with \f$r=\sqrt{(\frac{x}{\omega_x})^2 + (\frac{y}{\omega_y})^2}\f$.

class Profile2DCauchy : public Profile2D {
public:
    Profile2DCauchy(double omega_x, double omega_y, double gamma);

#ifndef SWIG
    Profile2DCauchy* clone() const override;
    double standardizedFT2D(double qx, double qy) const override;
    double decayFT2D(double qx, double qy) const override;

    std::unique_ptr<Distribution2DSampler> createSampler() const override;
#endif // SWIG
};

//! Two-dimensional Gauss distribution in Fourier space;
//! corresponds to normalized exp(-r^2/2) in real space
//! with \f$r=\sqrt{(\frac{x}{\omega_x})^2 + (\frac{y}{\omega_y})^2}\f$.

class Profile2DGauss : public Profile2D {
public:
    Profile2DGauss(double omega_x, double omega_y, double gamma);

#ifndef SWIG
    Profile2DGauss* clone() const override;

    double standardizedFT2D(double qx, double qy) const override;
    double decayFT2D(double qx, double qy) const override;

    std::unique_ptr<Distribution2DSampler> createSampler() const override;
#endif // SWIG
};

//! Two-dimensional gate distribution in Fourier space;
//! corresponds to normalized constant if r<1 (and 0 otherwise) in real space,
//! with \f$r=\sqrt{(\frac{x}{\omega_x})^2 + (\frac{y}{\omega_y})^2}\f$.

class Profile2DGate : public Profile2D {
public:
    Profile2DGate(double omega_x, double omega_y, double gamma);

#ifndef SWIG
    Profile2DGate* clone() const override;

    double standardizedFT2D(double qx, double qy) const override;
    double decayFT2D(double qx, double qy) const override;

    std::unique_ptr<Distribution2DSampler> createSampler() const override;
#endif // SWIG
};

//! Two-dimensional cone distribution in Fourier space;
//! corresponds to 1-r if r<1 (and 0 otherwise) in real space
//! with \f$r=\sqrt{(\frac{x}{\omega_x})^2 + (\frac{y}{\omega_y})^2}\f$.

class Profile2DCone : public Profile2D {
public:
    Profile2DCone(double omega_x, double omega_y, double gamma);

#ifndef SWIG
    Profile2DCone* clone() const override;
    double standardizedFT2D(double qx, double qy) const override;
    double decayFT2D(double qx, double qy) const override;

    std::unique_ptr<Distribution2DSampler> createSampler() const override;
#endif // SWIG
};

//! Two-dimensional Voigt distribution in Fourier space;
//! corresponds to eta*Gauss + (1-eta)*Cauchy

class Profile2DVoigt : public Profile2D {
public:
    Profile2DVoigt(double omega_x, double omega_y, double gamma, double eta);

#ifndef SWIG
    Profile2DVoigt* clone() const override;
    void validate() const override;

    double standardizedFT2D(double qx, double qy) const override;
    double decayFT2D(double qx, double qy) const override;
    double eta() const { return m_eta; } //!< balances between Gauss (eta=0) and Lorentz (eta=1)

    std::unique_ptr<Distribution2DSampler> createSampler() const override;

protected:
    double m_eta;
#endif // SWIG
};

#endif // BORNAGAIN_SAMPLE_CORRELATION_PROFILES2D_H
