//! [`LayerMask`]: a 32-slot bitset used to include/exclude entities.

use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

use serde::{Deserialize, Serialize};

/// The number of distinct layers a [`LayerMask`] can represent.
///
/// Fixed at 32 so a mask is a single `u32` — cheap to copy, store on a
/// component, and test in hot paths (physics filtering, render visibility,
/// scene queries).
pub const MAX_LAYERS: u32 = 32;

/// A set of layers, packed into the bits of a `u32`.
///
/// A `LayerMask` is the one shared primitive behind every "which layers does
/// this interact with?" question in the engine. It plays two roles:
///
/// - **Membership** — the layers an entity *belongs to* (see
///   [`Layer`](super::Layer)).
/// - **Filter** — the layers a camera, query, or collision rule *cares about*.
///
/// Two masks interact when they share any layer: [`intersects`](Self::intersects)
/// is the universal test (`(a & b) != 0`). Layer indices run `0..32`; passing an
/// index `>= 32` panics (in every build), catching mistakes early rather than
/// silently wrapping.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct LayerMask(u32);

impl LayerMask {
    /// The empty mask — interacts with nothing.
    pub const NONE: LayerMask = LayerMask(0);

    /// Every layer set — interacts with everything.
    pub const ALL: LayerMask = LayerMask(u32::MAX);

    /// A mask from a raw bit pattern.
    pub const fn from_bits(bits: u32) -> Self {
        LayerMask(bits)
    }

    /// The raw bit pattern.
    pub const fn bits(self) -> u32 {
        self.0
    }

    /// A mask containing only the single layer `index` (`0..32`).
    ///
    /// # Panics
    /// Panics if `index >= 32`.
    pub const fn layer(index: u32) -> Self {
        assert!(
            index < MAX_LAYERS,
            "layer index out of range (must be 0..32)"
        );
        LayerMask(1u32 << index)
    }

    /// This mask with layer `index` added.
    pub const fn with(self, index: u32) -> Self {
        LayerMask(self.0 | Self::layer(index).0)
    }

    /// This mask with layer `index` removed.
    pub const fn without(self, index: u32) -> Self {
        LayerMask(self.0 & !Self::layer(index).0)
    }

    /// This mask with layer `index` flipped.
    pub const fn toggled(self, index: u32) -> Self {
        LayerMask(self.0 ^ Self::layer(index).0)
    }

    /// Whether layer `index` is present.
    ///
    /// # Panics
    /// Panics if `index >= 32`.
    pub const fn contains_layer(self, index: u32) -> bool {
        self.0 & Self::layer(index).0 != 0
    }

    /// Whether this mask and `other` share at least one layer.
    ///
    /// This is the canonical interaction test — a body on the masks it belongs
    /// to "interacts with" a filter that selects any of those layers.
    pub const fn intersects(self, other: LayerMask) -> bool {
        self.0 & other.0 != 0
    }

    /// Whether every layer in `other` is also in this mask.
    pub const fn contains(self, other: LayerMask) -> bool {
        self.0 & other.0 == other.0
    }

    /// The union (bitwise OR) of two masks.
    pub const fn union(self, other: LayerMask) -> Self {
        LayerMask(self.0 | other.0)
    }

    /// The intersection (bitwise AND) of two masks.
    pub const fn intersection(self, other: LayerMask) -> Self {
        LayerMask(self.0 & other.0)
    }

    /// The layers in this mask that are not in `other`.
    pub const fn difference(self, other: LayerMask) -> Self {
        LayerMask(self.0 & !other.0)
    }

    /// The complement — every layer not in this mask.
    pub const fn complement(self) -> Self {
        LayerMask(!self.0)
    }

    /// Whether no layers are set.
    pub const fn is_empty(self) -> bool {
        self.0 == 0
    }

    /// The number of layers set.
    pub const fn len(self) -> u32 {
        self.0.count_ones()
    }

    /// Iterates the indices (`0..32`) of the set layers, ascending.
    pub fn iter(self) -> impl Iterator<Item = u32> {
        (0..MAX_LAYERS).filter(move |&i| self.0 & (1u32 << i) != 0)
    }
}

impl Default for LayerMask {
    /// The empty mask. Filters that should default to "see everything" must opt
    /// into [`LayerMask::ALL`] explicitly rather than rely on this.
    fn default() -> Self {
        LayerMask::NONE
    }
}

impl FromIterator<u32> for LayerMask {
    /// Builds a mask from layer indices. Each index must be `0..32`.
    fn from_iter<I: IntoIterator<Item = u32>>(iter: I) -> Self {
        iter.into_iter().fold(LayerMask::NONE, LayerMask::with)
    }
}

