use std::time::{SystemTime, UNIX_EPOCH};

pub struct Slump {
    state: u64,
}

impl Slump {
    pub fn new(seed: u64) -> Self {
        Self { state: seed }
    }

    pub fn from_time() -> Self {
        let seed = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos() as u64;

        Self::new(seed)
    }

    pub fn next_u64(&mut self) -> u64 {
        self.state = self.state.wrapping_add(0x9E3779B97F4A7C15);

        let mut z = self.state;

        z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
        z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);

        z ^ (z >> 31)
    }

    pub fn next_u32(&mut self) -> u32 {
        self.next_u64() as u32
    }

    pub fn next_f32(&mut self) -> f32 {
        const SCALE: f32 = 1.0 / (u32::MAX as f32 + 1.0);
        (self.next_u32() as f32) * SCALE
    }

    pub fn gen_range(&mut self, upper: u32) -> u32 {
        self.next_u32() % upper
    }

    pub fn shuffle<T>(&mut self, slice: &mut [T]) {
        let len = slice.len();

        for i in (1..len).rev() {
            let j = self.gen_range((i + 1) as u32) as usize;
            slice.swap(i, j);
        }
    }
}

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

    #[test]
    fn test() {
        let mut rng = Slump::from_time();

        println!("u32: {}", rng.next_u32());
        println!("f32: {}", rng.next_f32());
        println!("r: {}", rng.gen_range(10));

        let mut items = [1, 2, 3, 4, 5];
        rng.shuffle(&mut items);

        println!("sh: {:?}", items);
    }
}
