/*
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jitsi.impl.neomedia.codec.audio.silk;

import static org.jitsi.impl.neomedia.codec.audio.silk.Macros.*;

/**
 * Resample with a 2x downsampler (optional), a 2nd order AR filter followed by FIR interpolation.
 *
 * @author Jing Dai
 * @author Dingxin Xu
 */
public class ResamplerPrivateDownFIR
{
    /**
     * Resample with a 2x downsampler (optional),
     * a 2nd order AR filter followed by FIR interpolation.
     * @param SS Resampler state.
     * @param out Output signal.
     * @param out_offset offset of valid data.
     * @param in Input signal.
     * @param in_offset offset of valid data.
     * @param inLen Number of input samples.
     */
    static void SKP_Silk_resampler_private_down_FIR(
        Object                      SS,               /* I/O: Resampler state                         */
        short[]                        out,            /* O:    Output signal                             */
        int out_offset,
        short[]                        in,             /* I:    Input signal                            */
        int in_offset,
        int                         inLen            /* I:    Number of input samples                    */
    )
    {
        SKP_Silk_resampler_state_struct S = (SKP_Silk_resampler_state_struct)SS;
        int nSamplesIn, interpol_ind;
        int max_index_Q16, index_Q16, index_increment_Q16, res_Q6;
        short[] buf1 = new short[ ResamplerPrivate.RESAMPLER_MAX_BATCH_SIZE_IN / 2 ];
        int[] buf2 = new int[ ResamplerPrivate.RESAMPLER_MAX_BATCH_SIZE_IN + ResamplerRom.RESAMPLER_DOWN_ORDER_FIR ];
        int[] buf_ptr;
        int buf_ptr_offset;
        short[] interpol_ptr, FIR_Coefs;
        int interpol_ptr_offset, FIR_Coefs_offset;

        /* Copy buffered samples to start of buffer */
//TODO: arrayCopy();
//        SKP_memcpy( buf2, S->sFIR, RESAMPLER_DOWN_ORDER_FIR * sizeof( SKP_int32 ) );
        for(int i_djinn=0; i_djinn<ResamplerRom.RESAMPLER_DOWN_ORDER_FIR; i_djinn++)
            buf2[i_djinn] = S.sFIR[i_djinn];

        FIR_Coefs = S.Coefs;
        FIR_Coefs_offset = 2;

        /* Iterate over blocks of frameSizeIn input samples */
        index_increment_Q16 = S.invRatio_Q16;
        while( true )
        {
            nSamplesIn = Math.min( inLen, S.batchSize );

            if( S.input2x == 1 )
            {
                /* Downsample 2x */
                ResamplerDown2.SKP_Silk_resampler_down2( S.sDown2,0, buf1,0, in,in_offset, nSamplesIn );

                nSamplesIn = nSamplesIn >> 1;

                /* Second-order AR filter (output in Q8) */
                ResamplerPrivateAR2.SKP_Silk_resampler_private_AR2( S.sIIR,0, buf2,ResamplerRom.RESAMPLER_DOWN_ORDER_FIR, buf1,0, S.Coefs,0, nSamplesIn );
            }
            else
            {
                /* Second-order AR filter (output in Q8) */
                ResamplerPrivateAR2.SKP_Silk_resampler_private_AR2( S.sIIR,0, buf2,ResamplerRom.RESAMPLER_DOWN_ORDER_FIR, in,in_offset, S.Coefs,0, nSamplesIn );
            }

            max_index_Q16 = nSamplesIn << 16;

            /* Interpolate filtered signal */
            if( S.FIR_Fracs == 1 )
            {
                for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 )
                {
                    /* Integer part gives pointer to buffered input */
                    buf_ptr = buf2;
                    buf_ptr_offset = index_Q16 >> 16;

                    /* Inner product */
                    res_Q6 = SKP_SMULWB(         buf_ptr[ buf_ptr_offset   ]+buf_ptr[ buf_ptr_offset+11 ], FIR_Coefs[ FIR_Coefs_offset   ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+1 ]+buf_ptr[ buf_ptr_offset+10 ], FIR_Coefs[ FIR_Coefs_offset+1 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+2 ]+buf_ptr[  buf_ptr_offset+9 ], FIR_Coefs[ FIR_Coefs_offset+2 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+3 ]+buf_ptr[  buf_ptr_offset+8 ], FIR_Coefs[ FIR_Coefs_offset+3 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+4 ]+buf_ptr[  buf_ptr_offset+7 ], FIR_Coefs[ FIR_Coefs_offset+4 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+5 ]+buf_ptr[  buf_ptr_offset+6 ], FIR_Coefs[ FIR_Coefs_offset+5 ] );

                    /* Scale down, saturate and store in output array */
                    out[out_offset++] = (short)SigProcFIX.SKP_SAT16( SigProcFIX.SKP_RSHIFT_ROUND( res_Q6, 6 ) );
                }
            }
            else
            {
                for( index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16 )
                {
                    /* Integer part gives pointer to buffered input */
                    buf_ptr = buf2;
                    buf_ptr_offset = index_Q16 >> 16;

                    /* Fractional part gives interpolation coefficients */
                    interpol_ind = SKP_SMULWB( index_Q16 & 0xFFFF, S.FIR_Fracs );

                    /* Inner product */
                    interpol_ptr = FIR_Coefs;
//BugFix                    interpol_ptr_offset = ResamplerRom.RESAMPLER_DOWN_ORDER_FIR / 2 * interpol_ind;
                    interpol_ptr_offset = FIR_Coefs_offset + ResamplerRom.RESAMPLER_DOWN_ORDER_FIR / 2 * interpol_ind;
                    res_Q6 = SKP_SMULWB(         buf_ptr[ buf_ptr_offset   ], interpol_ptr[ interpol_ptr_offset   ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+1 ], interpol_ptr[ interpol_ptr_offset+1 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+2 ], interpol_ptr[ interpol_ptr_offset+2 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+3 ], interpol_ptr[ interpol_ptr_offset+3 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+4 ], interpol_ptr[ interpol_ptr_offset+4 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+5 ], interpol_ptr[ interpol_ptr_offset+5 ] );
                    interpol_ptr = FIR_Coefs;
//BugFix                    interpol_ptr_offset = ResamplerRom.RESAMPLER_DOWN_ORDER_FIR / 2 * ( S.FIR_Fracs - 1 - interpol_ind );
                    interpol_ptr_offset = FIR_Coefs_offset + ResamplerRom.RESAMPLER_DOWN_ORDER_FIR / 2 * ( S.FIR_Fracs - 1 - interpol_ind );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+11 ], interpol_ptr[ interpol_ptr_offset   ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[ buf_ptr_offset+10 ], interpol_ptr[ interpol_ptr_offset+1 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[  buf_ptr_offset+9 ], interpol_ptr[ interpol_ptr_offset+2 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[  buf_ptr_offset+8 ], interpol_ptr[ interpol_ptr_offset+3 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[  buf_ptr_offset+7 ], interpol_ptr[ interpol_ptr_offset+4 ] );
                    res_Q6 = SKP_SMLAWB( res_Q6, buf_ptr[  buf_ptr_offset+6 ], interpol_ptr[ interpol_ptr_offset+5 ] );

                    /* Scale down, saturate and store in output array */
                    out[out_offset++] = (short)SigProcFIX.SKP_SAT16( SigProcFIX.SKP_RSHIFT_ROUND( res_Q6, 6 ) );
                }
            }

            in_offset += nSamplesIn << S.input2x;
            inLen -= nSamplesIn << S.input2x;

            if( inLen > S.input2x )
            {
                /* More iterations to do; copy last part of filtered signal to beginning of buffer */
//TODO: arrayCopy();
//                SKP_memcpy( buf2, &buf2[ nSamplesIn ], RESAMPLER_DOWN_ORDER_FIR * sizeof( SKP_int32 ) );
                for(int i_djinn=0; i_djinn<ResamplerRom.RESAMPLER_DOWN_ORDER_FIR; i_djinn++)
                    buf2[i_djinn] = buf2[nSamplesIn+i_djinn];
            }
            else
            {
                break;
            }
        }

        /* Copy last part of filtered signal to the state for the next call */
//TODO: arrayCopy();
//        SKP_memcpy( S->sFIR, &buf2[ nSamplesIn ], RESAMPLER_DOWN_ORDER_FIR * sizeof( SKP_int32 ) );
        for(int i_djinn=0; i_djinn<ResamplerRom.RESAMPLER_DOWN_ORDER_FIR; i_djinn++)
            S.sFIR[i_djinn] = buf2[nSamplesIn+i_djinn];
    }
}
