Documentation
extern crate std;

use core::cell::RefCell;
use crate::{unix, Random, RandomSource};

thread_local! {
    static GLOBAL_RAND: RefCell<Box<dyn RandomSource>> =
        RefCell::new(Box::new(unix::RandomDevice::new().unwrap()));
}

/// Gets a random `T` element using the global [RandomSource]
///
/// # Example
/// ```
/// let n = rnd::random::<u32>();
/// println!("{n}");
/// ```
pub fn random<T: Random>() -> T {
    GLOBAL_RAND.with(|r| {
        let mut bor = r.borrow_mut();
        let source = bor.as_mut();
        T::random(source)
    })
}

/// Sets the global [RandomSource] for this thread
///
/// # Example
/// ```no_run
/// pub struct MyRandomSource {
///     // ...
/// }
///
/// impl rnd::RandomSource for MyRandomSource {
///     fn fill_bytes(&mut self, bytes: &mut [u8]) {
///         // ... do some stuff
///     }
/// }
///
/// rnd::set_global_random(MyRandomSource {});
/// for _ in 0..10 {
///     println!("{}", rnd::random::<u32>());
/// }
/// ```
pub fn set_global_random(dev: impl RandomSource + 'static) {
    GLOBAL_RAND.with(|r| {
        *r.borrow_mut() = Box::new(dev)
    })
}

pub struct GlobalRandomSource;

impl RandomSource for GlobalRandomSource {
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        GLOBAL_RAND.with(|rs| {
            rs.borrow_mut().fill_bytes(bytes);
        })
    }
}