// ==================================================================================
// Copyright (c) 2016 HiFi-LoFi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished
// to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ==================================================================================

#ifndef _AUDIOFFT_H
#define _AUDIOFFT_H


/**
* AudioFFT provides real-to-complex/complex-to-real FFT routines.
*
* Features:
*
* - Real-complex FFT and complex-real inverse FFT for power-of-2-sized real data.
*
* - Uniform interface to different FFT implementations (currently Ooura, FFTW3 and Apple Accelerate).
*
* - Complex data is handled in "split-complex" format, i.e. there are separate
*   arrays for the real and imaginary parts which can be useful for SIMD optimizations
*   (split-complex arrays have to be of length (size/2+1) representing bins from DC
*   to Nyquist frequency).
*
* - Output is "ready to use" (all scaling etc. is already handled internally).
*
* - No allocations/deallocations after the initialization which makes it usable
*   for real-time audio applications (that's what I wrote it for and using it).
*
*
* How to use it in your project:
*
* - Add the .h and .cpp file to your project - that's all.
*
* - To get extra speed, you can link FFTW3 to your project and define
*   AUDIOFFT_FFTW3 (however, please check whether your project suits the
*   according license).
*
* - To get the best speed on Apple platforms, you can link the Apple
*   Accelerate framework to your project and define
*   AUDIOFFT_APPLE_ACCELERATE  (however, please check whether your
*   project suits the according license).
*
*
* Remarks:
*
* - AudioFFT is not intended to be the fastest FFT, but to be a fast-enough
*   FFT suitable for most audio applications.
*
* - AudioFFT uses the quite liberal MIT license.
*
*
* Example usage:
* @code
* #include "AudioFFT.h"
*
* void Example()
* {
*   const size_t fftSize = 1024; // Needs to be power of 2!
*
*   std::vector<float> input(fftSize, 0.0f);
*   std::vector<float> re(audiofft::AudioFFT::ComplexSize(fftSize));
*   std::vector<float> im(audiofft::AudioFFT::ComplexSize(fftSize));
*   std::vector<float> output(fftSize);
*
*   audiofft::AudioFFT fft;
*   fft.init(1024);
*   fft.fft(input.data(), re.data(), im.data());
*   fft.ifft(output.data(), re.data(), im.data());
* }
* @endcode
*/


#include <cstddef>
#include <memory>


namespace audiofft
{

  namespace details
  {

    class AudioFFTImpl
    {
    public:
      AudioFFTImpl() = default;
      virtual ~AudioFFTImpl() = default;
      virtual void init(size_t size) = 0;
      virtual void fft(const float* data, float* re, float* im) = 0;
      virtual void ifft(float* data, const float* re, const float* im) = 0;

    private:
      AudioFFTImpl(const AudioFFTImpl&) = delete;
      AudioFFTImpl& operator=(const AudioFFTImpl&) = delete;
    };
  }


  // ======================================================


  /**
   * @class AudioFFT
   * @brief Performs 1D FFTs
   */
  class AudioFFT
  {
  public:
    /**
     * @brief Constructor
     */
    AudioFFT();

    /**
     * @brief Initializes the FFT object
     * @param size Size of the real input (must be power 2)
     */
    void init(size_t size);

    /**
     * @brief Performs the forward FFT
     * @param data The real input data (has to be of the length as specified in init())
     * @param re The real part of the complex output (has to be of length as returned by ComplexSize())
     * @param im The imaginary part of the complex output (has to be of length as returned by ComplexSize())
     */
    void fft(const float* data, float* re, float* im);

    /**
     * @brief Performs the inverse FFT
     * @param data The real output data (has to be of the length as specified in init())
     * @param re The real part of the complex input (has to be of length as returned by ComplexSize())
     * @param im The imaginary part of the complex input (has to be of length as returned by ComplexSize())
     */
    void ifft(float* data, const float* re, const float* im);

    /**
     * @brief Calculates the necessary size of the real/imaginary complex arrays
     * @param size The size of the real data
     * @return The size of the real/imaginary complex arrays
     */
    static size_t ComplexSize(size_t size);

  private:
    std::unique_ptr<details::AudioFFTImpl> _impl;

    AudioFFT(const AudioFFT&) = delete;
    AudioFFT& operator=(const AudioFFT&) = delete;
  };


  /**
   * @deprecated
   * @brief Let's keep an AudioFFTBase type around for now because it has been here already in the 1st version in order to avoid breaking existing code.
   */
  typedef AudioFFT AudioFFTBase;

} // End of namespace

#endif // Header guard
