#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-noise-cancellation -- Spectral Subtraction Noise Reducer
use std::f64::consts::PI;
#[derive(Debug)] pub enum NoiseError { InvalidInput(String) }
impl std::fmt::Display for NoiseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InvalidInput(e) => write!(f, "{e}") } } }
impl std::error::Error for NoiseError {}
pub fn dft(signal: &[f64]) -> Vec<(f64, f64)> {
    let n = signal.len();
    (0..n).map(|k| {
        let (mut re, mut im) = (0.0, 0.0);
        for (t, &x) in signal.iter().enumerate() { let angle = 2.0 * PI * k as f64 * t as f64 / n as f64; re += x * angle.cos(); im -= x * angle.sin(); }
        (re, im)
    }).collect()
}
pub fn idft(spectrum: &[(f64, f64)]) -> Vec<f64> {
    let n = spectrum.len();
    (0..n).map(|t| {
        let mut sum = 0.0;
        for (k, &(re, im)) in spectrum.iter().enumerate() { let angle = 2.0 * PI * k as f64 * t as f64 / n as f64; sum += re * angle.cos() - im * angle.sin(); }
        sum / n as f64
    }).collect()
}
pub struct NoiseProfile { pub magnitude: Vec<f64> }
impl NoiseProfile {
    pub fn estimate(noise_frames: &[Vec<f64>]) -> Result<Self, NoiseError> {
        if noise_frames.is_empty() { return Err(NoiseError::InvalidInput("Empty".into())); }
        let n = noise_frames[0].len();
        let mut avg_mag = vec![0.0; n];
        for frame in noise_frames {
            let spec = dft(frame);
            for (i, (re, im)) in spec.iter().enumerate() { avg_mag[i] += (re*re + im*im).sqrt(); }
        }
        for m in avg_mag.iter_mut() { *m /= noise_frames.len() as f64; }
        Ok(Self { magnitude: avg_mag })
    }
}
pub struct SpectralSubtractor { pub gain_floor: f64 }
impl SpectralSubtractor {
    pub fn new(gain_floor: f64) -> Self { Self { gain_floor } }
    pub fn process(&self, signal: &[f64], noise: &NoiseProfile) -> Vec<f64> {
        let spec = dft(signal);
        let cleaned: Vec<(f64, f64)> = spec.iter().enumerate().map(|(i, &(re, im))| {
            let mag = (re*re + im*im).sqrt();
            let noise_mag = if i < noise.magnitude.len() { noise.magnitude[i] } else { 0.0 };
            let gain = if mag > noise_mag { (mag - noise_mag) / mag } else { self.gain_floor };
            (re * gain, im * gain)
        }).collect();
        idft(&cleaned)
    }
}
#[cfg(test)] mod tests {
    use super::*;
    #[test] fn test_dft_idft() { let sig = vec![1.0, 0.0, -1.0, 0.0]; let spec = dft(&sig); let rec = idft(&spec); for (a, b) in sig.iter().zip(rec.iter()) { assert!((a - b).abs() < 1e-10); } }
    #[test] fn test_noise_profile() { let frames = vec![vec![0.1, -0.1, 0.05, -0.05]]; let p = NoiseProfile::estimate(&frames).unwrap(); assert_eq!(p.magnitude.len(), 4); }
}
