/*
  Finite impulse response generator and instance classes
*/

#ifndef VALIB_FIR_H
#define VALIB_FIR_H

#include "defs.h"

enum fir_t { firt_zero, firt_identity, firt_gain, firt_custom };

class FIRGen;
class FIRInstance;

class FIRZero;
class FIRIdentity;
class FIRGain;

///////////////////////////////////////////////////////////////////////////////
// FIRGen - Finite impulse response function generator
//
// Interface for impulse response generator. It is used to generate an impulse
// response from a set of user-controllable parameters (if any) and a given
// sample rate.
//
// So FIRGen descendant may act as parameters container and these parameters
// may change, resulting in change of the impulse response. For class clients
// to notice these changes version is used. When version changes this means
// that we have to regenerate the response.
//
// Sample rate may change during normal data flow and therefore we need to
// regenerate the response for a new sample rate. Sample rate change does not
// change the version because it is not a contained parameter, but an external
// one.
//
// Depending on user parameters impulse response may degenerate into identity,
// gain or even zero response. Obviously, these cases require less computation
// and may be implemented more effective.
//
// version()
//   Returns the impulse response version. If the response function changes,
//   the version must also change so users of the generator can be notified
//   about this change and rebuild the response. Constant responses like zero
//   or identity never change the version (may return zero or any other const).
//
// make()
//   Builds response function instance for the sample rate given.
//
///////////////////////////////////////////////////////////////////////////////

class FIRGen
{
public:
  FIRGen() {}
  virtual ~FIRGen() {}

  virtual int version() const = 0;
  virtual const FIRInstance *make(int sample_rate) const = 0;
};

///////////////////////////////////////////////////////////////////////////////
// FIRInstance - Impulse response function instance.
// Simple container for instance data.
//
// StaticFIRInstance
//   Container for statically allocated instance data, so it does not delete
//   data on instance destruction.
//
// DynamicFIRInstance
//   Container for dynamically allocated instance data. Destroys instance data
//   on instance destruction.
//
///////////////////////////////////////////////////////////////////////////////

class FIRInstance
{
protected:
  FIRInstance(int sample_rate_, fir_t type_, int length_, int center_, const double *data_ = 0):
  sample_rate(sample_rate_), type(type_), length(length_), center(center_), data(data_) {}

public:
  int sample_rate;
  fir_t type;
  int length;
  int center;
  const double *data;

  virtual ~FIRInstance() {}
};

class StaticFIRInstance : public FIRInstance
{
public:
  StaticFIRInstance(int sample_rate_, fir_t type_, int length_, int center_, const double *data_ = 0):
  FIRInstance(sample_rate_, type_, length_, center_, data_) {}
};

class DynamicFIRInstance : public FIRInstance
{
protected:
  double *buf;

public:
  DynamicFIRInstance(int sample_rate_, fir_t type_, int length_, int center_, double *data_ = 0):
  FIRInstance(sample_rate_, type_, length_, center_, data_), buf(data_) {}
  virtual ~DynamicFIRInstance() { safe_delete(buf); }
};

///////////////////////////////////////////////////////////////////////////////
// General FIR instance classes
//
// Several response types may be generated by any generator. And because of its
// importance this types are made as special classes.
///////////////////////////////////////////////////////////////////////////////

class ZeroFIRInstance : public FIRInstance
{ 
public:
  ZeroFIRInstance(int sample_rate);
};

class IdentityFIRInstance : public FIRInstance
{
public:
  IdentityFIRInstance(int sample_rate);
};

class GainFIRInstance : public FIRInstance
{
public:
  double gain;
  GainFIRInstance(int sample_rate, double gain);
};

///////////////////////////////////////////////////////////////////////////////
// General FIR generators
///////////////////////////////////////////////////////////////////////////////

class FIRZero : public FIRGen
{
public:
  FIRZero() {}
  virtual const FIRInstance *make(int sample_rate) const;
  virtual int version() const { return 0; }
};

class FIRIdentity : public FIRGen
{
public:
  FIRIdentity() {}
  virtual const FIRInstance *make(int sample_rate) const;
  virtual int version() const { return 0; }
};

class FIRGain : public FIRGen
{
protected:
  int ver;
  double gain;

public:
  FIRGain();
  FIRGain(double gain);

  virtual const FIRInstance *make(int sample_rate) const;
  virtual int version() const { return ver; }

  void set_gain(double gain);
  double get_gain() const;
};

///////////////////////////////////////////////////////////////////////////////
// Constant generators
//
// These generators do no have any parameters and never change. So we can
// make it global and use everywhere.
///////////////////////////////////////////////////////////////////////////////

extern FIRZero fir_zero;
extern FIRIdentity fir_identity;

///////////////////////////////////////////////////////////////////////////////
// Generator reference.
//
// Sometimes it is convinient to treat change of FIR generator like generator's
// version change, so we can handle this change in a general way.
///////////////////////////////////////////////////////////////////////////////

class FIRRef : public FIRGen
{
protected:
  mutable int ver;
  mutable int fir_ver;
  const FIRGen *fir;

public:
  FIRRef(): ver(0), fir_ver(0), fir(0)
  {};

  FIRRef(const FIRGen *fir_): ver(0), fir_ver(0), fir(fir_)
  {
    if (fir_)
      fir_ver = fir->version();
  };

  FIRRef(const FIRRef &ref): ver(0), fir_ver(0), fir(0)
  {
    fir = ref.fir;
    if (fir)
      fir_ver = fir->version();
  }

  FIRRef &operator =(const FIRRef &ref)
  {
    if (fir != ref.fir)
    {
      fir = ref.fir;
      fir_ver = fir? fir->version(): 0;
      ver++;
    }
    return *this;
  }

  /////////////////////////////////////////////////////////
  // Handle generator changes

  void set(const FIRGen *fir_)
  {
    if (fir == fir_) return;

    fir = fir_;
    fir_ver = fir? fir->version(): 0;
    ver++;
  }

  const FIRGen *get() const
  {
    return fir;
  }

  void release()
  {
    fir = 0;
    ver++;
  }

  /////////////////////////////////////////////////////////
  // FIRGen interface

  virtual int version() const
  {
    if (fir)
      if (fir_ver != fir->version())
        ver++, fir_ver = fir->version();
    return ver; 
  }

  virtual const FIRInstance *make(int sample_rate) const
  {
    return fir? fir->make(sample_rate): 0;
  }
};

#endif
