//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Base/Math/FourierTransform.h
//! @brief     Defines class Math::FourierTransform.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2015
//! @authors   Scientific Computing Group at MLZ Garching
//
//  ************************************************************************************************

#ifdef SWIG
#error no need to expose this header to Swig
#endif // SWIG
#ifndef BORNAGAIN_BASE_MATH_FOURIERTRANSFORM_H
#define BORNAGAIN_BASE_MATH_FOURIERTRANSFORM_H

#include <fftw3.h>
#include <vector>

//! Fourier transform of vectors (in 1D or 2D) using Fast Fourier Transform (fftw package).
//!
//! Usage:
//! std::vector<double> signal, result;
//! FourierTransform ft;
//! ft.fft(signal, result)
//!
//! Given code rely on code from Jeremy Fix page, http://jeremy.fix.free.fr/spip.php?article15,
//! see also "Efficient convolution using the Fast Fourier Transform, Application in C++"
//! by Jeremy Fix, May 30, 2011
class FourierTransform {
public:
    //! definition of 1D vector of double
    using double1d_t = std::vector<double>;

    //! definition of 2D vector of double
    using double2d_t = std::vector<double1d_t>;

    FourierTransform();

    //! FT in 1D
    void fft(const double1d_t& source, double1d_t& result);

    //! Shift low frequency to the center of 1D FT array
    void fftshift(double1d_t& result) const;

    //! FT in 2D
    void fft(const double2d_t& source, double2d_t& result);

    //! Shift low frequency to the center of 2D FT array
    void fftshift(double2d_t& result) const;

    //! prepare arrays for 2D Fourier Transformation (FT) of the given vector
    void init(int h_src, int w_src);

private:
    //! compute FT of source using Fast Fourier transformation from fftw
    void fftw_forward_FT(const double2d_t& src);

    //! Workspace for Fourier Transform.

    //! Workspace contains input (src), intermediate and output (out)
    //! arrays to run FT via fft; 'source' is our signal
    //! Output arrays are allocated via fftw3 allocation for maximum performance.
    class Workspace {
    public:
        Workspace() = default;
        ~Workspace();
        void clear();
        friend class FourierTransform;

    private:
        //! Here, h = height (# rows), w = width (# columns)
        int h_src{0}, w_src{0};   // size of input 'source' array in 2D
        int h_fftw{0}, w_fftw{0}; // size of output 'FT' array in 2D

        double* in_src{nullptr}; // pointer to input 'source' array

        //! result of Fourier transformation of source
        double* out_fftw{nullptr}; // pointer to output 'FT' array

        fftw_plan p_forw_src{nullptr};
    };

    //! input and output data for fftw3
    Workspace ws;
};

#endif // BORNAGAIN_BASE_MATH_FOURIERTRANSFORM_H
