#pragma once

#include "buffer.h"
#include "delayline.h"
#include <vector>

struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState;

class Limiter {
  constexpr static int upsampleFactor = 2;
  constexpr static int resampleQuality = 7;
  constexpr static int workBufferSize = 4096;
  constexpr static int chunkSize = workBufferSize / upsampleFactor;

  const int sampleRate;
  const int channelCount;
  float attackSeconds;
  float releaseSeconds;
  float attackAlpha;
  float releaseAlpha;
  int lookaheadFrames;
  int delayFrames;

  // adjustable parameters
  float desiredAttackSeconds;
  float desiredReleaseSeconds;
  float desiredLookaheadSeconds;
  float preGainDb;
  bool bypass;
  float stereoLink;

  SpeexResamplerState *upsampler, *downsampler;

  BufferF32 workBuffer;
  std::unique_ptr<DelayLine> lookaheadDelayLine;
  std::vector<float> channelCurrentGainEnvelope;
  float linkedCurrentGainEnvelope;

  BufferF32 limiterWorkBuffer;
  std::vector<int> deque;
  BufferF32 slidingWindowWorkBuffer;

  void updateParameters();

public:
  Limiter(int channelCount, int sampleRate, float lookaheadSeconds,
          float attackSeconds, float releaseSeconds, float preGainDb,
          bool bypass, float stereoLink);
  ~Limiter();

  void setParameters(float lookaheadSeconds, float attackSeconds,
                     float releaseSeconds, float preGainDb, bool bypass,
                     float stereoLink);

  void process(BufferF32 buf);
  int getDelayFrames() const { return delayFrames; }
};