// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::ops::{Add, Sub, Mul, Div};

use glam::Vec2;

/**
 * Methods for exponentially decaying one value towards another value, similar
 * to lerp smoothing but done correctly (see https://youtu.be/LSNQuFEDOyQ for
 * more information).
 *
 * Note that these methods are **unstable**, so these results shouldn't be used
 * in any results that must be deterministic.
 */
pub trait Decay:
	Sized + Copy +
	Add<Self, Output = Self> +
	Sub<Self, Output = Self> +
	Mul<f32, Output = Self> +
	Div<f32, Output = Self>
{
	/**
	 * Decays towards zero.
	 */
	fn decay(&mut self, rate: f32, dt: f32) {
		*self = *self * (-rate * dt).exp();
	}

	/**
	 * Decays towards a specific value.
	 */
	fn decay_to(&mut self, target_pos: Self, rate: f32, dt: f32) {
		*self = target_pos - (target_pos - *self) * (-rate * dt).exp();
	}

	/*
	 * Decays towards a specific value that's moving.
	 *
	 * This method provides a similar result to `decay_to` but it's a better
	 * approximation.
	 *
	 * This is the solution to the differential equation
	 * 	dx = -k(x - T) dt
	 * which is:
	 * 	x = T_0 + v(t - 1 / k) + (x_0 - T_0 + v / k)e^(-kt)
	 * with t = dt.
	 */
	fn decay_to_moving(&mut self, target_pos: Self, target_vel: Self, rate: f32, dt: f32) {
		*self = target_pos + target_vel * (dt - 1.0 / rate) + (*self - target_pos + target_vel / rate) * (-rate * dt).exp();
	}
}

impl Decay for f32 {}
impl Decay for Vec2 {}
