use rand::{Rng, seq::IteratorRandom};

use crate::types::{Coord, Direction, simulation::Board, wire::Settings};

use super::{Map, MapId, standard::Standard};

#[derive(Debug, Clone, Copy)]
pub struct Royale {
    standard: Standard,
    shrink_every_n_turns: u8,
}

impl Map for Royale {
    fn pre_update(&self, board: &mut Board, rng: &mut impl Rng) {
        self.standard.pre_update(board, rng);
    }

    fn post_update(&self, board: &mut Board, rng: &mut impl Rng) {
        self.standard.post_update(board, rng);

        // Royale uses the current turn to generate hazards, not the previous turn that's in the
        // board state
        let turn = board.turn + 1;

        if turn < u32::from(self.shrink_every_n_turns) {
            return;
        }

        let side = enum_iterator::all::<Direction>()
            .choose(rng)
            .unwrap_or(Direction::Up);
        match side {
            Direction::Up => {
                if let Some(i) = board.hazard.first_zero() {
                    let y = board.linear_to_coord(i).y;
                    for x in 0..board.width {
                        let i = board.coord_to_linear(Coord { x, y });
                        board.hazard.set(i, true);
                    }
                }
            }
            Direction::Down => {
                if let Some(i) = board.hazard.last_zero() {
                    let y = board.linear_to_coord(i).y;
                    for x in 0..board.width {
                        let i = board.coord_to_linear(Coord { x, y });
                        board.hazard.set(i, true);
                    }
                }
            }
            Direction::Left => {
                if let Some(i) = board.hazard.first_zero() {
                    let x = board.linear_to_coord(i).y;
                    for y in 0..board.height {
                        let i = board.coord_to_linear(Coord { x, y });
                        board.hazard.set(i, true);
                    }
                }
            }
            Direction::Right => {
                if let Some(i) = board.hazard.last_zero() {
                    let x = board.linear_to_coord(i).y;
                    for y in 0..board.height {
                        let i = board.coord_to_linear(Coord { x, y });
                        board.hazard.set(i, true);
                    }
                }
            }
        }
    }
}

impl MapId for Royale {
    const ID: &str = "royale";
}

impl From<&Settings> for Royale {
    fn from(value: &Settings) -> Self {
        let standard = Standard::from(value);
        let shrink_every_n_turns = value.royale.shrink_every_n_turns;
        Self {
            standard,
            shrink_every_n_turns,
        }
    }
}
