use std::f64::consts::PI;
use std::ops::Neg;

use crate::map;

/// Represents a digital, sampled frequency
/// as radians per samples in [0; 2*pi[ range
/// mapped to the whole [0; usize::MAX] range
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct DigitalFrequency(pub usize);

/// Represents an absolute phase offset
/// as radians in [0; 2*pi[ range
/// mapped to the whole [0; usize::MAX] range
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct Phase(pub DigitalFrequency);

impl DigitalFrequency
{
    /// Creates a DigitalFrequency from rads per samples
    pub fn from_rad(radians_per_sample: f64) -> Self
    {
        // Frequnecy wraps arround : Going at 2 pi radians per second
        // Is like not oscillating at all
        let f = radians_per_sample.rem_euclid(2. * PI);

        // Then we map the [0, 2*PI] range into the 0 to usize range
        DigitalFrequency(map(f, 0., 2. * PI, 0., usize::MAX as f64).floor() as usize)
    }

    /// Creates a DigitalFrequency from a frequency given in hertz (s^(-1))
    /// in the context of a sample rate also given in hertz
    pub fn from_time_frequency(hertz: f64, sample_rate: f64) -> Self
    {
        Self::from_rad(map(hertz, 0., sample_rate, 0., 2. * PI))
    }

    /// Gets the frequency as radians per sample
    pub fn as_rad(&self) -> f64
    {
        map(self.0 as f64, 0., usize::MAX as f64, 0., 2. * PI)
    }

    /// Gets the frequency as hertz in the context of a sample rate
    /// also given in hert
    pub fn as_time_frequency(&self, sample_rate: f64) -> f64
    {
        map(self.0 as f64, 0., usize::MAX as f64, 0., sample_rate)
    }
}

impl Neg for DigitalFrequency
{
    type Output = Self;

    /// Returns the "negative frequency"
    fn neg(self) -> Self::Output
    {
        DigitalFrequency(usize::MAX - self.0)
    }
}
