#include <stdint.h>

/* FFT computation */
#ifndef FFTCOMPLEX_T_DEFINED
typedef struct FFTComplex {
    float re, im;
} FFTComplex;
#define FFTCOMPLEX_T_DEFINED
#endif

void sbr_qmf_pre_shuffle_sse(float *z);
void fft32_interleave_sse(FFTComplex *z);
//void vector_fmul_reverse_sse(float *dst, const float *src0, const float *src1, uint32_t len);
//float __attribute__ ((aligned (16))) sbr_qmf_window_ds[320];
//float __attribute__ ((aligned (16))) sbr_ldqmf_window_ds[320];

static const float __attribute__((aligned(16))) tcos_analysis[64] = {
    -0.008677451287989, -0.078057863137826, -0.147250226758303, -0.216087851684923,
    -0.284404902049773, -0.352036796094658, -0.418820602662969, -0.484595433715113,
    -0.54920283192189,  -0.612487152402081, -0.674295937684597, -0.734480284991882,
    -0.792895204959746, -0.849399970929445, -0.903858457970525, -0.956139470817702,
    -1.00611705993175, -1.05367082492298, -1.09868620460631, -1.14105475298919,
    -1.18067440052753, -1.21744970002014, -1.25129205654939, -1.28211994091418,
    -1.30985908604087, -1.33444266589917, -1.35581145649195, -1.37391397853099,
    -1.38870662145509, -1.40015374849177, -1.40822778250935, -1.41290927245269,
     1.41418694020244,  1.4120577077451,   1.40652670458816,  1.39760725540267,
     1.38532084792299,  1.36969708118088,  1.35077359419891,  1.32859597531472,
     1.30321765235473,  1.27469976392184,  1.24311101210715,  1.20852749698062,
     1.17103253325936,  1.13071644959515,  1.0876763709648,   1.04201598468759,
     0.993845290633451, 0.943280336223609, 0.890442936862206, 0.835460382472311,
     0.778465130843354, 0.719594488528702, 0.658990280062152, 0.596798506290201,
     0.533168992643221, 0.468255028192876, 0.402212996365318, 0.335201998199818,
     0.267383469060427, 0.198920789724042, 0.12997889278181,  0.060723865302061, 
};

static const FFTComplex __attribute__((aligned(16))) tcos_analysis_ld[16] = {
  { 1.414107, 0.017355 }, { 1.405597, 0.155878 }, { 1.383550, 0.292900 }, { 1.348178, 0.427101 },
  { 1.299823, 0.557189 }, { 1.238950, 0.681911 }, { 1.166145, 0.800066 }, { 1.082110, 0.910515 },
  { 0.987653, 1.012196 }, { 0.883685, 1.104129 }, { 0.771206, 1.185429 }, { 0.651300, 1.255312 },
  { 0.525122, 1.313106 }, { 0.393886, 1.358254 }, { 0.258857, 1.390321 }, { 0.121336, 1.408999 },
};

static void vector_fmul_reverse_sse(float *dst, const float *src0,
                                    const float *src1, int len){
    uint64_t i = len*4-32;
    __asm__ volatile(
        "1: \n\t"
        "movaps        16(%1), %%xmm0 \n\t"
        "movaps          (%1), %%xmm1 \n\t"
        "shufps $0x1b, %%xmm0, %%xmm0 \n\t"
        "shufps $0x1b, %%xmm1, %%xmm1 \n\t"
        "mulps        (%3,%0), %%xmm0 \n\t"
        "mulps      16(%3,%0), %%xmm1 \n\t"
        "movaps     %%xmm0,   (%2,%0) \n\t"
        "movaps     %%xmm1, 16(%2,%0) \n\t"
        "add    $32, %1 \n\t"
        "sub    $32, %0 \n\t"
        "jge    1b \n\t"
        :"+r"(i), "+r"(src1)
        :"r"(dst), "r"(src0));
}

static void sbr_sum64x5_sse(float *z){
    uint64_t i = 0;
    __asm__ volatile(
        "1: \n\t"
        "movaps       (%1,%0), %%xmm0 \n\t"
        "addps     256(%1,%0), %%xmm0 \n\t"
        "addps     512(%1,%0), %%xmm0 \n\t"
        "addps     768(%1,%0), %%xmm0 \n\t"
        "addps    1024(%1,%0), %%xmm0 \n\t"
        "movaps     %%xmm0,   (%1,%0) \n\t"
        "add    $16, %0 \n\t"
        "cmp  $1024, %0 \n\t"
        "jne    1b \n\t"
        :"+r"(i)
        :"r"(z));
}

