///|
/// Public API helpers
/// Convenience functions for rendering to Image and bridging external trees.

///|
/// Parse and render SVG markup with structured diagnostics.
pub fn render_svg(
  svg_str : String,
  width : Int,
  height : Int,
  options : RenderOptions,
) -> RenderResult {
  let diagnostics : Array[RenderDiagnostic] = []
  match
    parse_svg_document_with_options(
      svg_str,
      width.to_double(),
      height.to_double(),
      options,
      diagnostics,
    ) {
    Some(document) =>
      render_svg_document_with_diagnostics(
        document, width, height, options, diagnostics,
      )
    None => {
      diagnostics.push({
        kind: ParseFailed,
        stage: Document,
        resource: "",
        node_id: "",
      })
      { image: Image::new(width, height), diagnostics }
    }
  }
}

///|
/// Render a parsed SVG document with structured diagnostics.
pub fn render_svg_document(
  document : SVGDocument,
  width : Int,
  height : Int,
  options : RenderOptions,
) -> RenderResult {
  render_svg_document_with_diagnostics(document, width, height, options, [])
}

///|
fn render_svg_document_with_diagnostics(
  document : SVGDocument,
  width : Int,
  height : Int,
  options : RenderOptions,
  diagnostics : Array[RenderDiagnostic],
) -> RenderResult {
  let image = Image::new(width, height)
  let setter = make_image_compositing_setter(image)
  let ctx = {
    ..RenderState::new(setter, width, height),
    image_resolver: options.image_resolver,
    target_image: Some(image),
    diagnostics,
  }
  document.render(ctx)
  { image, diagnostics }
}

///|
/// Parse and render an SVG markup string into an Image.
pub fn render_svg_to_image(
  svg_str : String,
  width : Int,
  height : Int,
) -> Image? {
  parse_svg_document_in_viewport(svg_str, width.to_double(), height.to_double()).map(fn(
      doc,
    ) {
      render_svg_document(doc, width, height, RenderOptions::default()).image
    },
  )
}

///|
/// Render PathCommand array directly to an Image (no SVG string parsing).
/// This is much faster than render_svg_to_image for programmatic paths
/// (e.g., font glyph outlines) because it skips SVG serialization and parsing.
///
/// `transform` is a 6-element affine transform [a, b, c, d, e, f] or empty for identity.
pub fn render_path_commands_to_image(
  commands : Array[PathCommand],
  width : Int,
  height : Int,
  fill_color : Color,
  transform? : Array[Double] = [],
) -> Image {
  let image = Image::new(width, height)
  if commands.is_empty() || width <= 0 || height <= 0 {
    return image
  }
  let tf = if transform.length() >= 6 {
    Transform::matrix(
      transform[0],
      transform[1],
      transform[2],
      transform[3],
      transform[4],
      transform[5],
    )
  } else {
    Transform::identity()
  }
  let contours = path_to_polylines(commands, device_path_flatness(tf)).map(fn(
    polyline,
  ) {
    polyline.map(fn(point) { tf.apply(point.0, point.1) })
  })
  raster_contours_coverage(
    contours,
    fill_color,
    NonZero,
    make_image_compositing_setter(image),
    width,
    height,
  )
  image
}