/*
 * codec2_fft.c
 *
 *  Created on: 24.09.2016
 *      Author: danilo
 */

#include "codec2_fft.h"

#include "debug_alloc.h"

#ifdef USE_KISS_FFT
#include "_kiss_fft_guts.h"

#else
#if 0
// caching constants in RAM did not seem to have an effect on performance
// TODO: Decide what to with this code
#define FFT_INIT_CACHE_SIZE 4
const arm_cfft_instance_f32* fft_init_cache[FFT_INIT_CACHE_SIZE];

static const arm_cfft_instance_f32* arm_fft_instance2ram(const arm_cfft_instance_f32* in)
{

    arm_cfft_instance_f32* out = malloc(sizeof(arm_cfft_instance_f32));

    if (out) {
        memcpy(out,in,sizeof(arm_cfft_instance_f32));
        out->pBitRevTable = malloc(out->bitRevLength * sizeof(uint16_t));
        out->pTwiddle = malloc(out->fftLen * sizeof(float32_t));
        memcpy((void*)out->pBitRevTable,in->pBitRevTable,out->bitRevLength * sizeof(uint16_t));
        memcpy((void*)out->pTwiddle,in->pTwiddle,out->fftLen * sizeof(float32_t));
    }
    return out;
}


static const arm_cfft_instance_f32* arm_fft_cache_get(const arm_cfft_instance_f32* romfft)
{
    const arm_cfft_instance_f32* retval = NULL;
    static int used = 0;
    for (int i = 0; fft_init_cache[i] != NULL && i < used; i++)
    {
        if (romfft->fftLen == fft_init_cache[i]->fftLen)
        {
            retval = fft_init_cache[i];
            break;
        }
    }
    if (retval == NULL && used < FFT_INIT_CACHE_SIZE)
    {
         retval = arm_fft_instance2ram(romfft);
         fft_init_cache[used++] = retval;
    }
    if (retval == NULL)
    {
        retval = romfft;
    }
    return retval;
}
#endif
#endif

void codec2_fft_free(codec2_fft_cfg cfg)
{
#ifdef USE_KISS_FFT
    KISS_FFT_FREE(cfg);
#else
    FREE(cfg);
#endif
}

codec2_fft_cfg codec2_fft_alloc(int nfft, int inverse_fft, void* mem, size_t* lenmem)
{
    codec2_fft_cfg retval;
#ifdef USE_KISS_FFT
    retval = kiss_fft_alloc(nfft, inverse_fft, mem, lenmem);
#else
    retval = MALLOC(sizeof(codec2_fft_struct));
    retval->inverse  = inverse_fft;
    switch(nfft)
    {
    case 128:
        retval->instance = &arm_cfft_sR_f32_len128;
        break;
    case 256:
        retval->instance = &arm_cfft_sR_f32_len256;
        break;
    case 512:
        retval->instance = &arm_cfft_sR_f32_len512;
        break;
//    case 1024:
//        retval->instance = &arm_cfft_sR_f32_len1024;
//        break;
    default:
        abort();
    }
    // retval->instance = arm_fft_cache_get(retval->instance);
#endif
    return retval;
}

codec2_fftr_cfg codec2_fftr_alloc(int nfft, int inverse_fft, void* mem, size_t* lenmem)
{
    codec2_fftr_cfg retval;
#ifdef USE_KISS_FFT
    retval = kiss_fftr_alloc(nfft, inverse_fft, mem, lenmem);
#else
    retval = MALLOC(sizeof(codec2_fftr_struct));
    retval->inverse  = inverse_fft;
    retval->instance = MALLOC(sizeof(arm_rfft_fast_instance_f32));
    arm_rfft_fast_init_f32(retval->instance,nfft);
    // memcpy(&retval->instance->Sint,arm_fft_cache_get(&retval->instance->Sint),sizeof(arm_cfft_instance_f32));
#endif
    return retval;
}
void codec2_fftr_free(codec2_fftr_cfg cfg)
{
#ifdef USE_KISS_FFT
    KISS_FFT_FREE(cfg);
#else
    FREE(cfg->instance);
    FREE(cfg);
#endif
}

// there is a little overhead for inplace kiss_fft but this is
// on the powerful platforms like the Raspberry or even x86 PC based ones
// not noticeable
// the reduced usage of RAM and increased performance on STM32 platforms
// should be worth it.
void codec2_fft_inplace(codec2_fft_cfg cfg, codec2_fft_cpx* inout)
{

#ifdef USE_KISS_FFT
    // decide whether to use the local stack based buffer for in
    // or to allow kiss_fft to allocate RAM
    // second part is just to play safe since first method
    // is much faster and uses less RAM
    if (cfg->nfft <= 512)
    {
        kiss_fft_cpx in[512];
        memcpy(in,inout,cfg->nfft*sizeof(kiss_fft_cpx));
        kiss_fft(cfg, in, (kiss_fft_cpx*)inout);
    }
    else
    {
        kiss_fft(cfg, (kiss_fft_cpx*)inout, (kiss_fft_cpx*)inout);
    }
#else
    arm_cfft_f32(cfg->instance,(float*)inout,cfg->inverse,1);
    if (cfg->inverse)
    {
        arm_scale_f32((float*)inout,cfg->instance->fftLen,(float*)inout,cfg->instance->fftLen*2);
    }

#endif
}
