/*====================================================================================
    EVS Codec 3GPP TS26.443 Nov 13, 2018. Version 12.11.0 / 13.7.0 / 14.3.0 / 15.1.0
  ====================================================================================*/

#include <math.h>
#include "options.h"
#include "cnst.h"
#include "rom_com.h"
#include "prot.h"

namespace evs { 


/*--------------------------------------------------------------------------
* window_ola()
*
* Windowing, Overlap and Add
*--------------------------------------------------------------------------*/


void window_ola(
    const float *ImdctOut,          /* i  : input                       */
    float *auOut,             /* o  : output audio                */
    float *OldauOut,          /* i/o: audio from previous frame   */
    const short L,                  /* i  : length                      */
    const short right_mode,
    const short left_mode,          /* window overlap of current frame (0: full, 2: none, or 3: half) */
    const short use_bfi_win,        /* i  : use BFI windowing           */
    const short oldHqVoicing,       /* i  : previous HqVoicing          */
    float *oldgapsynth        /* i  : previous gapsynth           */
)
{
    short i, decimate, decay;
    short n ,n16, windecay48, windecay16;
    float win_right[R2_48];
    float win_int_left[R1_16];
    float win_left[R1_48];
    float win_int_right[R2_16];


    float SS2[L_FRAME48k-NS2SA(48000, N_ZERO_MDCT_NS)];
    float wret2[L_FRAME48k-NS2SA(48000, N_ZERO_MDCT_NS)];

    float *paout;


    n = (short)((float)L*N_ZERO_MDCT_NS/FRAME_SIZE_NS);
    n16 = (short)((float)L_FRAME16k*N_ZERO_MDCT_NS/FRAME_SIZE_NS);
    windecay48 = (short)(2*((float)L_FRAME48k*N_ZERO_MDCT_NS/FRAME_SIZE_NS))+R1_48;
    windecay16 = (short)(2*((float)L_FRAME16k*N_ZERO_MDCT_NS/FRAME_SIZE_NS))+R1_16;
    decimate = 1;
    decay = 0;

    tcx_get_windows_mode1(   left_mode,  right_mode, win_left, win_right, win_int_left, win_int_right, L);

    if ( L == L_FRAME32k || L == L_FRAME16k )
    {
        decimate = 3;
        decay = 1;
    }
    else if ( L == L_FRAME8k )
    {
        decimate = 6;
        decay = 2;
    }

    else if (L == 512)
    {
        windecay48 = (short)(2*((float)512*N_ZERO_MDCT_NS/FRAME_SIZE_NS))+R1_25;
        decimate = 1;
        decay = 0;
    }
    else if (L== 256)
    {
        windecay48 = (short)(2*((float)512*N_ZERO_MDCT_NS/FRAME_SIZE_NS))+R1_25;
        decimate = 2;
        decay = 0;
    }

    paout=auOut-n;

    if( use_bfi_win )
    {
        if ( L == L_FRAME32k )
        {
            for (i = 0; i < L/2; i+=2)
            {
                wret2[L/2-n + i +1] = win_left[(L_FRAME16k/2-i/2-1)*decimate+decay];
                wret2[L/2-n + i] =  win_int_left[(L_FRAME16k/2-i/2-1)];
            }

            for (i = n; i < L/2; i+=2)
            {
                wret2[ i -n] = win_left[(L_FRAME16k-i/2)*decimate-decay-1];
                wret2[ i +1-n] = win_int_left[L_FRAME16k-(i/2)-1];
            }
        }
        else
        {
            for (i = 0; i < L/2; i++)
            {
                wret2[i+L/2-n] =  win_left[(L/2-i-1)*decimate+decay];
            }

            for (i =n; i < L/2; i++)
            {
                wret2[i-n] =  win_left[(L-i)*decimate-decay-1];
            }
        }
        sinq(EVS_PI/(2*(L-n)), EVS_PI/(4*(L-n)), L-n, SS2);

        for (i = n; i < L/2; i++)
        {
            paout[i] = ImdctOut[L/2 + i];
        }

        for (i = 0; i < L/2; i++)
        {
            paout[L/2 + i] = -ImdctOut[L - 1 - i];
        }

        if( oldHqVoicing )
        {
            for (i=0 ; i < L-n; i++)
            {
                auOut[i] = auOut[i]*SS2[i]+ oldgapsynth[i+n]*(SS2[L-n-i-1]);
            }
        }
        else
        {
            for (i=0 ; i < L-n; i++)
            {
                auOut[i] =auOut[i]*SS2[i]+ OldauOut[i+n]*(SS2[L-n-i-1])/(wret2[i]+0.01f);
            }
        }
    }

    if ( L == L_FRAME32k )
    {
        if (use_bfi_win==0)
        {
            for (i = n; i < L/2; i+=2)
            {
                paout[i] = ImdctOut[L/2 + i] * win_right[(2*L_FRAME16k-(n16+(i-n)/2))*decimate-1-decay-windecay48]+OldauOut[i];
                paout[i+1] = ImdctOut[L/2 + i +1] * win_int_right[2*L_FRAME16k-(n16+(i-n)/2)-1-windecay16]+OldauOut[i+1];
            }

            for (i = 0; i < L/2-n; i+=2)
            {
                paout[L/2 + i +1] = -ImdctOut[L - 1 - (i+1)] * win_right[(3*L_FRAME16k/2-1-i/2)*decimate+decay-windecay48]+OldauOut[i+L/2+1];
                paout[L/2 + i ] = -ImdctOut[L - 1 - i] * win_int_right[(3*L_FRAME16k/2-1-i/2)-windecay16]+OldauOut[i+L/2];
            }

            for (i = L/2-n; i < L/2; i+=2)
            {
                paout[L/2 + i +1] = -ImdctOut[L - 1 - (i+1)]+OldauOut[i+L/2+1] ;
                paout[L/2 + i ] = -ImdctOut[L - 1 - i]+OldauOut[i+L/2];
            }
        }

        for (i = 0; i < L/2; i+=2)
        {
            OldauOut[L/2 + i +1] = -ImdctOut[i+1] * win_left[(L_FRAME16k/2-i/2-1)*decimate+decay];
            OldauOut[L/2 + i] = -ImdctOut[i] * win_int_left[(L_FRAME16k/2-i/2-1)];
        }


        for (i = n; i < L/2; i+=2)
        {
            OldauOut[ i] = -ImdctOut[L/2 - 1 - i]  *win_left[(L_FRAME16k-i/2)*decimate-decay-1];
            OldauOut[ i +1] = -ImdctOut[L/2 - 1 - (i +1)] * win_int_left[L_FRAME16k-(i/2)-1];
        }
    }
    else
    {
        if (use_bfi_win==0)
        {

            for (i = n; i < L/2; i++)
            {
                paout[i] = ImdctOut[L/2 + i] * win_right[(2*L-i)*decimate-1-decay-windecay48]+OldauOut[i];
            }

            for (i = 0; i < L/2-n; i++)
            {
                paout[L/2 + i] = -ImdctOut[L - 1 - i] * win_right[(3*L/2-1-i)*decimate+decay-windecay48]+OldauOut[i+L/2];
            }
            for (i = L/2-n; i < L/2; i++)
            {
                paout[L/2 + i] = -ImdctOut[L - 1 - i] + OldauOut[i+L/2];
            }
        }

        for (i = 0; i < L/2; i++)
        {
            OldauOut[L/2 + i] = -ImdctOut[i] * win_left[(L/2-i-1)*decimate+decay];
        }


        for (i = n; i < L/2; i++)
        {
            OldauOut[ i] = -ImdctOut[L/2 - 1 - i] * win_left[(L-i)*decimate-decay-1];
        }


    }

    for (i = 0; i < n; i++)
    {
        OldauOut[i] = -ImdctOut[L/2 - 1 - i];
    }
    for (i = 0; i < n; i++)
    {
        paout[L + i] = OldauOut[i];
    }

    return;
}

/*---------------------------------------------------------------------*
* core_switching_OLA()
*
* modify window after HQ core decoding
* Overlap ACELP and HQ
*---------------------------------------------------------------------*/

void core_switching_OLA(
    const float *mem_over_hp,       /* i  : upsampling filter memory            */
    const short last_L_frame,       /* i  : last L_frame lengthture             */
    const int   output_Fs,          /* i  : output sampling rate                */
    float *synth,             /* i/o: synthesized signal from HQ core     */
    const float *synth_subfr_out,   /* i  : synthesized signal from ACELP core  */
    float *synth_subfr_bwe,   /* i  : synthesized BWE from ACELP core     */
    const short output_frame,       /* i  : output frame length                 */
    const short bwidth              /* i  : output bandwidth                    */
)
{
    short i, L, Loverlapp, out_filt_length, filt_delay, decimate, decay;
    float tmp_buf_switch[SWITCH_MAX_GAP], tmp_buf_switch2[HQ_DELAY_COMP*HQ_DELTA_MAX+2];
    float delta;
    const float *win, *win_int;

    win = window_48kHz;
    win_int = window_8_16_32kHz;
    decimate = 1;
    decay = 0;

    if( output_frame == L_FRAME32k || output_frame == L_FRAME16k )
    {
        decimate = 3;
        decay = 1;
    }
    else if( output_frame == L_FRAME8k )
    {
        decimate = 6;
        decay = 2;
    }

    /* set multiplication factor according to the sampling rate */
    delta = 1;
    if( output_frame == L_FRAME16k )
    {
        delta = 2;
    }
    else if( output_frame == L_FRAME32k )
    {
        delta = 4;
    }
    else if( output_frame == L_FRAME48k )
    {
        delta = 6;
    }

    set_f( tmp_buf_switch, 0, SWITCH_MAX_GAP );
    set_f( tmp_buf_switch2, 0, HQ_DELAY_COMP*HQ_DELTA_MAX+2);

    Loverlapp = (short)(delta*SWITCH_OVERLAP_8k);

    mvr2r( synth_subfr_out,tmp_buf_switch, NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS));  /* copy 6.25 ms subframe  */

