/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  GeopsyCore is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2009-04-09
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <fftw3.h>

#include "FFTW.h"

namespace GeopsyCore {

  /*!
    \class FFTW FFTW.h
    \brief Brief description of class still missing

    From fftw documentation:

    The upshot is that the only thread-safe (re-entrant) routine in FFTW is fftw_execute (and the guru variants thereof).
    All other routines (e.g. the planner) should only be called from one thread at a time. So, for example, you can wrap
    a semaphore lock around any calls to the planner; even more simply, you can just create all of your plans from one thread.
    See http://www.fftw.org/fftw3_doc/Thread-safety.html#Thread-safety for details.

    Prefer FFTWCache in an intensive parallel context. This class is still available but not used anymore.
  */

  Mutex FFTW::_plannerMutex;

  /*!
  */
  void FFTW::forward(int nSamples, double * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_r2r_1d(nSamples, samples, samples, FFTW_R2HC, FFTW_ESTIMATE);
    _plannerMutex.unlock();
    fftw_execute_r2r(plan, samples, samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  void FFTW::backward(int nSamples, double * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_r2r_1d(nSamples, samples, samples, FFTW_HC2R, FFTW_ESTIMATE);
    _plannerMutex.unlock();
    fftw_execute_r2r(plan, samples, samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  void FFTW::forward(int nSamples, Complex * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_dft_1d(nSamples, (fftw_complex*)samples, (fftw_complex*)samples, FFTW_FORWARD, FFTW_ESTIMATE);
    _plannerMutex.unlock();
    fftw_execute_dft(plan, (fftw_complex *)samples, (fftw_complex *)samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  void FFTW::backward(int nSamples, Complex * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_dft_1d(nSamples, (fftw_complex*)samples, (fftw_complex*)samples, FFTW_BACKWARD, FFTW_ESTIMATE);
    _plannerMutex.unlock();
    fftw_execute_dft(plan, (fftw_complex *)samples, (fftw_complex *)samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  void FFTW::manyForward(int howmany, int nSamples, Complex * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_many_dft(1, &nSamples, howmany,
                                         (fftw_complex *)samples, 0, 1, nSamples,
                                         (fftw_complex *)samples, 0, 1, nSamples, FFTW_FORWARD, FFTW_PATIENT);
    _plannerMutex.unlock();
    fftw_execute_dft(plan, (fftw_complex *)samples, (fftw_complex *)samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  void FFTW::manyBackward(int howmany, int nSamples, Complex * samples)
  {
    _plannerMutex.lock();
    fftw_plan plan=fftw_plan_many_dft(1, &nSamples, howmany,
                                         (fftw_complex *)samples, 0, 1, nSamples,
                                         (fftw_complex *)samples, 0, 1, nSamples, FFTW_BACKWARD, FFTW_PATIENT);
    _plannerMutex.unlock();
    fftw_execute_dft(plan, (fftw_complex *)samples, (fftw_complex *)samples);
    _plannerMutex.lock();
    fftw_destroy_plan(plan);
    _plannerMutex.unlock();
  }

  bool FFTW::loadWisdom(QString fileName)
  {
    QFile f(fileName);
    if( !f.open(QIODevice::ReadOnly) ) {
      return false;
    }
    fftw_import_wisdom_from_string(f.readAll().data());
    // TODO interpret returned value from fftw
    return true;
  }

} // namespace GeopsyCore
