/// An SDF scene is a tree of distance-function nodes.
/// Every node evaluates to a signed distance at a given point in 3-space.
#[derive(Debug, Clone)]
pub enum SdfNode {
    // Primitives — each is a closed-form distance function
    Sphere { radius: f64 },
    Box { half_extents: [f64; 3] },
    Cylinder { radius: f64, height: f64 },

    // Boolean operations — compose child distance fields
    Union(Vec<SdfNode>),
    Intersection(Vec<SdfNode>),
    Difference { base: Box<SdfNode>, subtract: Vec<SdfNode> },
    SmoothUnion { children: Vec<SdfNode>, k: f64 },

    // Affine transforms — modify the evaluation point
    Translate { offset: [f64; 3], child: Box<SdfNode> },
    Rotate { axis: [f64; 3], angle_deg: f64, child: Box<SdfNode> },
    Scale { factor: [f64; 3], child: Box<SdfNode> },

    // Linear extrude a 2D profile (future)
    // Extrude { height: f64, child: Box<SdfNode> },
}

impl SdfNode {
    /// Bounding radius estimate for the raymarcher's far plane.
    pub fn bounding_radius(&self) -> f64 {
        match self {
            SdfNode::Sphere { radius } => *radius,
            SdfNode::Box { half_extents: h } => (h[0] * h[0] + h[1] * h[1] + h[2] * h[2]).sqrt(),
            SdfNode::Cylinder { radius, height } => (radius * radius + (height / 2.0).powi(2)).sqrt(),
            SdfNode::Union(children) | SdfNode::Intersection(children) => {
                children.iter().map(|c| c.bounding_radius()).fold(0.0f64, f64::max)
            }
            SdfNode::Difference { base, .. } => base.bounding_radius(),
            SdfNode::SmoothUnion { children, .. } => {
                children.iter().map(|c| c.bounding_radius()).fold(0.0f64, f64::max)
            }
            SdfNode::Translate { offset, child } => {
                let off_len = (offset[0].powi(2) + offset[1].powi(2) + offset[2].powi(2)).sqrt();
                child.bounding_radius() + off_len
            }
            SdfNode::Rotate { child, .. } => child.bounding_radius(),
            SdfNode::Scale { factor, child } => {
                let max_scale = factor[0].abs().max(factor[1].abs()).max(factor[2].abs());
                child.bounding_radius() * max_scale
            }
        }
    }
}
