#pragma once
#include "buffer.h"
#include "fft/pffft.h"

constexpr static int METER_MAX_CHANNELS = 2;

class FFTObserver {
  const int stride;
  uint32_t enabled;
  BufferF32 inputQueue;
  BufferF32 windowFunction;
  int inputQueueWriteIndex;
  float decayAlpha;

  void doFrame();

  PFFFT_Setup *setup;

public:
  FFTObserver(int channelCount, int binCount, int stride, float decayAlpha);
  ~FFTObserver();

  void update(const BufferF32 &buf);

  bool getEnabled() const;
  void setEnabled(bool enabled);

  float getDecayAlpha() const;
  void setDecayAlpha(float decayAlpha);

  const std::shared_ptr<BufferF32> buffer;
};

class Meter {
  const int channelCount;
  const int sampleRate;
  const float ppmHoldUpdateHz;
  const int ppmHoldKeepPeriods;
  const float vuAlpha;

  BufferF32 ppmHoldValues;

  int ppmHoldValuesWriteIndex;
  BufferF32 ppmHoldBuffer;
  BufferF32 ppmHoldBufferRemaining;

  float lastVuValue[METER_MAX_CHANNELS];

  float ppmHoldIndicated[METER_MAX_CHANNELS];
  float ppmIndicated[METER_MAX_CHANNELS];
  float vuIndicated[METER_MAX_CHANNELS];

  FFTObserver fftObserver;

public:
  Meter(int channelCount, int sampleRate, float ppmHoldUpdateHz,
        int ppmHoldKeepPeriods, float vuAlpha);
  void update(const BufferF32 &buf);

  float getPpmHoldIndicated(int channel) const {
    assert(channel >= 0 && channel < channelCount);
    return ppmHoldIndicated[channel];
  }
  float getPpmIndicated(int channel) const {
    assert(channel >= 0 && channel < channelCount);
    return ppmIndicated[channel];
  }
  float getVuIndicated(int channel) const {
    assert(channel >= 0 && channel < channelCount);
    return vuIndicated[channel];
  }

  std::shared_ptr<BufferF32> getFFTObserverBuffer() const;

  bool getFFTObserverEnabled() const;
  void setFFTObserverEnabled(bool enabled);

  float getFFTObserverDecayAlpha() const;
  void setFFTObserverDecayAlpha(float decayAlpha);
};