//! Random generation utilities

use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};

use crate::time::monotonic_time;

// use crate::device::monotonic_timestamp;

const BUFFER_SIZE: usize = 1024;

struct RandomState {
    data: [u8; BUFFER_SIZE],
    pos: usize,
    last_state: u32,
}

impl RandomState {
    fn new(seed: u32) -> Self {
        Self {
            data: [0; BUFFER_SIZE],
            pos: BUFFER_SIZE,
            last_state: seed,
        }
    }

    fn next(&mut self) -> u32 {
        let mut x = self.last_state;
        x ^= x << 13;
        x ^= x >> 17;
        x ^= x << 5;
        self.last_state = x;
        x
    }

    fn fill_buf(&mut self) {
        self.pos = 0;
        for i in (0..self.data.len()).step_by(4) {
            let v = self.next();
            self.data[i..i + 4].copy_from_slice(&v.to_ne_bytes());
        }
    }

    fn read_buf(&mut self, buf: &mut [u8]) {
        let mut rem = buf.len();
        let mut pos = 0;

        while rem != 0 {
            if self.pos == BUFFER_SIZE {
                self.fill_buf();
            }
            let count = core::cmp::min(rem, BUFFER_SIZE - self.pos);

            buf[pos..pos + count].copy_from_slice(&self.data[self.pos..self.pos + count]);

            self.pos += count;
            rem -= count;
            pos += count;
        }
    }
}

static RANDOM_STATE: OneTimeInit<IrqSafeSpinlock<RandomState>> = OneTimeInit::new();

/// Fills `buf` with random bytes
pub fn read_unsecure(buf: &mut [u8]) {
    let state = RANDOM_STATE.get();
    state.lock().read_buf(buf)
}

pub fn range(a: u64, b: u64) -> u64 {
    assert!(b > a);
    let mut bytes = [0; 8];
    read_unsecure(&mut bytes);
    let v = u64::from_ne_bytes(bytes) % (b - a);
    a + v
}

/// Initializes the random generator state
pub fn init() {
    let now = monotonic_time();
    let random_seed = now.subsec_nanos().wrapping_add(now.seconds());

    let mut state = RandomState::new(random_seed as u32);
    state.fill_buf();

    RANDOM_STATE.init(IrqSafeSpinlock::new(state));
}
