/*
 *
 * fft_FFTPACK.c	- FFTPACK fft routines
 *
 */
#define _USE_MATH_DEFINES

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "config.h"

#ifdef CMUNIPACK_USE_INTERNAL_FFTPACK

#include "fft.h"

#ifndef PI
#define PI M_PI
#endif

int     cffti_(int *n, double *work);
int     cfftf_(int *n, complex *c, double *work);
int     cfftb_(int *n, complex *c, double *work);

int     rffti_(int *n, double *work);
int     rfftf_(int *n, double *r, double *work);
int     rfftb_(int *n, double *r, double *work);

int     ezffti_(int *n, double *work);
int     ezfftf_(int *n, double *r, double *a0, double *a, double *b, double *work);
int     ezfftb_(int *n, double *r, double *a0, double *a, double *b, double *work);


/* 
 * cycleimage() --- shifts the origin of a periodic image to x0, y0	
 */
static void cycleimage(double **f, int Nx, int Ny, int x0, int y0)
{
	int	x, y;
	double	*F;

	/* shift the origin into the image */
	while (x0 < 0) {
		x0 += Nx;
	}
	while (y0 < 0) {
		y0 += Ny;
	}
	
	F = (double *) calloc(Nx, sizeof(double));
	for (y = 0; y < Ny; y++) {
		for (x = 0; x < Nx; x++) {
			F[x] = f[y][x];
		}
		for (x = 0; x < Nx; x++) {
			f[y][(x + x0) % Nx] = F[x];
		}
	}
	free(F);
	F = (double *) calloc(Ny, sizeof(double));
	for (x = 0; x < Nx; x++) {
		for (y = 0; y < Ny; y++) {
			F[y] = f[y][x];
		}
		for (y = 0; y < Ny; y++) {
			f[(y + y0) % Ny][x] = F[y];
		}
	}
	free(F);
}

void	forward_fft(double **f, int nx, int ny, fft_type fk)
{
	int 	x, y, n;
	double	*r, *work1, *work2;

	n = ny / 2;

	/* y-direction fft's */
	r = (double *) calloc(ny, sizeof(double));
	work1 = (double *) calloc(2 * ny + 15, sizeof(double));
	rffti_(&ny, work1);
	for (x = 0; x < nx; x++) {
		for (y = 0; y < ny; y++) {
			r[y] = f[y][x];
		}
		rfftf_(&ny, r, work1);
		fk[0][x].r = r[0];
		fk[0][x].i = 0.0;
		for (y = 1; y < n; y++) {
			fk[y][x].r = r[2 * y - 1];
			fk[y][x].i = r[2 * y];
		}
		fk[n][x].r = r[ny - 1];
		fk[n][x].i = 0.0;
	}
	free(work1);
	free(r);

	/* x-direction fft's */
	work2 = (double *) calloc(4 * nx + 15, sizeof(double));
	cffti_(&nx, work2);
	for (y = 0; y <= n; y++) {
		cfftf_(&nx, fk[y], work2);
	}
	free(work2);
}

void	inverse_fft(fft_type fk, int nx, int ny, double **f) 
{
	int 	x, y, n;
	double	*r, *work1, *work2;

	n = ny / 2;

	/* inverse x-transforms */
	work2 = (double *) calloc(4 * nx + 15, sizeof(double));
	cffti_(&nx, work2);
	for (y = 0; y <= n; y++) {
		cfftb_(&nx, fk[y], work2);
	}
	free(work2);

	/* inverse y-transforms  */
	r = (double *) calloc(ny, sizeof(double));
	work1 = (double *) calloc(2 * ny + 15, sizeof(double));
	rffti_(&ny, work1);
	for (x = 0; x < nx; x++) {
		r[0] = fk[0][x].r;
		for (y = 1; y < n; y++) {
			r[2 * y - 1] = fk[y][x].r;
			r[2 * y] = fk[y][x].i;
		}
		r[ny - 1] = fk[n][x].r;
		rfftb_(&ny, r, work1);
		for (y = 0; y < ny; y++) {
			 f[y][x] = r[y] / (nx * ny);
		}
	}
	free(r);
	free(work1);
}

void	ccf(fft_type fk1, fft_type fk2, int Nx, int Ny, double **ff12, int x0, int y0)
{
	int	ix, iy, n;
	double	a1, b1, a2, b2;
	complex **Fk1, **Fk2;

	Fk1 = (complex **) fk1;
	Fk2 = (complex **) fk2;
	n = Ny / 2;
	for (iy = 0; iy <= n; iy++) {
		for (ix = 0; ix < Nx; ix ++) {
			a1 = Fk1[iy][ix].r;
			a2 = Fk2[iy][ix].r;
			b1 = Fk1[iy][ix].i;
			b2 = Fk2[iy][ix].i;
			Fk1[iy][ix].r = (a1 * a2 + b1 * b2) / (Nx * Ny);
			Fk1[iy][ix].i = (a1 * b2 - b1 * a2) / (Nx * Ny);
		}
	}
	inverse_fft(fk1, Nx, Ny, ff12);
	cycleimage(ff12, Nx, Ny, x0, y0);
}

void	alloc_fft(fft_type *fk, int Nx, int Ny)
{
	int 	n, y;

	if (Ny % 2) {
		exit(1); /*("alloc_fft: Ny must be even\n");*/
	}

	n = Ny / 2;
	*fk = (complex **) calloc(n + 1, sizeof(complex *));
	(*fk)[0] = (complex *) calloc(Nx * (n + 1), sizeof(complex));
	if (!(*fk) || !((*fk)[0]))
		exit(1); /*error_exit("alloc_fft: failed to allocate memory\n");*/
	for (y = 1; y <= n; y++) {
		(*fk)[y] = (*fk)[y - 1] + Nx;	
	}
}

void	free_fft(fft_type fk, int Nx, int Ny)
{
	free(fk[0]);
	free(fk);
}

#else 
#ifdef CMUNIPACK_USE_FFTW



#endif
#endif
