#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "cdo_output.h"

#ifdef HAVE_LIBFFTW3
#include "cdo_fftw3.h"

#include "cdo_options.h"
#include "cdo_fctrans.h"
#include "cdo_omp.h"
#endif

void
fc2gp(const double *fc, double *gp, long nlat, long nlon, long nlev, long nfc)
{
#ifdef HAVE_LIBFFTW3
  std::vector<FFTW3RCMemory> fmPool(Threading::ompNumMaxThreads);

  for (long ilev = 0; ilev < nlev; ++ilev)
  {
    auto gpx = gp + ilev * nlon * nlat;
    auto fcx = fc + ilev * nfc * nlat;
#ifdef _OPENMP
#pragma omp parallel for default(shared)
#endif
    for (long ilat = 0; ilat < nlat; ++ilat)
    {
      auto tid = (Threading::ompNumMaxThreads == 1) ? 0 : cdo_omp_get_thread_num();
      auto &fm = fmPool[tid];
      if (fm.empty()) fm.init(FFTW_BACKWARD, nlon);

      for (long ifc = 0; ifc < nfc / 2; ++ifc)
      {
        fm.complex[ifc][0] = fcx[2 * ifc * nlat + ilat];
        fm.complex[ifc][1] = fcx[(2 * ifc + 1) * nlat + ilat];
      }
      for (long ifc = nfc / 2; ifc < (nlon / 2 + 1); ++ifc)
      {
        fm.complex[ifc][0] = 0.0;
        fm.complex[ifc][1] = 0.0;
      }

      fftw_execute(fm.plan);

      for (long ilon = 0; ilon < nlon; ++ilon) { gpx[ilat * nlon + ilon] = fm.real[ilon]; }
    }
  }

  static int nprint = 0;
  if (Options::cdoVerbose && nprint++ < 4) fftw_print_plan(fmPool[0].plan);
#else
  cdo_abort("FFTW support not compiled in!");
#endif
}

void
gp2fc(const double *gp, double *fc, long nlat, long nlon, long nlev, long nfc)
{
#ifdef HAVE_LIBFFTW3
  std::vector<FFTW3RCMemory> fmPool(Threading::ompNumMaxThreads);

  double norm = 1. / nlon;

  for (long ilev = 0; ilev < nlev; ++ilev)
  {
    auto gpx = gp + ilev * nlon * nlat;
    auto fcx = fc + ilev * nfc * nlat;
#ifdef _OPENMP
#pragma omp parallel for default(shared)
#endif
    for (long ilat = 0; ilat < nlat; ++ilat)
    {
      auto tid = (Threading::ompNumMaxThreads == 1) ? 0 : cdo_omp_get_thread_num();
      auto &fm = fmPool[tid];
      if (fm.empty()) fm.init(FFTW_FORWARD, nlon);

      for (long ilon = 0; ilon < nlon; ++ilon) { fm.real[ilon] = gpx[ilat * nlon + ilon]; }

      fftw_execute(fm.plan);

      for (long ifc = 0; ifc < nfc / 2; ++ifc)
      {
        fcx[2 * ifc * nlat + ilat] = norm * fm.complex[ifc][0];
        fcx[(2 * ifc + 1) * nlat + ilat] = norm * fm.complex[ifc][1];
      }
    }
  }

  static int nprint = 0;
  if (Options::cdoVerbose && nprint++ < 4) fftw_print_plan(fmPool[0].plan);
#else
  cdo_abort("FFTW support not compiled in!");
#endif
}
