//! Analysis windows for the STFT.
//!
//! A raw rectangular frame leaks spectral energy across bins (the abrupt edges
//! look like discontinuities to the FFT). A Hann window tapers each frame to
//! zero at its edges, trading a little main-lobe width for much lower side-lobe
//! leakage — the standard choice for a spectrogram.

use std::f64::consts::PI;

/// Returns a length-`n` periodic Hann window, `w[i] = 0.5·(1 - cos(2πi/n))`.
///
/// The *periodic* form (denominator `n`, not `n-1`) is used because STFT frames
/// tile the signal; it gives perfect overlap-add reconstruction at 50% hop.
pub fn hann(n: usize) -> Vec<f32> {
    if n <= 1 {
        return vec![1.0; n];
    }
    (0..n)
        .map(|i| (0.5 - 0.5 * (2.0 * PI * i as f64 / n as f64).cos()) as f32)
        .collect()
}

/// Applies `window` to `frame` element-wise into a new buffer. Lengths must match.
pub fn apply(frame: &[f32], window: &[f32]) -> Vec<f32> {
    debug_assert_eq!(frame.len(), window.len());
    frame.iter().zip(window).map(|(&s, &w)| s * w).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn hann_endpoints_are_zero_and_center_is_one() {
        let w = hann(8);
        assert!(w[0].abs() < 1e-6, "first sample tapers to ~0, got {}", w[0]);
        // Periodic Hann peaks at the midpoint n/2.
        assert!((w[4] - 1.0).abs() < 1e-6, "center peaks at 1, got {}", w[4]);
    }

    #[test]
    fn hann_is_symmetric_about_center() {
        let w = hann(16);
        // Periodic window is symmetric across indices 1..n-1.
        for i in 1..8 {
            assert!((w[i] - w[16 - i]).abs() < 1e-6);
        }
    }

    #[test]
    fn apply_scales_samples() {
        let out = apply(&[2.0, 2.0], &[0.5, 0.25]);
        assert_eq!(out, vec![1.0, 0.5]);
    }
}
