Documentation
use super::*;

/// Bounds component.
///
/// Use to set custom bounds or read bounds defined by a mesh.
///
/// If an entity has a render component and it doesn't exist it will be added and
/// set to match the mesh, but will be available a frame later.

pub struct Bounds {
    id: Entity,
}

impl Bounds {
    /// Get the local space bounding box.
    ///
    /// Convenience for calling [`Self::bounding_box_min`] and [`Self::bounding_box_max`].
    pub fn bounding_box(&self) -> macaw::BoundingBox {
        // TODO: optimize to one FFI call
        macaw::BoundingBox {
            min: self.bounding_box_min().get(),
            max: self.bounding_box_max().get(),
        }
    }

    /// Set the local space bounding box.
    ///
    /// Convenience for calling [`Self::bounding_box_min`] and [`Self::bounding_box_max`].
    pub fn set_bounding_box(&self, bbox: macaw::BoundingBox) {
        // TODO: optimize to one FFI call
        self.bounding_box_min().set(bbox.min);
        self.bounding_box_max().set(bbox.max);
    }
}

impl std::fmt::Debug for Bounds {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Bounds")
            .field("entity", &self.id.name())
            .field("bounding_box_min", &self.bounding_box_min().get())
            .field("bounding_box_max", &self.bounding_box_max().get())
            .field(
                "bounding_sphere_radius",
                &self.bounding_sphere_radius().get(),
            )
            .field("match_render_shape", &self.match_render_shape().get())
            .finish()
    }
}

impl Bounds {
    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the bounding box min of the entity.
        ///
        /// Used to set/get/animate the min.
        ///
        /// For physics this will be used when the body is setup, for spatial queries it will be used as is.
        Bounds,
        BoundingBoxMin,
        Vec3,
        bounding_box_min,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the bounding box max of the entity.
        ///
        /// Used to set/get/animate the max.
        ///
        /// For physics this will be used when the body is setup, for spatial queries it will be used as is.
        Bounds,
        BoundingBoxMax,
        Vec3,
        bounding_box_max,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the bounding sphere radius of the entity.
        ///
        /// Used to set/get/animate the radius.
        ///
        /// For physics this will be used when the body is setup, for spatial queries it will be used as is.
        Bounds,
        BoundingSphereRadius,
        f32,
        bounding_sphere_radius,
        ValueAccessorReadWriteAnimate
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the match render shape toggle of the entity.
        ///
        /// Used to set/get whether these bounds will match the render shape.
        ///
        /// Set this to false if you want to set a custom bounding shape, box or sphere when the entity has a render component with a shape set.
        Bounds,
        MatchRenderShape,
        bool,
        match_render_shape,
        ValueAccessorReadWrite
    );

    impl_world_accessor!(
        /// Returns a `ValueAccessor` for the bounding shape of the entity.
        ///
        /// Used to set/get the bounding shape.
        ///
        /// Use this to set a custom collider/physics/spatial query shape with MatchRenderShape set to false.
        Bounds,
        BoundingShape,
        Shape,
        shape,
        ValueAccessorDataReadWrite
    );

    /// Convenience method to set up both the sphere and the bounding box
    /// to contain a certain radius.
    ///
    pub fn create_sphere_bounds(&self, radius: f32) {
        self.set_bounding_box(macaw::BoundingBox::from_min_max(
            Vec3::splat(-radius),
            Vec3::splat(radius),
        ));
        self.bounding_sphere_radius().set(radius);
        self.match_render_shape().set(false);
    }
}

impl_world_component!(Bounds);