use crate::generators::{
    GenerationStep, GenerationSteps, GenerationVisitor, MazeGenerator2D, VecGenerationVisitor,
};
use crate::grid_coord_2d::GridCoord2D;
use crate::wall4_grid::Wall4Grid;
use rand::rngs::StdRng;
use rand::{RngExt, SeedableRng};

pub struct Sidewinder4 {
    rng_seed: u64,
}

impl Default for Sidewinder4 {
    fn default() -> Self {
        Self::new_random()
    }
}

impl Sidewinder4 {
    fn generate_with_steps(&self, width: usize, height: usize) -> (Wall4Grid, Vec<GenerationStep>) {
        let mut grid = Wall4Grid::new(width, height);
        let mut visitor = VecGenerationVisitor::default();
        if width == 0 || height == 0 {
            visitor.on_step(&GenerationStep::Complete);
            return (grid, visitor.into_steps());
        }

        let mut rng = StdRng::seed_from_u64(self.rng_seed);
        for y in 0..height {
            let mut run_start = 0usize;
            for x in 0..width {
                let cell = GridCoord2D::new(x, y);
                visitor.on_step(&GenerationStep::Visit { cell });
                let at_east_boundary = x + 1 == width;
                let at_north_boundary = y == 0;
                let carve_east = !at_east_boundary && (at_north_boundary || rng.random_bool(0.5));

                if carve_east {
                    let east = GridCoord2D::new(x + 1, y);
                    grid.remove_wall_between(cell, east);
                    visitor.on_step(&GenerationStep::Carve {
                        from: cell,
                        to: east,
                    });
                } else if !at_north_boundary {
                    let carve_x = rng.random_range(run_start..=x);
                    let from = GridCoord2D::new(carve_x, y);
                    let to = GridCoord2D::new(carve_x, y - 1);
                    grid.remove_wall_between(from, to);
                    visitor.on_step(&GenerationStep::Carve { from, to });
                    run_start = x + 1;
                }
            }
        }

        visitor.on_step(&GenerationStep::Complete);
        (grid, visitor.into_steps())
    }
}

impl MazeGenerator2D for Sidewinder4 {
    fn new_random() -> Self {
        Self {
            rng_seed: rand::random(),
        }
    }

    fn new_from_seed(rng_seed: u64) -> Self {
        if rng_seed == 0 {
            Self::new_random()
        } else {
            Self { rng_seed }
        }
    }

    fn generate(&self, width: usize, height: usize) -> Wall4Grid {
        self.generate_with_steps(width, height).0
    }

    fn generate_steps(&self, width: usize, height: usize) -> GenerationSteps {
        GenerationSteps::new(self.generate_with_steps(width, height).1)
    }

    fn name(&self) -> &'static str {
        "sidewinder"
    }

    fn description(&self) -> &'static str {
        "Fast row-wise generator with horizontal bias"
    }
}