impl BitOr for LayerMask {
    type Output = LayerMask;
    fn bitor(self, rhs: LayerMask) -> LayerMask {
        self.union(rhs)
    }
}

impl BitOrAssign for LayerMask {
    fn bitor_assign(&mut self, rhs: LayerMask) {
        self.0 |= rhs.0;
    }
}

impl BitAnd for LayerMask {
    type Output = LayerMask;
    fn bitand(self, rhs: LayerMask) -> LayerMask {
        self.intersection(rhs)
    }
}

impl BitAndAssign for LayerMask {
    fn bitand_assign(&mut self, rhs: LayerMask) {
        self.0 &= rhs.0;
    }
}

impl BitXor for LayerMask {
    type Output = LayerMask;
    fn bitxor(self, rhs: LayerMask) -> LayerMask {
        LayerMask(self.0 ^ rhs.0)
    }
}

impl BitXorAssign for LayerMask {
    fn bitxor_assign(&mut self, rhs: LayerMask) {
        self.0 ^= rhs.0;
    }
}

impl Not for LayerMask {
    type Output = LayerMask;
    fn not(self) -> LayerMask {
        self.complement()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn layer_sets_a_single_bit() {
        assert_eq!(LayerMask::layer(0).bits(), 0b1);
        assert_eq!(LayerMask::layer(3).bits(), 0b1000);
        assert_eq!(LayerMask::layer(31).bits(), 1 << 31);
    }

    #[test]
    #[should_panic]
    fn layer_index_out_of_range_panics() {
        let _ = LayerMask::layer(32);
    }

    #[test]
    fn builders_add_and_remove_layers() {
        let m = LayerMask::NONE.with(1).with(4);
        assert!(m.contains_layer(1));
        assert!(m.contains_layer(4));
        assert!(!m.contains_layer(0));
        assert_eq!(m.len(), 2);

        let m = m.without(1);
        assert!(!m.contains_layer(1));
        assert!(m.contains_layer(4));

        let m = m.toggled(4).toggled(7);
        assert!(!m.contains_layer(4));
        assert!(m.contains_layer(7));
    }

    #[test]
    fn intersects_is_the_interaction_test() {
        let player = LayerMask::layer(1);
        let npc = LayerMask::layer(2);
        // A trigger that only fires for the player or npc layers.
        let trigger_filter = player.union(npc);
        assert!(player.intersects(trigger_filter));
        assert!(npc.intersects(trigger_filter));
        // A wall on layer 5 does not trip the trigger.
        assert!(!LayerMask::layer(5).intersects(trigger_filter));
    }

    #[test]
    fn set_algebra() {
        let a = LayerMask::NONE.with(0).with(1).with(2);
        let b = LayerMask::NONE.with(1).with(2).with(3);
        assert_eq!(a.union(b), LayerMask::NONE.with(0).with(1).with(2).with(3));
        assert_eq!(a.intersection(b), LayerMask::NONE.with(1).with(2));
        assert_eq!(a.difference(b), LayerMask::layer(0));
        assert!(a.contains(LayerMask::NONE.with(0).with(1)));
        assert!(!a.contains(b));
        assert_eq!(LayerMask::ALL.complement(), LayerMask::NONE);
    }

    #[test]
    fn bit_operators_match_named_methods() {
        let a = LayerMask::layer(1);
        let b = LayerMask::layer(2);
        assert_eq!(a | b, a.union(b));
        assert_eq!((a | b) & a, a);
        assert_eq!(a ^ a, LayerMask::NONE);
        assert_eq!(!LayerMask::NONE, LayerMask::ALL);

        let mut m = LayerMask::NONE;
        m |= a;
        m |= b;
        assert!(m.intersects(a) && m.intersects(b));
        m &= a;
        assert_eq!(m, a);
    }

    #[test]
    fn iter_yields_ascending_indices() {
        let m = LayerMask::NONE.with(0).with(5).with(31);
        assert_eq!(m.iter().collect::<Vec<_>>(), vec![0, 5, 31]);
        assert!(LayerMask::NONE.iter().next().is_none());
    }

    #[test]
    fn from_iter_collects_indices() {
        let m: LayerMask = [1u32, 3, 5].into_iter().collect();
        assert_eq!(m, LayerMask::NONE.with(1).with(3).with(5));
    }

    #[test]
    fn round_trips_through_ron() {
        let m = LayerMask::NONE.with(2).with(9).with(30);
        let ron = ron::to_string(&m).unwrap();
        let back: LayerMask = ron::from_str(&ron).unwrap();
        assert_eq!(m, back);
    }
}