    /* conversion from 12.8kHz to output_Fs */
    if( last_L_frame == L_FRAME)
    {
        /* resample filter memory */
        if( output_frame == L_FRAME8k )
        {
            mvr2r( synth_subfr_out + NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS), tmp_buf_switch+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS),NS2SA(output_Fs, DELAY_CLDFB_NS)); /* copy subframe to tmp buffer */
        }
        else
        {
            out_filt_length = modify_Fs_intcub3m_sup( mem_over_hp+2, NS2SA(12800,DELAY_CLDFB_NS), 12800, tmp_buf_switch2, output_Fs , &filt_delay );

            for( i=0; i<filt_delay; i++ )
            {
                tmp_buf_switch2[out_filt_length+i-filt_delay] = tmp_buf_switch2[out_filt_length-1-filt_delay];
            }

            if ( (bwidth == NB && output_Fs>=16000) || (bwidth > NB && output_Fs>16000) )
            {
                /* mix cubic and CLDFB resampled buffers in case of resampling to higher frequency rates */
                for(i=0; i<out_filt_length; i++)
                {
                    float a = (float) i / out_filt_length;
                    float b = 1.f - a;

                    tmp_buf_switch[NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS) + i] = a * tmp_buf_switch2[i] + b * synth_subfr_out[NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS) + i];
                }
            }
            else
            {
                /* copy cubic resampled buffer (memory) */
                mvr2r( tmp_buf_switch2, tmp_buf_switch+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS), out_filt_length );
            }
        }
    }
    else
    {
        if( output_frame == L_FRAME16k )
        {
            /* no resampling */
            mvr2r( mem_over_hp+2, tmp_buf_switch+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS), NS2SA(output_Fs,DELAY_CLDFB_NS) );
        }
        else
        {
            if( output_frame == L_FRAME8k )
            {
                mvr2r( synth_subfr_out+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS), tmp_buf_switch+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS), NS2SA(output_Fs, DELAY_CLDFB_NS)); /* copy subframe to tmp buffer */
            }
            else
            {
                /* resample filter memory */
                out_filt_length = modify_Fs_intcub3m_sup( mem_over_hp+2, NS2SA(16000,DELAY_CLDFB_NS), 16000, tmp_buf_switch2, output_Fs, &filt_delay );

                for( i=0; i<filt_delay; i++ )
                {
                    tmp_buf_switch2[out_filt_length+i-filt_delay] = tmp_buf_switch2[out_filt_length-1-filt_delay];
                }

                if ( (bwidth == NB && output_Fs>=16000) || (bwidth > NB && output_Fs>16000) )
                {
                    /* mix cubic and CLDFB resampled buffers in case of resampling to higher frequency rates */
                    for(i=0; i<out_filt_length; i++)
                    {
                        float a = (float) i / out_filt_length;
                        float b = 1.f - a;

                        tmp_buf_switch[NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS) + i] = a * tmp_buf_switch2[i] + b * synth_subfr_out[NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS) + i];
                    }
                }
                else
                {
                    /* copy cubic resampled buffer (memory) */
                    mvr2r( tmp_buf_switch2, tmp_buf_switch+NS2SA(output_Fs,SWITCH_GAP_LENGTH_NS), out_filt_length );
                }
            }
        }
    }

    /* Windowing for overlapadd */
    L = NS2SA(output_Fs, SWITCH_GAP_LENGTH_NS + DELAY_CLDFB_NS); /* 6.25 ms gap + 1.25 ms resamp  */

    set_f( synth, 0, L - Loverlapp);

    if( output_frame == L_FRAME32k )
    {
        for( i=0; i<NS2SA(output_Fs,10000000.0f-N_ZERO_MDCT_NS); i+=2 )
        {
            synth[L-Loverlapp+i] /= win[(3*L_FRAME16k/2-1-i/2)*decimate+decay-(short)(2*((float)L_FRAME48k*N_ZERO_MDCT_NS/FRAME_SIZE_NS))];
            synth[L-Loverlapp+i+1] /= win_int[(3*L_FRAME16k/2-1-i/2)-(short)(2*((float)L_FRAME16k*N_ZERO_MDCT_NS/FRAME_SIZE_NS))];
        }
    }
    else
    {
        for( i=0 ; i<NS2SA(output_Fs,10000000.0f-N_ZERO_MDCT_NS); i++ )
        {
            synth[L-Loverlapp+i] /= win[(3*output_frame/2-1-i)*decimate+decay-(short)(2*((float)L_FRAME48k*N_ZERO_MDCT_NS/FRAME_SIZE_NS))];
        }
    }


    for( i=0; i<Loverlapp; i++ ) /* Windowing for overlapadd */
    {
        synth[i+(short)(L-Loverlapp)] *= (float)sin((i+1)*EVS_PI/(2*(Loverlapp+1)));
        synth_subfr_bwe[i+(short)(L-Loverlapp)-NS2SA(output_Fs,DELAY_CLDFB_NS)] *= (float)cos((i+1)*EVS_PI/(2*(Loverlapp+1)))*(float)cos((i+1)*EVS_PI/(2*(Loverlapp+1)));
        tmp_buf_switch[i+(short)(L-Loverlapp)] *= (float)cos((i+1)*EVS_PI/(2*(Loverlapp+1)))*(float)cos((i+1)*EVS_PI/(2*(Loverlapp+1)));
    }

    /* overlap-add ACELP (tmp_buf_switch) + HQ (synth) */
    for( i=0; i<NS2SA(output_Fs,DELAY_CLDFB_NS); i++ )
    {
        synth[i] += tmp_buf_switch[i];

    }
    for( i=NS2SA(output_Fs,DELAY_CLDFB_NS); i<L; i++ )
    {
        synth[i]+= tmp_buf_switch[i] + synth_subfr_bwe[i-NS2SA(output_Fs,DELAY_CLDFB_NS)];
    }

    return;
}


/*-------------------------------------------------------------------*
* sinq()
*
* Fast sinus generate sin(tmp*i+phi)
*-------------------------------------------------------------------*/

void sinq(
    const float tmp,    /* i  : sinus factor cos(tmp*i+phi)  */
    const float phi,    /* i  : sinus phase cos(tmp*i+phi)  */
    const short N,      /* i  : size of output */
    float x[]           /* o  : output vector  */
)
{
    float A;
    short i;

    x[0] = (float)sin(phi);
    x[1] = (float)sin(tmp+phi);
    x[2] = (float)sin(2*tmp+phi);

    if (fabs((float)(tmp)) > 0.0001f)
    {
        A = (x[2]+x[0])/x[1];
    }
    else
    {
        A = 0;
    }

    for (i=3; i<N; i++)
    {
        x[i] = A*x[i-1]-x[i-2];
    }

    return;
}


} // end of namespace
