//! Seeded piece-sequence generation.

pub const PIECE_TYPES: &str = "ZLOSIJT";

const MOD: i64 = 2147483647;
const MULT: i64 = 16807;

/// MINSTD Lehmer random number generator.
pub struct SeededRng {
    t: i64,
}

impl SeededRng {
    pub fn new(seed: i64) -> Self {
        let mut t = seed % MOD;
        if t <= 0 {
            t += MOD - 1;
        }
        SeededRng { t }
    }

    pub fn next(&mut self) -> i64 {
        self.t = (MULT * self.t) % MOD;
        self.t
    }

    pub fn next_float(&mut self) -> f64 {
        (self.next() - 1) as f64 / (MOD - 1) as f64
    }

    pub fn next_int(&mut self, n: usize) -> usize {
        (self.next_float() * n as f64) as usize
    }
}

fn shuffle_bag(rng: &mut SeededRng) -> Vec<char> {
    let mut bag: Vec<char> = PIECE_TYPES.chars().collect();
    let mut i = bag.len() - 1;
    while i > 0 {
        let j = rng.next_int(i + 1);
        bag.swap(i, j);
        i -= 1;
    }
    bag
}

pub fn generate_sequence(seed: i64, n_pieces: usize, no_szo: bool) -> Vec<char> {
    let mut rng = SeededRng::new(seed);
    let mut pieces: Vec<char> = Vec::new();
    while pieces.len() < n_pieces + 6 {
        pieces.extend(shuffle_bag(&mut rng));
    }
    if no_szo {
        let first_bag_len = pieces.len().min(7);
        for _ in 0..first_bag_len {
            if !matches!(pieces[0], 'S' | 'Z' | 'O') {
                break;
            }
            let head = pieces.remove(0);
            pieces.insert(first_bag_len - 1, head);
        }
    }
    pieces.truncate(n_pieces);
    pieces
}
