use crate::prelude::*;
use std::collections::HashSet;
use std::time::{SystemTime, UNIX_EPOCH};

impl Runtime {
    fn generated_name_is_available_with_reserved(
        &self,
        candidate_name: &str,
        reserved_names: &HashSet<String>,
    ) -> bool {
        if is_valid_litex_name(candidate_name).is_err() {
            return false;
        }
        if reserved_names.contains(candidate_name) {
            return false;
        }
        if self.is_name_used(candidate_name) {
            return false;
        }
        for names_in_scope in self.parsing_time_name_scope_stack.iter() {
            if names_in_scope.contains_key(candidate_name) {
                return false;
            }
        }
        true
    }

    pub(crate) fn generate_one_unused_name_with_reserved(
        &self,
        reserved_names: &HashSet<String>,
    ) -> String {
        for i in 1..=4096usize {
            let candidate_name = format!("x{}", i);
            if self.generated_name_is_available_with_reserved(&candidate_name, reserved_names) {
                return candidate_name;
            }
        }

        let available_chars: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
        let first_char_candidates: Vec<char> = "abcdefghijklmnopqrstuvwxyz".chars().collect();
        let mut try_index: usize = 0;
        loop {
            let current_time_nanos: u128 = match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(current_duration) => current_duration.as_nanos(),
                Err(_) => 0,
            };
            let mixed_seed_value: u128 =
                current_time_nanos ^ ((try_index as u128 + 1) * 0x9e3779b97f4a7c15u128);
            let generated_name_length: usize = 8 + (mixed_seed_value as usize % 17);

            let mut generated_chars: Vec<char> = Vec::new();
            let first_char_index = ((mixed_seed_value >> 1) as usize) % first_char_candidates.len();
            generated_chars.push(first_char_candidates[first_char_index]);

            let mut current_state_value: u128 = mixed_seed_value;
            for character_index in 1..generated_name_length {
                current_state_value = current_state_value
                    .wrapping_mul(6364136223846793005u128)
                    .wrapping_add(1442695040888963407u128 + character_index as u128);
                let available_char_index = (current_state_value as usize) % available_chars.len();
                generated_chars.push(available_chars[available_char_index]);
            }

            let candidate_name: String = generated_chars.into_iter().collect();
            if !self.generated_name_is_available_with_reserved(&candidate_name, reserved_names) {
                try_index += 1;
                continue;
            }
            return candidate_name;
        }
    }

    pub fn generate_random_unused_names(&self, count: usize) -> Vec<String> {
        let mut reserved_names: HashSet<String> = HashSet::with_capacity(count);
        let mut generated_names: Vec<String> = Vec::with_capacity(count);
        for _ in 0..count {
            let generated_name = self.generate_one_unused_name_with_reserved(&reserved_names);
            reserved_names.insert(generated_name.clone());
            generated_names.push(generated_name);
        }
        generated_names
    }

    pub fn generate_random_unused_name(&self) -> String {
        self.generate_one_unused_name_with_reserved(&HashSet::new())
    }
}
