use cord_trig::ir::NodeId;
use cord_trig::lower::{SdfBuilder, TrigPoint3};
use cord_trig::TrigGraph;
use crate::SdfNode;

/// Convert an SdfNode tree into a TrigGraph.
///
/// Walks the SDF tree recursively, using SdfBuilder to decompose
/// each primitive/transform/boolean into trig IR nodes.
pub fn sdf_to_trig(node: &SdfNode) -> TrigGraph {
    let mut b = SdfBuilder::new();
    let p = b.root_point();
    let output = lower_node(&mut b, p, node);
    b.finish(output)
}

fn lower_node(b: &mut SdfBuilder, point: TrigPoint3, node: &SdfNode) -> NodeId {
    match node {
        SdfNode::Sphere { radius } => b.sphere(point, *radius),
        SdfNode::Box { half_extents } => b.box_sdf(point, *half_extents),
        SdfNode::Cylinder { radius, height } => b.cylinder(point, *radius, *height),

        SdfNode::Union(children) => {
            let mut result = lower_node(b, point, &children[0]);
            for child in &children[1..] {
                let c = lower_node(b, point, child);
                result = b.union(result, c);
            }
            result
        }

        SdfNode::Intersection(children) => {
            let mut result = lower_node(b, point, &children[0]);
            for child in &children[1..] {
                let c = lower_node(b, point, child);
                result = b.intersection(result, c);
            }
            result
        }

        SdfNode::Difference { base, subtract } => {
            let mut result = lower_node(b, point, base);
            for sub in subtract {
                let s = lower_node(b, point, sub);
                result = b.difference(result, s);
            }
            result
        }

        SdfNode::SmoothUnion { children, k } => {
            let mut result = lower_node(b, point, &children[0]);
            for child in &children[1..] {
                let c = lower_node(b, point, child);
                result = b.smooth_union(result, c, *k);
            }
            result
        }

        SdfNode::Translate { offset, child } => {
            let p2 = b.translate(point, *offset);
            lower_node(b, p2, child)
        }

        SdfNode::Rotate { axis, angle_deg, child } => {
            let angle_rad = angle_deg.to_radians();
            // SdfNode::Rotate stores axis-aligned rotations from the SCAD lowerer.
            // Decompose by dominant axis.
            let p2 = if axis[0].abs() > 0.9 {
                b.rotate_x(point, angle_rad * axis[0].signum())
            } else if axis[1].abs() > 0.9 {
                b.rotate_y(point, angle_rad * axis[1].signum())
            } else {
                b.rotate_z(point, angle_rad * axis[2].signum())
            };
            lower_node(b, p2, child)
        }

        SdfNode::Scale { factor, child } => {
            let (p2, min_scale) = b.scale(point, *factor);
            let dist = lower_node(b, p2, child);
            b.scale_distance(dist, min_scale)
        }
    }
}
