///|
/// 场景图节点树遍历结果
pub(all) struct NodeTreeInfo {
  /// 节点总数
  total_nodes : Int
  /// 最大深度
  max_depth : Int
  /// 含网格的节点数
  mesh_node_count : Int
  /// 含蒙皮的节点数
  skinned_node_count : Int
  /// 根节点索引列表
  root_nodes : Array[Int]
  /// 各节点深度映射(节点索引 -> 深度)
  node_depths : Map[Int, Int]
} derive(Debug, ToJson, Eq)

///|
/// 遍历 glTF 场景图,收集节点树信息
///
/// # Example
/// ```mbt check
/// test "node tree basic" {
///   let doc = @gltf.GltfDocument::{
///     version: "2.0",
///     generator: None,
///     copyright: None,
///     scenes: [{ name: Some("Scene"), nodes: [0] }],
///     nodes: [
///       {
///         name: Some("Root"),
///         children: [1],
///         mesh: None,
///         skin: None,
///         camera: None,
///         translation: None,
///         rotation: None,
///         scale: None,
///       },
///       {
///         name: Some("Child"),
///         children: [],
///         mesh: Some(0),
///         skin: None,
///         camera: None,
///         translation: None,
///         rotation: None,
///         scale: None,
///       },
///     ],
///     meshes: [],
///     materials: [],
///     textures: [],
///     images: [],
///     samplers: [],
///     accessors: [],
///     buffer_views: [],
///     buffers: [],
///     animations: [],
///     skins: [],
///     default_scene: Some(0),
///   }
///   let info = @gltf.traverse_node_tree(doc)
///   inspect(info.total_nodes, content="2")
///   inspect(info.max_depth, content="2")
///   inspect(info.mesh_node_count, content="1")
/// }
/// ```
pub fn traverse_node_tree(doc : GltfDocument) -> NodeTreeInfo {
  let node_depths : Map[Int, Int] = Map([])
  let visited : Map[Int, Bool] = Map([])
  let mut max_depth = 0
  let mut mesh_node_count = 0
  let mut skinned_node_count = 0
  // DFS 遍历
  fn dfs(idx : Int, depth : Int) {
    if visited.contains(idx) {
      return
    }
    visited[idx] = true
    node_depths[idx] = depth
    if depth > max_depth {
      max_depth = depth
    }
    if idx >= 0 && idx < doc.nodes.length() {
      let node = doc.nodes[idx]
      if node.mesh is Some(_) {
        mesh_node_count += 1
      }
      if node.skin is Some(_) {
        skinned_node_count += 1
      }
      for child_idx in node.children {
        dfs(child_idx, depth + 1)
      }
    }
  }

  // 收集根节点(场景中直接引用的节点)
  let root_nodes : Array[Int] = []
  for scene in doc.scenes {
    for node_idx in scene.nodes {
      if !root_nodes.contains(node_idx) {
        root_nodes.push(node_idx)
      }
    }
  }
  // 从根节点开始遍历
  for root_idx in root_nodes {
    dfs(root_idx, 1)
  }
  // 检查孤立节点(未被任何场景引用)
  for i in 0.. ResourceDeps {
  let external_uris : Array[String] = []
  let mut embedded_buffer_count = 0
  let mut embedded_image_count = 0
  let mut total_buffer_bytes = 0
  // 检查缓冲区
  for buf in doc.buffers {
    total_buffer_bytes += buf.byte_length
    match buf.uri {
      Some(uri) =>
        if uri.has_prefix("data:") {
          embedded_buffer_count += 1
        } else {
          external_uris.push(uri)
        }
      None => embedded_buffer_count += 1
    }
  }
  // 检查图像
  for img in doc.images {
    match img.uri {
      Some(uri) =>
        if uri.has_prefix("data:") {
          embedded_image_count += 1
        } else {
          external_uris.push(uri)
        }
      None => embedded_image_count += 1
    }
  }
  // 收集纹理引用的图像索引
  let texture_image_refs : Array[Int] = []
  for tex in doc.textures {
    match tex.source {
      Some(idx) => texture_image_refs.push(idx)
      None => ()
    }
  }
  {
    external_uris,
    embedded_buffer_count,
    embedded_image_count,
    total_buffer_bytes,
    texture_image_refs,
  }
}

///|
/// 网格元数据摘要
pub(all) struct MeshMetadata {
  /// 网格名称
  name : String?
  /// 图元数量
  primitive_count : Int
  /// 顶点属性集合(如 POSITION、NORMAL)
  attributes : Array[String]
  /// 使用的材质索引列表
  material_indices : Array[Int]
  /// 是否含索引数据
  has_indices : Bool
  /// 顶点总数(所有图元 POSITION accessor count 之和)
  vertex_count : Int
} derive(Debug, ToJson)

///|
/// 提取所有网格的元数据
pub fn extract_mesh_metadata(doc : GltfDocument) -> Array[MeshMetadata] {
  doc.meshes.map(mesh => {
    let mut primitive_count = 0
    let attrs_set : Map[String, Bool] = Map([])
    let material_indices : Array[Int] = []
    let mut has_indices = false
    let mut vertex_count = 0
    for prim in mesh.primitives {
      primitive_count += 1
      for attr, acc_idx in prim.attributes {
        attrs_set[attr] = true
        // 累加 POSITION 顶点数
        if attr == "POSITION" && acc_idx < doc.accessors.length() {
          vertex_count += doc.accessors[acc_idx].count
        }
      }
      match prim.material {
        Some(idx) =>
          if !material_indices.contains(idx) {
            material_indices.push(idx)
          }
        None => ()
      }
      if prim.indices is Some(_) {
        has_indices = true
      }
    }
    let attributes = attrs_set.keys().collect()
    {
      name: mesh.name,
      primitive_count,
      attributes,
      material_indices,
      has_indices,
      vertex_count,
    }
  })
}

///|
/// 动画元数据摘要
pub(all) struct AnimationMetadata {
  /// 动画名称
  name : String?
  /// 通道数量
  channel_count : Int
  /// 驱动的节点索引列表
  target_nodes : Array[Int]
  /// 驱动的属性集合
  target_paths : Array[String]
  /// 使用的插值方式列表
  interpolations : Array[String]
} derive(Debug, ToJson)

///|
/// 提取所有动画的元数据
pub fn extract_animation_metadata(
  doc : GltfDocument,
) -> Array[AnimationMetadata] {
  doc.animations.map(anim => {
    let target_nodes : Array[Int] = []
    let paths_set : Map[String, Bool] = Map([])
    let interp_set : Map[String, Bool] = Map([])
    for ch in anim.channels {
      match ch.target.node {
        Some(n) => if !target_nodes.contains(n) { target_nodes.push(n) }
        None => ()
      }
      let path_str = match ch.target.path {
        Translation => "translation"
        Rotation => "rotation"
        Scale => "scale"
        Weights => "weights"
      }
      paths_set[path_str] = true
    }
    for s in anim.samplers {
      let interp_str = match s.interpolation {
        Linear => "LINEAR"
        Step => "STEP"
        CubicSpline => "CUBICSPLINE"
      }
      interp_set[interp_str] = true
    }
    {
      name: anim.name,
      channel_count: anim.channels.length(),
      target_nodes,
      target_paths: paths_set.keys().collect(),
      interpolations: interp_set.keys().collect(),
    }
  })
}