#include "fft.h"
#include <math.h>
#include <stdlib.h>

/* Everything here comes from Audacity 1.3.13
 (orignally in C++ and with more genericity and functionnality)
 Original Author : Dominic Mazzoni
 Licenced under GPL 2.0 (see LICENCE)
*/

#define MaxFastBits 16

int **gFFTBitTable = NULL;

void FFT(int NumSamples,
		int InverseTransform,
		float *RealIn, float *ImagIn, float *RealOut, float *ImagOut)
{
	int NumBits;                 /* Number of bits needed to store indices */
	int i, j, k, n;
	int BlockSize, BlockEnd;

	double angle_numerator = 2.0 * M_PI;
	double tr, ti;                /* temp real, temp imaginary */
/*
	if (!IsPowerOfTwo(NumSamples)) {
		fprintf(stderr, "%d is not a power of two\n", NumSamples);
		exit(1);
	}
*/
	if (!gFFTBitTable)
		InitFFT();

	if (!InverseTransform)
		angle_numerator = -angle_numerator;

	NumBits = NumberOfBitsNeeded(NumSamples);

	/*
	 **   Do simultaneous data copy and bit-reversal ordering into outputs...
	 */
	for (i = 0; i < NumSamples; i++) {
		j = FastReverseBits(i, NumBits);
		RealOut[j] = RealIn[i];
		ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
	}

	/*
	 **   Do the FFT itself...
	 */

	BlockEnd = 1;
	for (BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) {

		double delta_angle = angle_numerator / (double) BlockSize;

		double sm2 = sin(-2 * delta_angle);
		double sm1 = sin(-delta_angle);
		double cm2 = cos(-2 * delta_angle);
		double cm1 = cos(-delta_angle);
		double w = 2 * cm1;
		double ar0, ar1, ar2, ai0, ai1, ai2;

		for (i = 0; i < NumSamples; i += BlockSize) {
			ar2 = cm2;
			ar1 = cm1;

			ai2 = sm2;
			ai1 = sm1;

			for (j = i, n = 0; n < BlockEnd; j++, n++) {
				ar0 = w * ar1 - ar2;
				ar2 = ar1;
				ar1 = ar0;

				ai0 = w * ai1 - ai2;
				ai2 = ai1;
				ai1 = ai0;

				k = j + BlockEnd;
				tr = ar0 * RealOut[k] - ai0 * ImagOut[k];
				ti = ar0 * ImagOut[k] + ai0 * RealOut[k];

				RealOut[k] = RealOut[j] - tr;
				ImagOut[k] = ImagOut[j] - ti;

				RealOut[j] += tr;
				ImagOut[j] += ti;
			}
		}
		BlockEnd = BlockSize;
	}

	/*
	 **   Need to normalize if inverse transform...
	 */

	if (InverseTransform) {
		float denom = (float) NumSamples;

		for (i = 0; i < NumSamples; i++) {
			RealOut[i] /= denom;
			ImagOut[i] /= denom;
		}
	}
}

void InitFFT()
{
	gFFTBitTable = malloc(MaxFastBits*sizeof(int));

	int len = 2;
	int b, i;
	for (b=1; b<=MaxFastBits; b++) {
		gFFTBitTable[b-1]=malloc(len*sizeof(int));

		for (i=0; i<len; i++)
			gFFTBitTable[b-1][i] = ReverseBits(i, b);

		len <<= 1;
	}
}

int NumberOfBitsNeeded(int PowerOfTwo)
{
	int i;

/*
	if (PowerOfTwo < 2) {
		fprintf(stderr, "Error: FFT called with size %d\n", PowerOfTwo);
		exit(1);
	}
*/
	for (i = 0;; i++)
		if (PowerOfTwo & (1 << i))
			return i;
}

inline int FastReverseBits(int i, int NumBits)
{
	if (NumBits <= MaxFastBits)
		return gFFTBitTable[NumBits - 1][i];
	else
		return ReverseBits(i, NumBits);
}

int ReverseBits(int index, int NumBits)
{
   int i, rev;

   for (i = rev = 0; i < NumBits; i++) {
      rev = (rev << 1) | (index & 1);
      index >>= 1;
   }

   return rev;
}


/*
 * PowerSpectrum
 *
 * This function computes the same as RealFFT, above, but
 * adds the squares of the real and imaginary part of each
 * coefficient, extracting the power and throwing away the
 * phase.
 *
 * For speed, it does not call RealFFT, but duplicates some
 * of its code.
 */

void PowerSpectrum(float *In, float *Out)
{
	int i;

	float theta = M_PI / 128;

	float tmpReal[128];
	float tmpImag[128];
	float RealOut[128];
	float ImagOut[128];

	for (i = 0; i < 128; i++) {
		tmpReal[i] = In[2 * i];
		tmpImag[i] = In[2 * i + 1]; //FIXME : WTFFFF ?
		tmpImag[i]=0;
	}

	FFT(128, 0, tmpReal, tmpImag, RealOut, ImagOut);

	float wtemp = sin(0.5 * theta);

	float wpr = -2.0 * wtemp * wtemp;
	float wpi = -1.0 * sin(theta);
	float wr = 1.0 + wpr;
	float wi = wpi;

	int i3;

	float h1r, h1i, h2r, h2i, rt, it;
	for (i = 1; i < 128 / 2; i++) {

		i3 = 128 - i;

		h1r = 0.5 * (RealOut[i] + RealOut[i3]);
		h1i = 0.5 * (ImagOut[i] - ImagOut[i3]);
		h2r = 0.5 * (ImagOut[i] + ImagOut[i3]);
		h2i = -0.5 * (RealOut[i] - RealOut[i3]);

		rt = h1r + wr * h2r - wi * h2i;
		it = h1i + wr * h2i + wi * h2r;

		Out[i] = rt * rt + it * it;

		rt = h1r - wr * h2r + wi * h2i;
		it = -h1i + wr * h2i + wi * h2r;

		Out[i3] = rt * rt + it * it;

		wr = (wtemp = wr) * wpr - wi * wpi + wr;
		wi = wi * wpr + wtemp * wpi + wi;
	}

	rt = (h1r = RealOut[0]) + ImagOut[0];
	it = h1r - ImagOut[0];
	Out[0] = rt * rt + it * it;
	rt = RealOut[128 / 2];
	it = ImagOut[128 / 2];
	Out[128 / 2] = rt * rt + it * it;
}