// ----------------------------------------------------------------------------
//
//  Copyright (C) 2018-2019 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/>.
//
// ----------------------------------------------------------------------------


#include <math.h>
#include <stdio.h>
#include <string.h>
#include "jlr4filt.h"
#include "delay.h"


Jlr4chan::Jlr4chan (void) :
    _nreq (0),
    _nack (0),
    _mode (0),
    _inp (0),
    _hpf (0),
    _hps (0),
    _lpf (0),
    _lps (0),
    _gain (1.0f),
    _delay (0)
{
}


Jlr4chan::~Jlr4chan (void)
{
}


Jlr4filt::Jlr4filt (const char *client_name, const char *server_name,
		    int ninp, int nout, float maxdel) :
    _chanpar (0),
    _maxdel (0)
{
    if (ninp < 1) ninp = 1;
    if (ninp > MAXINP) ninp = MAXINP;
    if (nout < 1) nout = 1;
    if (nout > MAXOUT) nout = MAXOUT;
    if (maxdel < 0.0f) maxdel = 0.0f;
    if (maxdel > 0.1f) maxdel = 0.1f;
    if (   open_jack (client_name, server_name, ninp, nout)
        || create_inp_ports ("in_%d")
        || create_out_ports ("out_%d"))
    {
	_state = FAILED;
	return;
    }
    init (maxdel);
    _state = PROCESS;
}


Jlr4filt::~Jlr4filt (void)
{
    _state = INITIAL;
    close_jack ();
    delete[] _chanpar;
}


void Jlr4filt::init (float maxdel)
{
    int       i;
    Jlr4chan  *C;

    _chanpar = new Jlr4chan [(unsigned int) _nout];
    _maxdel = (int)(maxdel * jack_rate () + 0.5f);
    if (_maxdel)
    {
	for (i = 0, C = _chanpar; i < _nout; i++, C++)
	{
	    C->_dproc.init (_maxdel, jack_size ());
	}
    }
}


void Jlr4filt::set_filter (int inp, int out, float hpf, float hps, float lpf, float lps)
{
    Jlr4chan *C;

    if ((inp < 0) || (inp >= _ninp)) return;
    if ((out < 0) || (out >= _nout)) return;
    C = _chanpar + out;
    C->_inp = inp;
    C->_hpf = hpf / jack_rate ();
    C->_hps = hps;
    C->_lpf = lpf / jack_rate ();
    C->_lps = lps;
    C->_nreq++;
}


void Jlr4filt::set_delay (int out, float delay)
{
    int32_t d;

    if ((out < 0) || (out >= _nout)) return;
    d = (int32_t)(delay * jack_rate () + 0.5f);
    if (d < 0)       d = 0;
    if (d > _maxdel) d = _maxdel;
    _chanpar [out]._delay = d;
}


void Jlr4filt::set_gain (int out, float gain)
{
    if ((out < 0) || (out >= _nout)) return;
    _chanpar [out]._gain = gain;
}


int Jlr4filt::jack_process (int nframes)
{
    Jlr4chan     *C;
    float        *inp [MAXINP];
    float        *out;
    const float  *p;
    int          i, j, m;
    
    if (_state < PROCESS) return 0;

    for (i = 0; i < _ninp; i++)
    {
        inp [i] = (float *) jack_port_get_buffer (_inp_ports [i], nframes);
    }

    for (i = 0, C = _chanpar; i < _nout; i++, C++)
    {
        out = (float *) jack_port_get_buffer (_out_ports [i], nframes);
	if (C->_nreq != C->_nack)
	{
	    C->_mode = 0;
	    if (C->_hpf > 0)
	    {
		C->_hpfilt.setpars (C->_hpf, C->_hps);
		C->_mode |= HPF;
	    }
	    if (C->_lpf > 0)
	    {
		C->_lpfilt.setpars (C->_lpf, C->_lps);
		C->_mode |= LPF;
	    }
	    C->_nack++;
	}
	m = C->_mode;
	if (m)
	{
	    p = inp [C->_inp];
	    if (C->_delay)
	    {
		C->_dproc.write (p);
		p = C->_dproc.readp (C->_delay);
	    }
	    if (m & HPF)
	    {
		C->_hpfilt.process_hipass (nframes, p, out);
		p = out;
	    }
	    if (m & LPF)
	    {
		C->_lpfilt.process_lopass (nframes, p, out);
	    }
	    if (C->_gain != 1.0f)
	    {
		for (j = 0; j < nframes; j++) out [j] *= C->_gain;
	    }
	}
	else memset (out, 0, nframes * sizeof (float));
    }

    return 0;
}