void sbr_mdct_postrotate_sse(FFTComplex *z, unsigned int n, const float *tcos)
{
    long j, k = n, n2 = k >> 1, n4 = k >> 2;

    /* post rotation + reinterleave + reorder */
#define CMUL(j,xmm0,xmm1)\
        "movups   (%2,"#j",2), "#xmm1"\n"\
        "movups 16(%2,"#j",2), %%xmm2 \n"\
        "movaps       "#xmm1", "#xmm0"\n"\
        "shufps $0xdd, %%xmm2, "#xmm0"\n"\
        "shufps $0x88, %%xmm2, "#xmm1"\n"\
        "movaps        "#xmm1",%%xmm6 \n"\
        "movaps        "#xmm0",%%xmm7 \n"\
        "mulps      (%3,"#j"), %%xmm6 \n"\
        "mulps      (%3,"#j"), %%xmm7 \n"\
        "mulps      (%4,"#j"), "#xmm0"\n"\
        "mulps      (%4,"#j"), "#xmm1"\n"\
        "addps         %%xmm6, "#xmm0"\n"\
        "subps         %%xmm7, "#xmm1"\n"

    j = -n2;
    k =  n2-16;
    __asm__ volatile(
        "1: \n"
        CMUL(%0, %%xmm0, %%xmm1)
        CMUL(%1, %%xmm4, %%xmm5)
        "shufps    $0x1b, %%xmm1, %%xmm1 \n"
        "shufps    $0x1b, %%xmm5, %%xmm5 \n"
        "movaps   %%xmm4, %%xmm3 \n"
        "unpckhps %%xmm1, %%xmm3 \n"
        "unpcklps %%xmm1, %%xmm4 \n"
        "movaps   %%xmm0, %%xmm2 \n"
        "unpckhps %%xmm5, %%xmm2 \n"
        "unpcklps %%xmm5, %%xmm0 \n"
        "movups   %%xmm4,   (%2,%1,2) \n"
        "movups   %%xmm3, 16(%2,%1,2) \n"
        "movups   %%xmm0,   (%2,%0,2) \n"
        "movups   %%xmm2, 16(%2,%0,2) \n"
        "sub $16, %1 \n"
        "add $16, %0 \n"
        "jl 1b \n"
        :"+&r"(j), "+&r"(k)
        :"r"(z+n4), "r"(tcos+n4), "r"(tcos+3*n4)
        :"memory");
}

#if 0
void aac_sbr_init(void)
{
    unsigned int n;
    for (n = 0; n < 320; n++) {
        sbr_qmf_window_ds[n] = sbr_qmf_window_us[2*n];
    }
    for (n = 0; n < 320; n++) {
        sbr_ldqmf_window_ds[n] = 0.5f * (sbr_ldqmf_window_us[2*n+1] - sbr_ldqmf_window_us[2*n]) + sbr_ldqmf_window_us[2*n];
    }
}
#endif

/**
 * Analysis QMF Bank (14496-3 sp04 p206)
 *
 * @param   x       pointer to the beginning of the first sample window
 * @param   W       array of complex-valued samples split into subbands
 */
static const uint8_t revtab32_fwd[32] = {
   0,  8,  4, 13,  1,  9,  7, 14,  2, 10,  6, 15,  3, 11,  5, 12,
  16, 28, 20, 25, 17, 31, 23, 26, 18, 30, 22, 27, 19, 29, 21, 24,
};
void sbr_qmf_analysis(void *mdct, float *sbr_qmf_window_ds, float *x,
                      float z[320], FFTComplex W[2][32][32], uint32_t buf_idx)
{
    unsigned int i, j, k;
    //vector_fmul_copy_sse(x, x+1024, 288);
    //vector_fmul_copy_sse(x+288, in, 1024);
    for (i = 0; i < 32; i++) { // numTimeSlots*RATE = 16*2
        vector_fmul_reverse_sse(z, sbr_qmf_window_ds, x, 320);
        sbr_sum64x5_sse(z);
        sbr_qmf_pre_shuffle_sse(z);
        /* pre rotation */
        for(k = 0; k < 16; k++) {
            j=revtab32_fwd[k];
            /* this is just input * conj(tcos) */
            z[2*j+0] = (z[128 - 4*k - 1] * tcos_analysis[32+2*k] + z[64+4*k  ] * tcos_analysis[   2*k]);
            z[2*j+1] = (z[128 - 4*k - 1] * tcos_analysis[   2*k] - z[64+4*k  ] * tcos_analysis[32+2*k]);
            j=revtab32_fwd[k+16];
            /* this is just input * conj(tcos) */
            z[2*j+0] = (z[128 - 4*k - 3] * tcos_analysis[32+2*k+1] + z[64+4*k+2] * tcos_analysis[   2*k+1]);
            z[2*j+1] = (z[128 - 4*k - 3] * tcos_analysis[   2*k+1] - z[64+4*k+2] * tcos_analysis[32+2*k+1]);
        }
        fft32_interleave_sse((FFTComplex*)z);
        sbr_mdct_postrotate_sse((FFTComplex*)z, 64, tcos_analysis);
        for (k = 0; k < 32; k++) {
            W[buf_idx][i][k].re = z[k];
            W[buf_idx][i][k].im = z[63-k];
        }
        x += 32;
    }
}

