//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Sample/Correlation/Profiles1D.h
//! @brief     Defines class interface Profile1D, 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_PROFILES1D_H
#define BORNAGAIN_SAMPLE_CORRELATION_PROFILES1D_H

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

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


//! Interface for a one-dimensional distribution, with normalization adjusted so that
//! the Fourier transform standardizedFT(q) is a decay function that starts at standardizedFT(0)=1.

class Profile1D : public Cloneable {
public:
    Profile1D(double omega);

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

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

    //! Returns Fourier transform of the normalized distribution;
    //! is a decay function starting at standardizedFT(0)=1.
    virtual double standardizedFT(double q) const = 0;
    //! Returns Fourier transform of the distribution scaled as decay function f(x)/f(0).
    virtual double decayFT(double q) const = 0;

    double omega() const { return m_omega; }
    double decayLength() const { return m_omega; }

    //! Returns the negative of the second order derivative in q space around q=0
    virtual double qSecondDerivative() const = 0;

protected:
    mutable bool m_validated{false};
    double m_omega; //!< half-width
#endif
};


//! Exponential Profile1D exp(-|omega*x|);
//! its Fourier transform standardizedFT(q) is a Cauchy-Lorentzian starting at standardizedFT(0)=1.

class Profile1DCauchy : public Profile1D {
public:
    Profile1DCauchy(double omega);

#ifndef SWIG
    Profile1DCauchy* clone() const override;

    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double qSecondDerivative() const override;

    std::unique_ptr<Distribution1DSampler> createSampler() const override;
#endif
};

//! Gaussian Profile1D;
//! its Fourier transform standardizedFT(q) is a Gaussian starting at standardizedFT(0)=1.

class Profile1DGauss : public Profile1D {
public:
    Profile1DGauss(double omega);

#ifndef SWIG
    Profile1DGauss* clone() const override;

    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double qSecondDerivative() const override;

    std::unique_ptr<Distribution1DSampler> createSampler() const override;
#endif
};

//! Square gate Profile1D;
//! its Fourier transform standardizedFT(q) is a sinc function starting at standardizedFT(0)=1.

class Profile1DGate : public Profile1D {
public:
    Profile1DGate(double omega);

#ifndef SWIG
    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double qSecondDerivative() const override;

    Profile1DGate* clone() const override;

    std::unique_ptr<Distribution1DSampler> createSampler() const override;
#endif
};

//! Triangle Profile1D [1-|x|/omega if |x|<omega, and 0 otherwise];
//! its Fourier transform standardizedFT(q) is a squared sinc function starting at
//! standardizedFT(0)=1.

class Profile1DTriangle : public Profile1D {
public:
    Profile1DTriangle(double omega);

#ifndef SWIG
    Profile1DTriangle* clone() const override;

    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double qSecondDerivative() const override;

    std::unique_ptr<Distribution1DSampler> createSampler() const override;
#endif
};

//! Profile1D consisting of one cosine wave
//! [1+cos(pi*x/omega) if |x|<omega, and 0 otherwise];
//! its Fourier transform standardizedFT(q) starts at standardizedFT(0)=1.

class Profile1DCosine : public Profile1D {
public:
    Profile1DCosine(double omega);

#ifndef SWIG
    Profile1DCosine* clone() const override;

    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double qSecondDerivative() const override;

    std::unique_ptr<Distribution1DSampler> createSampler() const override;
#endif
};

//! Profile1D that provides a Fourier transform standardizedFT(q) in form
//! of a pseudo-Voigt decay function eta*Gauss + (1-eta)*Cauchy, with both components
//! starting at 1 for q=0.

class Profile1DVoigt : public Profile1D {
public:
    Profile1DVoigt(double omega, double eta);

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

    double standardizedFT(double q) const override;
    double decayFT(double q) const override;
    double eta() const { return m_eta; }
    double qSecondDerivative() const override;

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

protected:
    double m_eta; //!< balances between Gauss (eta=0) and Lorentz (eta=1)
#endif
};

#endif // BORNAGAIN_SAMPLE_CORRELATION_PROFILES1D_H
