use bevy::{
    prelude::*,
    render::{primitives::Aabb, render_resource::*},
};
use bitvec::boxed::BitBox;
use bytemuck::{Pod, Zeroable};

/// An axis-aligned box, extending from `minimum` to `maximum`.
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct Cuboid {
    pub minimum: Vec3,
    pub maximum: Vec3,
    pub color_rgba: [f32; 4],
}

impl Cuboid {
    pub fn new(minimum: Vec3, maximum: Vec3, color_rgba: [f32; 4]) -> Self {
        Self {
            minimum,
            maximum,
            color_rgba,
        }
    }

    pub(crate) const VERTEX_ATTRIBUTES: [VertexAttribute; 3] = [
        // Min
        VertexAttribute {
            format: VertexFormat::Float32x3,
            offset: 0,
            shader_location: 3, // shader locations 0-2 are taken up by Position, Normal and UV attributes
        },
        // Shape
        VertexAttribute {
            format: VertexFormat::Float32x3,
            offset: VertexFormat::Float32x3.size(),
            shader_location: 4,
        },
        // Color
        VertexAttribute {
            format: VertexFormat::Float32x4,
            offset: 2 * VertexFormat::Float32x3.size(),
            shader_location: 5,
        },
    ];
}

/// The set of cuboids to be extracted for rendering.
#[derive(Clone, Component)]
pub struct Cuboids {
    /// Instances to be rendered. These can be masked on/off by creating an optional [`CuboidsMask`] component.
    pub instances: Box<[Cuboid]>,
}

impl Cuboids {
    pub fn new(instances: Box<[Cuboid]>) -> Self {
        Self { instances }
    }
}

/// Automatically creates an [`Aabb`] that bounds all `instances`.
pub fn cuboids_aabb(instances: &[Cuboid]) -> Aabb {
    let mut min = Vec3::splat(f32::MAX);
    let mut max = Vec3::splat(f32::MIN);
    for i in instances.iter() {
        min = min.min(i.minimum);
        max = max.max(i.maximum);
    }
    Aabb::from_min_max(min, max)
}

/// Will be rendered.
pub const VISIBLE: bool = false;

/// Won't be rendered.
pub const INVISIBLE: bool = true;

/// An optional component to accompany [`Cuboids`] and set the visibility of individual instances.
#[derive(Component)]
pub struct CuboidsMask {
    /// Parallel to the `instances` on [`Cuboids`]. Only [`VISIBLE`] bits are rendered. [`INVISIBLE`] bits are not.
    pub bitmask: BitBox,
}

impl CuboidsMask {
    pub fn new(bitmask: BitBox) -> Self {
        Self { bitmask }
    }
}
