// ----------------------------------------------------------------------------
//
//  Copyright (C) 2012-2021 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 __AMBROT8_H
#define __AMBROT8_H


#include <string.h>
#include "posixthr.h"


class RotMatrix
{
private:

    friend class Ambrot8;
    
    RotMatrix (int degree):
        _degr (degree)
    {
        _size = 2 * _degr + 1;
        _data = new float [_size * _size];
        clear ();
    }

    ~RotMatrix (void)
    {
        delete[] _data;
    }

    float get (int r, int c)
    {
        return _data [_size * (r + _degr) + c + _degr];
    }

    void set (int r, int c, float v)
    {
        _data [_size * (r + _degr) + c + _degr] = v;
    }    
        
    void clear (void)
    {
        memset (_data, 0, _size * _size * sizeof (float));
        for (int i = 0; i < _size; i++) _data [i * (_size + 1)] = 1.0f;
    }

    void copy (RotMatrix *M)
    {
        memcpy (_data, M->_data, _size * _size * sizeof (float));
    }

    int     _degr;
    int     _size;
    float  *_data;
};


class RotMagics
{
    friend class Ambrot8;

    RotMagics (int degree);
    ~RotMagics (void);

    float R (int i) { return _R [i]; }
    float U (int i) { return _U [i]; }
    float V (int i) { return _V [i]; }
    float W (int i) { return _W [i]; }

    float *_R;
    float *_U;
    float *_V;
    float *_W;
};

    
class Ambrot8
{
public:

    enum { MAXDEGR = 8, MAXHARM = 81 };

    Ambrot8 (int fsamp, int degree);
    virtual ~Ambrot8 (void);

    void set_rotation (float a, float x, float y, float z, float dt);
    void set_quaternion (float w, float x, float y, float z, float dt);
    void process (int nframes, float *inp[], float *out[]);
    
private:

    void process0 (float *inp[], float *out[], int k0, int nf);
    void process1 (float *inp[], float *out[], int k0, int nf, int ni);
    void update (void);
    void newmatrix1 (void);
    void newmatrixd (int d);
    float funcV (int d, int m, int n);
    float funcW (int d, int m, int n);
    float funcP (int d, int m, int n, int i);

#ifdef TEST
    void printmatrix (int d);
#endif    
    
    int              _fsamp;
    int              _degree;
    RotMatrix       *_M [MAXDEGR + 1];  // Target matrices.
    RotMatrix       *_C [MAXDEGR + 1];  // Current matrices.
    RotMagics       *_Q [MAXDEGR + 1];  // Magic constants.
    float            _w, _x, _y, _z, _t;  // Target quaternion and time.
    P_mutex          _mutex;   // Protects current matrices. 
    volatile int     _touch0;  // Update request counter.
    volatile int     _touch1;  // Update done counter.
    int              _nipol;   // Number of frames to interpolate.
};


#endif
