// ----------------------------------------------------------------------------
//
//  Copyright (C) 2003-2013 Fons Adriaensen <fons@linuxaudio.org>
//    
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------


#ifndef __CONVOLVE_H
#define __CONVOLVE_H


//#define USE_SSE_AND_3DN


#include <fftw3.h>


enum { CONV_OPT_SSE = 1, CONV_OPT_3DN = 2 };


class Convdata
{
public:

    Convdata (size_t part, size_t lmax, unsigned int opts);
    ~Convdata (void);

    size_t       part (void) const { return _part; }
    unsigned int npar (void) const { return _npar; }
    unsigned int opts (void) const { return _opts; }

    void prepare_part (unsigned int ipar, float gain, float *data, int step = 1);
    void prepare_done (void);

    int get_refc (void) const { return _refc; }
    int inc_refc (void) { return ++_refc; }
    int dec_refc (void) { return --_refc; }

private:

    static void swap (fftwf_complex *p, size_t n);

    friend class Convolver;

    int                _refc;      // reference counter
    size_t             _part;      // partition size in frames
    unsigned int       _opts;      // optimization flags
    unsigned int       _npar;      // number of partitions
    unsigned int       _nact;      // number of active partitions
    float              _norm;      // gain normalization
    float             *_buff;      // input buffer
    fftwf_complex    **_fftd;      // transformed partitions
    fftwf_plan         _plan;      // fftw plan
};



class Convolver
{
public:

    Convolver (size_t part, size_t size, unsigned int opts, unsigned int nip, unsigned int nop);
    ~Convolver (void);

    float *wr_ptr (unsigned int i) const { return _ip_buff +     i * _part; }
    float *rd_ptr (unsigned int i) const { return _op_buff + 2 * i * _part; }

    int       set_conv (unsigned int ip, unsigned int op, Convdata *C);
    Convdata *get_conv (unsigned int ip, unsigned int op) const { return _conv [ip + _nip * op]; }

    void reset (void);
    void process (void);

    size_t       part (void) const { return _part; }
    unsigned int npar (void) const { return _npar; }
    unsigned int opts (void) const { return _opts; }
    unsigned int nip  (void) const { return _nip; }
    unsigned int nop  (void) const { return _nop; }

private:

    // configuration
    //
    size_t             _part;      // partition size in frames
    unsigned int       _opts;      // optimization flags
    unsigned int       _npar;      // number of partitions
    unsigned int       _nip;       // number of inputs
    unsigned int       _nop;       // number of outputs
    Convdata         **_conv;      // array of Convdata pointers
    fftwf_plan         _fwd_plan;  // fftw plans
    fftwf_plan         _rev_plan;  //

    // data buffers
    //
    float             *_ip_buff;   // input buffer
    float             *_op_buff;   // output buffer
    float             *_oA_buff;   // alternating output buffers
    float             *_oB_buff;   //
    fftwf_complex    **_fwd_data;  // circular array of input parts  
    fftwf_complex     *_mac_data;  // multiplied data accumulator  

    // interface 
    //
    float             *_wr_buff;   // input pointer for input 0
    float             *_rd_buff;   // output pointer for output 0

    // processing
    //
    unsigned int       _iter;
    unsigned int       _offs;
};


#endif

