/*
 * Compressor.h
 *
 * Copyright (c) 2020 Lost Robot <r94231@gmail.com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 2 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 (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */


#ifndef COMPRESSOR_H
#define COMPRESSOR_H

#include "CompressorControls.h"

#include "Effect.h"
#include "ValueBuffer.h"
#include "RmsHelper.h"


constexpr float COMP_LOG = -2.2;

class CompressorEffect : public Effect
{
	Q_OBJECT
public:
	CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
	~CompressorEffect() override;
	bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override;

	EffectControls* controls() override
	{
		return &m_compressorControls;
	}

private slots:
	void calcAutoMakeup();
	void calcAttack();
	void calcRelease();
	void calcAutoAttack();
	void calcAutoRelease();
	void calcHold();
	void calcOutGain();
	void calcRatio();
	void calcRange();
	void resizeRMS();
	void calcLookaheadLength();
	void calcThreshold();
	void calcKnee();
	void calcInGain();
	void calcTiltCoeffs();
	void calcMix();
	void changeSampleRate();
	void redrawKnee();

private:
	CompressorControls m_compressorControls;

	float msToCoeff(float ms);

	inline void calcTiltFilter(sample_t inputSample, sample_t &outputSample, int filtNum);
	inline int realmod(int k, int n);
	inline float realfmod(float k, float n);

	enum StereoLinkModes { Unlinked, Maximum, Average, Minimum, Blend };

	std::vector<float> m_preLookaheadBuf[2];
	int m_preLookaheadBufLoc[2] = {0};

	std::vector<float> m_lookaheadBuf[2];
	int m_lookaheadBufLoc[2] = {0};

	std::vector<float> m_inputBuf[2];
	int m_inputBufLoc = 0;

	float m_attCoeff;
	float m_relCoeff;
	float m_autoAttVal;
	float m_autoRelVal;

	int m_holdLength = 0;
	int m_holdTimer[2] = {0, 0};

	int m_lookaheadLength;
	int m_lookaheadDelayLength;
	int m_preLookaheadLength;
	float m_thresholdAmpVal;
	float m_autoMakeupVal;
	float m_outGainVal;
	float m_inGainVal;
	float m_rangeVal;
	float m_tiltVal;
	float m_mixVal;

	float m_coeffPrecalc;

	sampleFrame m_maxLookaheadVal;

	int m_maxLookaheadTimer[2] = {1, 1};
	
	float m_rmsTimeConst;
	float m_rmsVal[2] = {0, 0};

	float m_crestPeakVal[2] = {0, 0};
	float m_crestRmsVal[2] = {0, 0};
	float m_crestFactorVal[2] = {0, 0};
	float m_crestTimeConst;

	float m_tiltOut[2] = {0};

	bool m_cleanedBuffers = false;

	float m_sampleRate;

	float m_lgain;
	float m_hgain;
	float m_a0;
	float m_b1;

	float m_prevOut[2] = {0};

	float m_yL[2];
	float m_gainResult[2];
	float m_displayPeak[2];
	float m_displayGain[2];

	float m_kneeVal;
	float m_thresholdVal;
	float m_ratioVal;

	bool m_redrawKnee = true;
	bool m_redrawThreshold = true;

	friend class CompressorControls;
	friend class CompressorControlDialog;
} ;

#endif
