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

/**
 * A processed border used to get primitives from for rendering.
 *
 * The order of edges and corners are preserved from the input border section
 * data.
 */
pub struct Border {
	edges: [Option<BorderEdge>; 4],
	corners: [Option<BorderCorner>; 4],
}

impl Border {
	/**
	 * Input indexed as followed, where up is positive:
	 * 	567
	 * 	3 4
	 * 	012
	 */
	pub fn empty_adjacent_to_section_data(mut empty_adjacent: u8) -> u8 {
		/*
		 * Converts neighbour information into border information.
		 *
		 * This removes corner pieces of the border if that corner has any
		 * adjacent edge border. This keeps only the external corner
		 * borders.
		 */
		let (e0, e1, e2, e3) = ((empty_adjacent >> 1) & 1, (empty_adjacent >> 3) & 1, (empty_adjacent >> 4) & 1, (empty_adjacent >> 6) & 1);
		empty_adjacent &= !(e0 | e1);
		empty_adjacent &= !((e0 | e2) << 2);
		empty_adjacent &= !((e1 | e3) << 5);
		empty_adjacent &= !((e2 | e3) << 7);
		empty_adjacent
	}

	/**
	 * Takes input from border section data output from
	 * `empty_adjacent_to_section_data` and then merged together.
	 */
	pub fn new(section_data: u8) -> Border {
		let mut corners = [0, 2, 5, 7].map(|i| ((section_data & (1 << i)) != 0).then_some(BorderCorner::External));
		let mut edges = [1, 3, 4, 6].map(|i| ((section_data & (1 << i)) != 0).then(BorderEdge::default));

		for (corner_i, ((e0_i, e0_corner), (e1_i, e1_corner))) in [
			((0, 0), (1, 0)),
			((0, 1), (2, 0)),
			((1, 1), (3, 0)),
			((2, 1), (3, 1)),
		].into_iter().enumerate() {
			if edges[e0_i].is_some() && edges[e1_i].is_some() {
				let corner = &mut corners[corner_i];
				debug_assert!(corner.is_none());
				*corner = Some(BorderCorner::Internal);

				// Would like to use `if let` rather than `unwrap`, but there are borrow checker problems
				edges[e0_i].as_mut().unwrap().0[e0_corner] = true;
				edges[e1_i].as_mut().unwrap().0[e1_corner] = true;
			}
		}

		Border { edges, corners }
	}

	pub fn get_edges(&self) -> &[Option<BorderEdge>] {
		&self.edges
	}

	pub fn get_corners(&self) -> &[Option<BorderCorner>] {
		&self.corners
	}
}

/**
 * Contains data about whether the edge should be shrinked for each of the two
 * corners, first negative then positive.
 */
#[derive(Default, Clone, Copy)]
pub struct BorderEdge([bool; 2]);

impl BorderEdge {
	pub fn negative_corner(self) -> bool { self.0[0] }
	pub fn positive_corner(self) -> bool { self.0[1] }
}

pub enum BorderCorner {
	/**
	 * Part of a 270° angle inside the block, when the corner has no adjacent
	 * edges.
	 */
	External,

	/**
	 * Where two edges intersect.
	 */
	Internal,
}

pub const EDGE_I_TO_POS: [IVec2; 4] = [IVec2::NEG_Y, IVec2::NEG_X, IVec2::X, IVec2::Y];

pub const CORNER_I_TO_POS: [IVec2; 4] = [
	IVec2::new(-1, -1),
	IVec2::new( 1, -1),
	IVec2::new(-1,  1),
	IVec2::new( 1,  1),
];
