//***********************************************************************************************
//Mixer module for VCV Rack by Steve Baker and Marc Boulé 
//
//Based on code from the Fundamental plugin by Andrew Belt 
//See ./LICENSE.md for all licenses
//***********************************************************************************************


#include "MixerCommon.hpp"


//*****************************************************************************


// Math

// none


// Utility

float updateFadeGain(float fadeGain, float target, float *fadeGainX, float *fadeGainXr, float timeStepX, float shape, bool symmetricalFade) {
	// shape is 1.0f when exp, 0.0f when lin, -1.0f when log
	// target is 0.0f or 1.0f
	// fadeGainX moves from 0.0f to 1.0f gradually and linearly
	// fadeGainXr is a resettable and relative gainX, which is used for non-symmetrical fades (to remember position when change direction while fade is happening
	static const float A = 4.0f;
	static const float E_A_M1 = std::expm1(A);// e^A - 1
	
	float newFadeGain;

	if (target < *fadeGainX) {
		*fadeGainX -= timeStepX;
		if (*fadeGainX < target) {
			*fadeGainX = target;
		}
	}
	else if (target > *fadeGainX) {
		*fadeGainX += timeStepX;
		if (*fadeGainX > target) {
			*fadeGainX = target;
		}
	}
	*fadeGainXr += timeStepX;
	
	if (symmetricalFade) {
		newFadeGain = *fadeGainX;// linear
		if (*fadeGainX != target) {
			if (shape > 0.0f) {	
				float expY = std::expm1(A * *fadeGainX)/E_A_M1;
				newFadeGain = crossfade(newFadeGain, expY, shape);
			}
			else if (shape < 0.0f) {
				float logY = std::log1p(*fadeGainX * E_A_M1) / A;
				newFadeGain = crossfade(newFadeGain, logY, -1.0f * shape);		
			}
		}
	}
	else {// asymmetrical fade
		float fadeGainDelta = timeStepX;// linear
		
		if (shape > 0.0f) {	
			float fadeGainDeltaExp = (std::exp(A * (*fadeGainXr)) - std::exp(A * (*fadeGainXr - timeStepX))) / E_A_M1;
			fadeGainDelta = crossfade(fadeGainDelta, fadeGainDeltaExp, shape);
		}
		else if (shape < 0.0f) {
			float fadeGainDeltaLog = (std::log1p((*fadeGainXr) * E_A_M1) - std::log1p((*fadeGainXr - timeStepX) * E_A_M1)) / A;
			fadeGainDelta = crossfade(fadeGainDelta, fadeGainDeltaLog, -1.0f * shape);		
		}
		
		newFadeGain = fadeGain;
		if (target > fadeGain) {
			newFadeGain += fadeGainDelta;
		}
		else if (target < fadeGain) {
			newFadeGain -= fadeGainDelta;
		}	

		if (target > fadeGain && target < newFadeGain) {
			newFadeGain = target;
		}
		else if (target < fadeGain && target > newFadeGain) {
			newFadeGain = target;
		}
	}
	
	return newFadeGain;
}



