// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use rand::SeedableRng;
use rand_chacha::ChaCha20Rng;

use crate::utils::Float;

pub trait Sampler {
	fn next(&mut self) -> f32;
}

pub struct WhiteNoise(ChaCha20Rng);

impl WhiteNoise {
	pub fn new(seed: u64) -> WhiteNoise {
		WhiteNoise(ChaCha20Rng::seed_from_u64(seed))
	}
}

impl Sampler for WhiteNoise {
	fn next(&mut self) -> f32 {
		self.0.next_f32() * 2.0 - 1.0
	}
}

pub struct BrownianNoise {
	noise: WhiteNoise,
	x: f32,
	mul: f32,
}

impl BrownianNoise {
	/**
	 * Creates a `BrownianNoise`.
	 *
	 * Invariant: |mul| ≤ 2.
	 */
	pub fn new(mul: f32, seed: u64) -> BrownianNoise {
		assert!(mul.abs() <= 2.0);
		let mut noise = WhiteNoise::new(seed);
		let x = noise.next();
		BrownianNoise { noise, x, mul }
	}
}

impl Sampler for BrownianNoise {
	fn next(&mut self) -> f32 {
		self.x += self.noise.next() * self.mul;
		if self.x > 1.0 {
			self.x = 2.0 - self.x;
		} else if self.x < -1.0 {
			self.x = -2.0 - self.x;
		}
		self.x
	}
}
