// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use glam::IVec2;

use super::{Blocks, BlockBorder};

use crate::utils::component_finder::{self, BLOCK, UNASSIGNED};

pub struct Components {
	grid_size: IVec2,
	block_grid_offset: IVec2,
	data: Box<[u32]>,
}

impl Components {
	pub fn new(blocks: &Blocks, border: &BlockBorder) -> Components {
		let (w0, w1) = border.points();
		let (g0, g1) = (IVec2::NEG_ONE, blocks.get_grid_size().as_ivec2()); // Growing the block grid by one so component search can wrap around the world

		// Intersects the world and block grid rectangles
		let (p0, p1) = (w0.max(g0), w1.min(g1));
		let grid_size = p1.saturating_sub(p0) + IVec2::ONE;

		let mut data = Vec::with_capacity(grid_size.element_product() as usize);
		for y in 0..grid_size.y {
			for x in 0..grid_size.x {
				let block = blocks.get(p0 + IVec2::new(x, y)).unwrap_or_default();
				data.push(if block { BLOCK } else { UNASSIGNED });
			}
		}
		component_finder::find(grid_size.as_uvec2(), &mut data);

		Components { grid_size, block_grid_offset: p0, data: data.into_boxed_slice() }
	}

	pub fn reachable(&self, start: IVec2, goal: IVec2) -> bool {
		let (sc, gc) = (self.get_component(start), self.get_component(goal));
		sc == gc && sc != BLOCK // gc != BLOCK holds if this is true
	}

	fn get_component(&self, mut pos: IVec2) -> u32 {
		pos = (pos - self.block_grid_offset).clamp(IVec2::ZERO, self.grid_size - IVec2::ONE);
		self.data[(pos.x + pos.y * self.grid_size.x) as usize]
	}
}
