use crate::dict::index::WordIndex;
use crate::grid::slot::Slot;

pub struct SolverState {
    pub assignments: Vec<Option<usize>>,
    pub used_words: Vec<bool>,
    pub grid_letters: Vec<Vec<Option<u8>>>,
    pub cell_domains: Vec<Vec<u32>>,
    pub width: usize,
    pub height: usize,
}

impl SolverState {
    pub fn new(slots: &[Slot], width: usize, height: usize, dict_size: usize) -> Self {
        let all_bits = (1u32 << 26) - 1;
        Self {
            assignments: vec![None; slots.len()],
            used_words: vec![false; dict_size],
            grid_letters: vec![vec![None; width]; height],
            cell_domains: vec![vec![all_bits; width]; height],
            width,
            height,
        }
    }

    pub fn init_domains(&mut self, slots: &[Slot], dict: &WordIndex) {
        for slot in slots {
            for (pos, &(r, c)) in slot.cells().iter().enumerate() {
                let allowed = dict.allowed_letters(slot.length, pos);
                if allowed != 0 {
                    self.cell_domains[r][c] &= allowed;
                }
            }
        }
    }

    pub fn get_domain(&self, r: usize, c: usize) -> u32 {
        self.cell_domains[r][c]
    }

    pub fn set_domain(&mut self, r: usize, c: usize, domain: u32) {
        self.cell_domains[r][c] = domain;
    }

    pub fn get_letter(&self, row: usize, col: usize) -> Option<u8> {
        self.grid_letters[row][col]
    }

    pub fn set_letter(&mut self, row: usize, col: usize, letter: u8) {
        self.grid_letters[row][col] = Some(letter);
    }

    pub fn clear_letter(&mut self, row: usize, col: usize) {
        self.grid_letters[row][col] = None;
    }

    pub fn mark_used(&mut self, word_idx: usize) {
        self.used_words[word_idx] = true;
    }

    pub fn unmark_used(&mut self, word_idx: usize) {
        self.used_words[word_idx] = false;
    }

    pub fn is_used(&self, word_idx: usize) -> bool {
        self.used_words[word_idx]
    }

    pub fn current_constraints(&self, slot: &Slot) -> Vec<(usize, u8)> {
        let mut constraints = Vec::new();
        for (pos, (r, c)) in slot.cells().iter().enumerate() {
            if let Some(letter) = self.get_letter(*r, *c) {
                constraints.push((pos, letter));
            }
        }
        constraints
    }
}
