//! Bounded, native glTF 2.0 document and binary-buffer support.

// Accessor methods and owned DTO parameters are fixed by the compatibility map;
// explicit numeric conversions are guarded by the format bounds below.
#![allow(clippy::pedantic)]

use std::collections::HashMap;

use base64::Engine as _;
use libremetaverse_types::{Color4, Error, Matrix4, Quaternion, Vector2, Vector3, Vector4};
use serde_json::{Map, Value, json};

use crate::assets::GltfAlphaMode;
use crate::assets::GltfTextureTransform;
use crate::assets::gltf::{
    GltfAccessorType, GltfAnimationInterpolation, GltfAnimationTargetPath, GltfComponentType,
    GltfPrimitiveMode, GltfSamplerFilter, GltfSamplerWrap,
};

const MAX_DOCUMENT_BYTES: usize = 64 * 1024 * 1024;
const MAX_BUFFER_BYTES: usize = 256 * 1024 * 1024;
const MAX_COLLECTION_ITEMS: usize = 1_000_000;

fn parse_error(context: &'static str) -> Error {
    Error::Parse {
        position: 0,
        context,
    }
}

fn usize_from_i32(value: i32, context: &'static str) -> Result<usize, Error> {
    usize::try_from(value).map_err(|_| parse_error(context))
}

fn i32_value(value: Option<&Value>, default: i32) -> Result<i32, Error> {
    match value {
        None | Some(Value::Null) => Ok(default),
        Some(value) => value
            .as_i64()
            .and_then(|v| i32::try_from(v).ok())
            .ok_or_else(|| parse_error("glTF integer")),
    }
}

fn f32_value(value: Option<&Value>, default: f32) -> Result<f32, Error> {
    match value {
        None | Some(Value::Null) => Ok(default),
        Some(value) => value
            .as_f64()
            .filter(|v| v.is_finite())
            .map(|v| v as f32)
            .ok_or_else(|| parse_error("glTF finite number")),
    }
}

fn optional_string(value: Option<&Value>) -> Result<Option<String>, Error> {
    match value {
        None | Some(Value::Null) => Ok(None),
        Some(value) => value
            .as_str()
            .map(|v| Some(v.to_owned()))
            .ok_or_else(|| parse_error("glTF string")),
    }
}

fn number_array(value: Option<&Value>) -> Result<Option<Vec<f64>>, Error> {
    let Some(value) = value else { return Ok(None) };
    let values = value
        .as_array()
        .ok_or_else(|| parse_error("glTF number array"))?;
    if values.len() > 16 {
        return Err(parse_error("glTF number array limit"));
    }
    values
        .iter()
        .map(|v| {
            v.as_f64()
                .filter(|n| n.is_finite())
                .ok_or_else(|| parse_error("glTF finite number array"))
        })
        .collect::<Result<Vec<_>, _>>()
        .map(Some)
}

fn int_array(value: Option<&Value>) -> Result<Vec<i32>, Error> {
    let Some(value) = value else {
        return Ok(Vec::new());
    };
    let values = value
        .as_array()
        .ok_or_else(|| parse_error("glTF integer array"))?;
    if values.len() > MAX_COLLECTION_ITEMS {
        return Err(parse_error("glTF collection limit"));
    }
    values
        .iter()
        .map(|v| {
            v.as_i64()
                .and_then(|n| i32::try_from(n).ok())
                .ok_or_else(|| parse_error("glTF integer array"))
        })
        .collect()
}

fn finite_vec(value: Option<&Value>, count: usize, defaults: &[f32]) -> Result<Vec<f32>, Error> {
    let Some(value) = value else {
        return Ok(defaults.to_vec());
    };
    let values = value.as_array().ok_or_else(|| parse_error("glTF vector"))?;
    if values.len() != count {
        return Err(parse_error("glTF vector length"));
    }
    values
        .iter()
        .map(|v| {
            v.as_f64()
                .filter(|n| n.is_finite())
                .map(|n| n as f32)
                .ok_or_else(|| parse_error("glTF finite vector"))
        })
        .collect()
}

#[derive(Clone, Debug)]
pub struct GltfAccessor {
    buffer_view: i32,
    byte_offset: i32,
    component_type: GltfComponentType,
    normalized: bool,
    count: i32,
    type_: GltfAccessorType,
    max: Option<Vec<f64>>,
    min: Option<Vec<f64>>,
    name: Option<String>,
}

impl GltfAccessor {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn buffer_view(&self) -> i32 {
        self.buffer_view
    }
    pub fn set_buffer_view(&mut self, value: i32) {
        self.buffer_view = value;
    }
    pub fn byte_offset(&self) -> i32 {
        self.byte_offset
    }
    pub fn set_byte_offset(&mut self, value: i32) {
        self.byte_offset = value;
    }
    pub fn component_byte_size(&self) -> i32 {
        component_size(self.component_type) as i32
    }
    pub fn component_count(&self) -> i32 {
        accessor_components(self.type_) as i32
    }
    pub fn component_type(&self) -> GltfComponentType {
        self.component_type
    }
    pub fn set_component_type(&mut self, value: GltfComponentType) {
        self.component_type = value;
    }
    pub fn count(&self) -> i32 {
        self.count
    }
    pub fn set_count(&mut self, value: i32) {
        self.count = value;
    }
    pub fn default_stride(&self) -> i32 {
        self.component_byte_size() * self.component_count()
    }
    pub fn max(&self) -> Option<Vec<f64>> {
        self.max.clone()
    }
    pub fn set_max(&mut self, value: Option<Vec<f64>>) {
        self.max = value;
    }
    pub fn min(&self) -> Option<Vec<f64>> {
        self.min.clone()
    }
    pub fn set_min(&mut self, value: Option<Vec<f64>>) {
        self.min = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn normalized(&self) -> bool {
        self.normalized
    }
    pub fn set_normalized(&mut self, value: bool) {
        self.normalized = value;
    }
    pub fn type_(&self) -> GltfAccessorType {
        self.type_
    }
    pub fn set_type_(&mut self, value: GltfAccessorType) {
        self.type_ = value;
    }
}

impl Default for GltfAccessor {
    fn default() -> Self {
        Self {
            buffer_view: -1,
            byte_offset: 0,
            component_type: GltfComponentType::Float,
            normalized: false,
            count: 0,
            type_: GltfAccessorType::Scalar,
            max: None,
            min: None,
            name: None,
        }
    }
}

#[derive(Clone, Debug, Default)]
pub struct GltfAnimation {
    channels: Vec<GltfAnimationChannel>,
    name: Option<String>,
    samplers: Vec<GltfAnimationSampler>,
}
impl GltfAnimation {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn channels(&self) -> Vec<GltfAnimationChannel> {
        self.channels.clone()
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn samplers(&self) -> Vec<GltfAnimationSampler> {
        self.samplers.clone()
    }
}

#[derive(Clone, Debug)]
pub struct GltfAnimationChannel {
    sampler: i32,
    target: GltfAnimationChannelTarget,
}
impl Default for GltfAnimationChannel {
    fn default() -> Self {
        Self {
            sampler: -1,
            target: GltfAnimationChannelTarget::default(),
        }
    }
}
impl GltfAnimationChannel {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn sampler(&self) -> i32 {
        self.sampler
    }
    pub fn set_sampler(&mut self, value: i32) {
        self.sampler = value;
    }
    pub fn target(&self) -> GltfAnimationChannelTarget {
        self.target.clone()
    }
}

#[derive(Clone, Debug)]
pub struct GltfAnimationChannelTarget {
    node: i32,
    path: GltfAnimationTargetPath,
}
impl Default for GltfAnimationChannelTarget {
    fn default() -> Self {
        Self {
            node: -1,
            path: GltfAnimationTargetPath::Translation,
        }
    }
}
impl GltfAnimationChannelTarget {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn node(&self) -> i32 {
        self.node
    }
    pub fn set_node(&mut self, value: i32) {
        self.node = value;
    }
    pub fn path(&self) -> GltfAnimationTargetPath {
        self.path
    }
    pub fn set_path(&mut self, value: GltfAnimationTargetPath) {
        self.path = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfAnimationSampler {
    input: i32,
    interpolation: GltfAnimationInterpolation,
    output: i32,
}
impl Default for GltfAnimationSampler {
    fn default() -> Self {
        Self {
            input: -1,
            interpolation: GltfAnimationInterpolation::Linear,
            output: -1,
        }
    }
}
impl GltfAnimationSampler {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn input(&self) -> i32 {
        self.input
    }
    pub fn set_input(&mut self, value: i32) {
        self.input = value;
    }
    pub fn interpolation(&self) -> GltfAnimationInterpolation {
        self.interpolation
    }
    pub fn set_interpolation(&mut self, value: GltfAnimationInterpolation) {
        self.interpolation = value;
    }
    pub fn output(&self) -> i32 {
        self.output
    }
    pub fn set_output(&mut self, value: i32) {
        self.output = value;
    }
}

#[derive(Clone, Debug, Default)]
pub struct GltfBuffer {
    byte_length: i32,
    data: Option<Vec<u8>>,
    name: Option<String>,
    uri: Option<String>,
}
impl GltfBuffer {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn byte_length(&self) -> i32 {
        self.byte_length
    }
    pub fn set_byte_length(&mut self, value: i32) {
        self.byte_length = value;
    }
    pub fn data(&self) -> Option<Vec<u8>> {
        self.data.clone()
    }
    pub fn set_data(&mut self, value: Option<Vec<u8>>) {
        self.data = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn uri(&self) -> Option<String> {
        self.uri.clone()
    }
    pub fn set_uri(&mut self, value: Option<String>) {
        self.uri = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfBufferView {
    buffer: i32,
    byte_length: i32,
    byte_offset: i32,
    byte_stride: i32,
    name: Option<String>,
    target: i32,
}
impl Default for GltfBufferView {
    fn default() -> Self {
        Self {
            buffer: -1,
            byte_length: 0,
            byte_offset: 0,
            byte_stride: 0,
            name: None,
            target: -1,
        }
    }
}
impl GltfBufferView {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn buffer(&self) -> i32 {
        self.buffer
    }
    pub fn set_buffer(&mut self, value: i32) {
        self.buffer = value;
    }
    pub fn byte_length(&self) -> i32 {
        self.byte_length
    }
    pub fn set_byte_length(&mut self, value: i32) {
        self.byte_length = value;
    }
    pub fn byte_offset(&self) -> i32 {
        self.byte_offset
    }
    pub fn set_byte_offset(&mut self, value: i32) {
        self.byte_offset = value;
    }
    pub fn byte_stride(&self) -> i32 {
        self.byte_stride
    }
    pub fn set_byte_stride(&mut self, value: i32) {
        self.byte_stride = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn target(&self) -> i32 {
        self.target
    }
    pub fn set_target(&mut self, value: i32) {
        self.target = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfTextureRef {
    index: i32,
    tex_coord: i32,
    transform: GltfTextureTransform,
}
impl Default for GltfTextureRef {
    fn default() -> Self {
        Self {
            index: -1,
            tex_coord: 0,
            transform: GltfTextureTransform::default(),
        }
    }
}
impl GltfTextureRef {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn index(&self) -> i32 {
        self.index
    }
    pub fn set_index(&mut self, value: i32) {
        self.index = value;
    }
    pub fn tex_coord(&self) -> i32 {
        self.tex_coord
    }
    pub fn set_tex_coord(&mut self, value: i32) {
        self.tex_coord = value;
    }
    pub fn transform(&self) -> GltfTextureTransform {
        self.transform
    }
    pub fn set_transform(&mut self, value: GltfTextureTransform) {
        self.transform = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfNormalTextureRef {
    inner: GltfTextureRef,
    scale: f32,
}
impl Default for GltfNormalTextureRef {
    fn default() -> Self {
        Self {
            inner: GltfTextureRef::default(),
            scale: 1.0,
        }
    }
}
impl GltfNormalTextureRef {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn scale(&self) -> f32 {
        self.scale
    }
    pub fn set_scale(&mut self, value: f32) {
        self.scale = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfOcclusionTextureRef {
    inner: GltfTextureRef,
    strength: f32,
}
impl Default for GltfOcclusionTextureRef {
    fn default() -> Self {
        Self {
            inner: GltfTextureRef::default(),
            strength: 1.0,
        }
    }
}
impl GltfOcclusionTextureRef {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn strength(&self) -> f32 {
        self.strength
    }
    pub fn set_strength(&mut self, value: f32) {
        self.strength = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfDocumentMaterial {
    name: Option<String>,
    base_color_factor: Color4,
    base_color_texture: Option<GltfTextureRef>,
    metallic_factor: f32,
    metallic_roughness_texture: Option<GltfTextureRef>,
    roughness_factor: f32,
    normal_texture: Option<GltfNormalTextureRef>,
    occlusion_texture: Option<GltfOcclusionTextureRef>,
    emissive_texture: Option<GltfTextureRef>,
    emissive_factor: Vector3,
    alpha_mode: GltfAlphaMode,
    alpha_cutoff: f32,
    double_sided: bool,
}
impl Default for GltfDocumentMaterial {
    fn default() -> Self {
        Self {
            name: None,
            base_color_factor: Color4::white(),
            base_color_texture: None,
            metallic_factor: 1.0,
            metallic_roughness_texture: None,
            roughness_factor: 1.0,
            normal_texture: None,
            occlusion_texture: None,
            emissive_texture: None,
            emissive_factor: Vector3::zero(),
            alpha_mode: GltfAlphaMode::Opaque,
            alpha_cutoff: 0.5,
            double_sided: false,
        }
    }
}
impl GltfDocumentMaterial {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn base_color_factor(&self) -> Color4 {
        self.base_color_factor
    }
    pub fn set_base_color_factor(&mut self, value: Color4) {
        self.base_color_factor = value;
    }
    pub fn base_color_texture(&self) -> Option<GltfTextureRef> {
        self.base_color_texture.clone()
    }
    pub fn set_base_color_texture(&mut self, value: Option<GltfTextureRef>) {
        self.base_color_texture = value;
    }
    pub fn metallic_factor(&self) -> f32 {
        self.metallic_factor
    }
    pub fn set_metallic_factor(&mut self, value: f32) {
        self.metallic_factor = value;
    }
    pub fn metallic_roughness_texture(&self) -> Option<GltfTextureRef> {
        self.metallic_roughness_texture.clone()
    }
    pub fn set_metallic_roughness_texture(&mut self, value: Option<GltfTextureRef>) {
        self.metallic_roughness_texture = value;
    }
    pub fn roughness_factor(&self) -> f32 {
        self.roughness_factor
    }
    pub fn set_roughness_factor(&mut self, value: f32) {
        self.roughness_factor = value;
    }
    pub fn normal_texture(&self) -> Option<GltfNormalTextureRef> {
        self.normal_texture.clone()
    }
    pub fn set_normal_texture(&mut self, value: Option<GltfNormalTextureRef>) {
        self.normal_texture = value;
    }
    pub fn occlusion_texture(&self) -> Option<GltfOcclusionTextureRef> {
        self.occlusion_texture.clone()
    }
    pub fn set_occlusion_texture(&mut self, value: Option<GltfOcclusionTextureRef>) {
        self.occlusion_texture = value;
    }
    pub fn emissive_texture(&self) -> Option<GltfTextureRef> {
        self.emissive_texture.clone()
    }
    pub fn set_emissive_texture(&mut self, value: Option<GltfTextureRef>) {
        self.emissive_texture = value;
    }
    pub fn emissive_factor(&self) -> Vector3 {
        self.emissive_factor
    }
    pub fn set_emissive_factor(&mut self, value: Vector3) {
        self.emissive_factor = value;
    }
    pub fn alpha_mode(&self) -> GltfAlphaMode {
        self.alpha_mode
    }
    pub fn set_alpha_mode(&mut self, value: GltfAlphaMode) {
        self.alpha_mode = value;
    }
    pub fn alpha_cutoff(&self) -> f32 {
        self.alpha_cutoff
    }
    pub fn set_alpha_cutoff(&mut self, value: f32) {
        self.alpha_cutoff = value;
    }
    pub fn double_sided(&self) -> bool {
        self.double_sided
    }
    pub fn set_double_sided(&mut self, value: bool) {
        self.double_sided = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfImage {
    buffer_view: i32,
    mime_type: Option<String>,
    name: Option<String>,
    uri: Option<String>,
}
impl Default for GltfImage {
    fn default() -> Self {
        Self {
            buffer_view: -1,
            mime_type: None,
            name: None,
            uri: None,
        }
    }
}
impl GltfImage {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn buffer_view(&self) -> i32 {
        self.buffer_view
    }
    pub fn set_buffer_view(&mut self, value: i32) {
        self.buffer_view = value;
    }
    pub fn mime_type(&self) -> Option<String> {
        self.mime_type.clone()
    }
    pub fn set_mime_type(&mut self, value: Option<String>) {
        self.mime_type = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn uri(&self) -> Option<String> {
        self.uri.clone()
    }
    pub fn set_uri(&mut self, value: Option<String>) {
        self.uri = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfPrimitive {
    attributes: HashMap<String, i32>,
    indices: i32,
    material: i32,
    mode: GltfPrimitiveMode,
}
impl Default for GltfPrimitive {
    fn default() -> Self {
        Self {
            attributes: HashMap::new(),
            indices: -1,
            material: -1,
            mode: GltfPrimitiveMode::Triangles,
        }
    }
}
impl GltfPrimitive {
    pub const ATTR_COLOR_0: &'static str = "COLOR_0";
    pub const ATTR_JOINTS_0: &'static str = "JOINTS_0";
    pub const ATTR_NORMAL: &'static str = "NORMAL";
    pub const ATTR_POSITION: &'static str = "POSITION";
    pub const ATTR_TANGENT: &'static str = "TANGENT";
    pub const ATTR_TEXCOORD_0: &'static str = "TEXCOORD_0";
    pub const ATTR_TEXCOORD_1: &'static str = "TEXCOORD_1";
    pub const ATTR_WEIGHTS_0: &'static str = "WEIGHTS_0";
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn attributes(&self) -> HashMap<String, i32> {
        self.attributes.clone()
    }
    pub fn indices(&self) -> i32 {
        self.indices
    }
    pub fn set_indices(&mut self, value: i32) {
        self.indices = value;
    }
    pub fn material(&self) -> i32 {
        self.material
    }
    pub fn set_material(&mut self, value: i32) {
        self.material = value;
    }
    pub fn mode(&self) -> GltfPrimitiveMode {
        self.mode
    }
    pub fn set_mode(&mut self, value: GltfPrimitiveMode) {
        self.mode = value;
    }
}

#[derive(Clone, Debug, Default)]
pub struct GltfMesh {
    name: Option<String>,
    primitives: Vec<GltfPrimitive>,
    weights: Option<Vec<f64>>,
}
impl GltfMesh {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn primitives(&self) -> Vec<GltfPrimitive> {
        self.primitives.clone()
    }
    pub fn weights(&self) -> Option<Vec<f64>> {
        self.weights.clone()
    }
    pub fn set_weights(&mut self, value: Option<Vec<f64>>) {
        self.weights = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfNode {
    children: Vec<i32>,
    matrix: Option<Option<Matrix4>>,
    mesh: i32,
    name: Option<String>,
    rotation: Quaternion,
    scale: Vector3,
    skin: i32,
    translation: Vector3,
    weights: Option<Vec<f64>>,
}
impl Default for GltfNode {
    fn default() -> Self {
        Self {
            children: Vec::new(),
            matrix: None,
            mesh: -1,
            name: None,
            rotation: Quaternion::identity(),
            scale: Vector3::one(),
            skin: -1,
            translation: Vector3::zero(),
            weights: None,
        }
    }
}
impl GltfNode {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn children(&self) -> Vec<i32> {
        self.children.clone()
    }
    pub fn matrix(&self) -> Option<Option<Matrix4>> {
        self.matrix
    }
    pub fn set_matrix(&mut self, value: Option<Option<Matrix4>>) {
        self.matrix = value;
    }
    pub fn mesh(&self) -> i32 {
        self.mesh
    }
    pub fn set_mesh(&mut self, value: i32) {
        self.mesh = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn rotation(&self) -> Quaternion {
        self.rotation
    }
    pub fn set_rotation(&mut self, value: Quaternion) {
        self.rotation = value;
    }
    pub fn scale(&self) -> Vector3 {
        self.scale
    }
    pub fn set_scale(&mut self, value: Vector3) {
        self.scale = value;
    }
    pub fn skin(&self) -> i32 {
        self.skin
    }
    pub fn set_skin(&mut self, value: i32) {
        self.skin = value;
    }
    pub fn translation(&self) -> Vector3 {
        self.translation
    }
    pub fn set_translation(&mut self, value: Vector3) {
        self.translation = value;
    }
    pub fn weights(&self) -> Option<Vec<f64>> {
        self.weights.clone()
    }
    pub fn set_weights(&mut self, value: Option<Vec<f64>>) {
        self.weights = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfSampler {
    mag_filter: GltfSamplerFilter,
    min_filter: GltfSamplerFilter,
    name: Option<String>,
    wrap_s: GltfSamplerWrap,
    wrap_t: GltfSamplerWrap,
}
impl Default for GltfSampler {
    fn default() -> Self {
        Self {
            mag_filter: GltfSamplerFilter::Linear,
            min_filter: GltfSamplerFilter::LinearMipmapLinear,
            name: None,
            wrap_s: GltfSamplerWrap::Repeat,
            wrap_t: GltfSamplerWrap::Repeat,
        }
    }
}
impl GltfSampler {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn mag_filter(&self) -> GltfSamplerFilter {
        self.mag_filter
    }
    pub fn set_mag_filter(&mut self, value: GltfSamplerFilter) {
        self.mag_filter = value;
    }
    pub fn min_filter(&self) -> GltfSamplerFilter {
        self.min_filter
    }
    pub fn set_min_filter(&mut self, value: GltfSamplerFilter) {
        self.min_filter = value;
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn wrap_s(&self) -> GltfSamplerWrap {
        self.wrap_s
    }
    pub fn set_wrap_s(&mut self, value: GltfSamplerWrap) {
        self.wrap_s = value;
    }
    pub fn wrap_t(&self) -> GltfSamplerWrap {
        self.wrap_t
    }
    pub fn set_wrap_t(&mut self, value: GltfSamplerWrap) {
        self.wrap_t = value;
    }
}

#[derive(Clone, Debug, Default)]
pub struct GltfScene {
    name: Option<String>,
    nodes: Vec<i32>,
}
impl GltfScene {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn nodes(&self) -> Vec<i32> {
        self.nodes.clone()
    }
}

#[derive(Clone, Debug)]
pub struct GltfSkin {
    inverse_bind_matrices: i32,
    joints: Vec<i32>,
    name: Option<String>,
    skeleton: i32,
}
impl Default for GltfSkin {
    fn default() -> Self {
        Self {
            inverse_bind_matrices: -1,
            joints: Vec::new(),
            name: None,
            skeleton: -1,
        }
    }
}
impl GltfSkin {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn inverse_bind_matrices(&self) -> i32 {
        self.inverse_bind_matrices
    }
    pub fn set_inverse_bind_matrices(&mut self, value: i32) {
        self.inverse_bind_matrices = value;
    }
    pub fn joints(&self) -> Vec<i32> {
        self.joints.clone()
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn skeleton(&self) -> i32 {
        self.skeleton
    }
    pub fn set_skeleton(&mut self, value: i32) {
        self.skeleton = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfTexture {
    name: Option<String>,
    sampler: i32,
    source: i32,
}
impl Default for GltfTexture {
    fn default() -> Self {
        Self {
            name: None,
            sampler: -1,
            source: -1,
        }
    }
}
impl GltfTexture {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }
    pub fn name(&self) -> Option<String> {
        self.name.clone()
    }
    pub fn set_name(&mut self, value: Option<String>) {
        self.name = value;
    }
    pub fn sampler(&self) -> i32 {
        self.sampler
    }
    pub fn set_sampler(&mut self, value: i32) {
        self.sampler = value;
    }
    pub fn source(&self) -> i32 {
        self.source
    }
    pub fn set_source(&mut self, value: i32) {
        self.source = value;
    }
}

#[derive(Clone, Debug)]
pub struct GltfDocument {
    accessors: Vec<GltfAccessor>,
    animations: Vec<GltfAnimation>,
    buffer_views: Vec<GltfBufferView>,
    buffers: Vec<GltfBuffer>,
    copyright: Option<String>,
    default_scene: i32,
    extensions_required: Vec<String>,
    extensions_used: Vec<String>,
    generator: Option<String>,
    images: Vec<GltfImage>,
    materials: Vec<GltfDocumentMaterial>,
    meshes: Vec<GltfMesh>,
    min_version: Option<String>,
    nodes: Vec<GltfNode>,
    samplers: Vec<GltfSampler>,
    scenes: Vec<GltfScene>,
    skins: Vec<GltfSkin>,
    textures: Vec<GltfTexture>,
    version: String,
}

impl Default for GltfDocument {
    fn default() -> Self {
        Self {
            accessors: Vec::new(),
            animations: Vec::new(),
            buffer_views: Vec::new(),
            buffers: Vec::new(),
            copyright: None,
            default_scene: -1,
            extensions_required: Vec::new(),
            extensions_used: Vec::new(),
            generator: None,
            images: Vec::new(),
            materials: Vec::new(),
            meshes: Vec::new(),
            min_version: None,
            nodes: Vec::new(),
            samplers: Vec::new(),
            scenes: Vec::new(),
            skins: Vec::new(),
            textures: Vec::new(),
            version: "2.0".to_owned(),
        }
    }
}

impl GltfDocument {
    pub fn new() -> Result<Self, Error> {
        Ok(Self::default())
    }

    pub fn load(
        data: Vec<u8>,
        buffer_loader: Option<Box<dyn Fn(String) -> Vec<u8> + Send + Sync>>,
    ) -> Result<Self, Error> {
        if data.len() > MAX_DOCUMENT_BYTES {
            return Err(parse_error("glTF document size limit"));
        }
        if data.starts_with(b"glTF") {
            Self::load_glb(data)
        } else {
            Self::load_gltf(
                String::from_utf8(data).map_err(|_| parse_error("glTF UTF-8"))?,
                buffer_loader,
            )
        }
    }

    pub fn load_glb(data: Vec<u8>) -> Result<Self, Error> {
        if data.len() < 20 || data.len() > MAX_DOCUMENT_BYTES || &data[..4] != b"glTF" {
            return Err(parse_error("GLB header"));
        }
        let version = u32::from_le_bytes(
            data[4..8]
                .try_into()
                .map_err(|_| parse_error("GLB version"))?,
        );
        let length = u32::from_le_bytes(
            data[8..12]
                .try_into()
                .map_err(|_| parse_error("GLB length"))?,
        ) as usize;
        if version != 2 || length != data.len() {
            return Err(parse_error("GLB version or length"));
        }
        let mut offset = 12usize;
        let mut json_chunk = None;
        let mut bin_chunk = None;
        while offset < data.len() {
            let header_end = offset
                .checked_add(8)
                .ok_or_else(|| parse_error("GLB chunk overflow"))?;
            if header_end > data.len() {
                return Err(parse_error("GLB chunk header"));
            }
            let size = u32::from_le_bytes(
                data[offset..offset + 4]
                    .try_into()
                    .map_err(|_| parse_error("GLB chunk size"))?,
            ) as usize;
            let kind = &data[offset + 4..offset + 8];
            offset = header_end;
            let end = offset
                .checked_add(size)
                .ok_or_else(|| parse_error("GLB chunk overflow"))?;
            if end > data.len() || size > MAX_BUFFER_BYTES {
                return Err(parse_error("GLB chunk bounds"));
            }
            match kind {
                b"JSON" if json_chunk.is_none() => json_chunk = Some(&data[offset..end]),
                b"BIN\0" if bin_chunk.is_none() => bin_chunk = Some(data[offset..end].to_vec()),
                _ => {}
            }
            offset = end;
        }
        let json = json_chunk.ok_or_else(|| parse_error("GLB JSON chunk"))?;
        let json = std::str::from_utf8(json)
            .map_err(|_| parse_error("GLB JSON UTF-8"))?
            .trim_end_matches([' ', '\0']);
        let mut document = Self::load_gltf(json.to_owned(), None)?;
        if let Some(bin) = bin_chunk {
            let buffer = document
                .buffers
                .get_mut(0)
                .ok_or_else(|| parse_error("GLB missing buffer"))?;
            if usize_from_i32(buffer.byte_length, "GLB buffer length")? > bin.len() {
                return Err(parse_error("GLB BIN too short"));
            }
            buffer.data = Some(bin);
            buffer.uri = None;
        }
        Ok(document)
    }

    pub fn load_gltf(
        json_text: String,
        buffer_loader: Option<Box<dyn Fn(String) -> Vec<u8> + Send + Sync>>,
    ) -> Result<Self, Error> {
        if json_text.len() > MAX_DOCUMENT_BYTES {
            return Err(parse_error("glTF JSON size limit"));
        }
        let root: Value = serde_json::from_str(&json_text).map_err(|_| parse_error("glTF JSON"))?;
        let object = root
            .as_object()
            .ok_or_else(|| parse_error("glTF root object"))?;
        let mut document = Self::default();
        let asset = object
            .get("asset")
            .and_then(Value::as_object)
            .ok_or_else(|| parse_error("glTF asset"))?;
        document.version = asset
            .get("version")
            .and_then(Value::as_str)
            .ok_or_else(|| parse_error("glTF asset version"))?
            .to_owned();
        if document.version != "2.0" {
            return Err(parse_error("unsupported glTF version"));
        }
        document.generator = optional_string(asset.get("generator"))?;
        document.copyright = optional_string(asset.get("copyright"))?;
        document.min_version = optional_string(asset.get("minVersion"))?;
        document.default_scene = i32_value(object.get("scene"), -1)?;
        document.extensions_required = parse_strings(object.get("extensionsRequired"))?;
        document.extensions_used = parse_strings(object.get("extensionsUsed"))?;
        document.buffer_views = parse_array(object.get("bufferViews"), parse_buffer_view)?;
        document.accessors = parse_array(object.get("accessors"), parse_accessor)?;
        document.images = parse_array(object.get("images"), parse_image)?;
        document.samplers = parse_array(object.get("samplers"), parse_sampler)?;
        document.textures = parse_array(object.get("textures"), parse_texture)?;
        document.materials = parse_array(object.get("materials"), parse_material)?;
        document.meshes = parse_array(object.get("meshes"), parse_mesh)?;
        document.nodes = parse_array(object.get("nodes"), parse_node)?;
        document.scenes = parse_array(object.get("scenes"), parse_scene)?;
        document.skins = parse_array(object.get("skins"), parse_skin)?;
        document.animations = parse_array(object.get("animations"), parse_animation)?;
        document.buffers = parse_array(object.get("buffers"), |v| {
            parse_buffer(v, buffer_loader.as_deref())
        })?;
        document.validate()?;
        Ok(document)
    }

    fn validate(&self) -> Result<(), Error> {
        let mut total = 0usize;
        for buffer in &self.buffers {
            let expected = usize_from_i32(buffer.byte_length, "glTF buffer byteLength")?;
            if expected > MAX_BUFFER_BYTES {
                return Err(parse_error("glTF buffer size limit"));
            }
            if let Some(data) = &buffer.data {
                if data.len() < expected || data.len() > MAX_BUFFER_BYTES {
                    return Err(parse_error("glTF buffer data length"));
                }
                total = total
                    .checked_add(data.len())
                    .ok_or_else(|| parse_error("glTF total buffer overflow"))?;
            }
        }
        if total > MAX_BUFFER_BYTES {
            return Err(parse_error("glTF total buffer size limit"));
        }
        for view in &self.buffer_views {
            let buffer = self
                .buffers
                .get(usize_from_i32(view.buffer, "glTF buffer index")?)
                .ok_or_else(|| parse_error("glTF buffer index"))?;
            let start = usize_from_i32(view.byte_offset, "glTF bufferView offset")?;
            let len = usize_from_i32(view.byte_length, "glTF bufferView length")?;
            let end = start
                .checked_add(len)
                .ok_or_else(|| parse_error("glTF bufferView overflow"))?;
            if end > usize_from_i32(buffer.byte_length, "glTF buffer byteLength")? {
                return Err(parse_error("glTF bufferView bounds"));
            }
        }
        for (index, _) in self.nodes.iter().enumerate() {
            self.validate_node_depth(index, &mut Vec::new(), 0)?;
        }
        Ok(())
    }

    fn validate_node_depth(
        &self,
        index: usize,
        stack: &mut Vec<usize>,
        depth: usize,
    ) -> Result<(), Error> {
        if depth > 256 || stack.contains(&index) {
            return Err(parse_error("glTF node depth or cycle"));
        }
        let node = self
            .nodes
            .get(index)
            .ok_or_else(|| parse_error("glTF node index"))?;
        stack.push(index);
        for child in &node.children {
            self.validate_node_depth(
                usize_from_i32(*child, "glTF child index")?,
                stack,
                depth + 1,
            )?;
        }
        stack.pop();
        Ok(())
    }

    fn accessor_values(&self, index: i32) -> Result<Vec<Vec<f32>>, Error> {
        let accessor = self
            .accessors
            .get(usize_from_i32(index, "glTF accessor index")?)
            .ok_or_else(|| parse_error("glTF accessor index"))?;
        let view = self
            .buffer_views
            .get(usize_from_i32(
                accessor.buffer_view,
                "glTF accessor bufferView",
            )?)
            .ok_or_else(|| parse_error("glTF accessor bufferView"))?;
        let buffer = self
            .buffers
            .get(usize_from_i32(view.buffer, "glTF accessor buffer")?)
            .ok_or_else(|| parse_error("glTF accessor buffer"))?;
        let data = buffer
            .data
            .as_ref()
            .ok_or_else(|| parse_error("glTF unresolved buffer"))?;
        let components = accessor_components(accessor.type_);
        let component_bytes = component_size(accessor.component_type);
        let packed = components
            .checked_mul(component_bytes)
            .ok_or_else(|| parse_error("glTF accessor stride overflow"))?;
        let stride = if view.byte_stride == 0 {
            packed
        } else {
            usize_from_i32(view.byte_stride, "glTF accessor stride")?
        };
        if stride < packed || stride > 252 {
            return Err(parse_error("glTF accessor stride"));
        }
        let base = usize_from_i32(view.byte_offset, "glTF view offset")?
            .checked_add(usize_from_i32(
                accessor.byte_offset,
                "glTF accessor offset",
            )?)
            .ok_or_else(|| parse_error("glTF accessor offset overflow"))?;
        let count = usize_from_i32(accessor.count, "glTF accessor count")?;
        if count > MAX_COLLECTION_ITEMS {
            return Err(parse_error("glTF accessor count limit"));
        }
        let mut result = Vec::with_capacity(count);
        for row in 0..count {
            let start = base
                .checked_add(
                    row.checked_mul(stride)
                        .ok_or_else(|| parse_error("glTF accessor offset overflow"))?,
                )
                .ok_or_else(|| parse_error("glTF accessor offset overflow"))?;
            let end = start
                .checked_add(packed)
                .ok_or_else(|| parse_error("glTF accessor bounds overflow"))?;
            if end > data.len() {
                return Err(parse_error("glTF accessor bounds"));
            }
            let mut values = Vec::with_capacity(components);
            for component in 0..components {
                let offset = start + component * component_bytes;
                values.push(read_component(
                    &data[offset..offset + component_bytes],
                    accessor.component_type,
                    accessor.normalized,
                )?);
            }
            result.push(values);
        }
        Ok(result)
    }

    fn attribute_values(
        &self,
        primitive: &GltfPrimitive,
        semantic: &str,
    ) -> Result<Vec<Vec<f32>>, Error> {
        match primitive.attributes.get(semantic) {
            Some(index) => self.accessor_values(*index),
            None => Ok(Vec::new()),
        }
    }

    pub fn get_positions(&self, primitive: GltfPrimitive) -> Result<Vec<Vector3>, Error> {
        vectors3(self.attribute_values(&primitive, "POSITION")?)
    }
    pub fn get_normals(&self, primitive: GltfPrimitive) -> Result<Vec<Vector3>, Error> {
        vectors3(self.attribute_values(&primitive, "NORMAL")?)
    }
    pub fn get_tangents(&self, primitive: GltfPrimitive) -> Result<Vec<Vector4>, Error> {
        vectors4(self.attribute_values(&primitive, "TANGENT")?)
    }
    pub fn get_tex_coords(
        &self,
        primitive: GltfPrimitive,
        channel: Option<i32>,
    ) -> Result<Vec<Vector2>, Error> {
        vectors2(self.attribute_values(&primitive, &format!("TEXCOORD_{}", channel.unwrap_or(0)))?)
    }
    pub fn get_colors(
        &self,
        primitive: GltfPrimitive,
        set: Option<i32>,
    ) -> Result<Vec<Color4>, Error> {
        self.attribute_values(&primitive, &format!("COLOR_{}", set.unwrap_or(0)))?
            .into_iter()
            .map(|v| {
                if v.len() == 3 {
                    Ok(Color4 {
                        r: v[0],
                        g: v[1],
                        b: v[2],
                        a: 1.0,
                    })
                } else if v.len() == 4 {
                    Ok(Color4 {
                        r: v[0],
                        g: v[1],
                        b: v[2],
                        a: v[3],
                    })
                } else {
                    Err(parse_error("glTF color accessor type"))
                }
            })
            .collect()
    }
    pub fn get_joints(
        &self,
        primitive: GltfPrimitive,
        set: Option<i32>,
    ) -> Result<Vec<Vector4>, Error> {
        vectors4(self.attribute_values(&primitive, &format!("JOINTS_{}", set.unwrap_or(0)))?)
    }
    pub fn get_weights(
        &self,
        primitive: GltfPrimitive,
        set: Option<i32>,
    ) -> Result<Vec<Vector4>, Error> {
        vectors4(self.attribute_values(&primitive, &format!("WEIGHTS_{}", set.unwrap_or(0)))?)
    }
    pub fn get_indices(&self, primitive: GltfPrimitive) -> Result<Vec<i32>, Error> {
        if primitive.indices < 0 {
            return Ok(Vec::new());
        }
        self.accessor_values(primitive.indices)?
            .into_iter()
            .map(|v| {
                if v.len() != 1 || v[0] < 0.0 || v[0] > i32::MAX as f32 {
                    Err(parse_error("glTF index accessor"))
                } else {
                    Ok(v[0] as i32)
                }
            })
            .collect()
    }
    pub fn get_inverse_bind_matrices(&self, skin: GltfSkin) -> Result<Vec<Matrix4>, Error> {
        if skin.inverse_bind_matrices < 0 {
            return Ok(Vec::new());
        }
        self.accessor_values(skin.inverse_bind_matrices)?
            .into_iter()
            .map(matrix_from_gltf)
            .collect()
    }
    pub fn get_sampler_timestamps(&self, sampler: GltfAnimationSampler) -> Result<Vec<f32>, Error> {
        self.accessor_values(sampler.input)?
            .into_iter()
            .map(|v| {
                v.first()
                    .copied()
                    .ok_or_else(|| parse_error("glTF timestamp accessor"))
            })
            .collect()
    }
    pub fn get_sampler_vec3(&self, sampler: GltfAnimationSampler) -> Result<Vec<Vector3>, Error> {
        vectors3(self.accessor_values(sampler.output)?)
    }
    pub fn get_sampler_quaternion(
        &self,
        sampler: GltfAnimationSampler,
    ) -> Result<Vec<Quaternion>, Error> {
        self.accessor_values(sampler.output)?
            .into_iter()
            .map(|v| {
                if v.len() == 4 {
                    Ok(Quaternion {
                        x: v[0],
                        y: v[1],
                        z: v[2],
                        w: v[3],
                    })
                } else {
                    Err(parse_error("glTF quaternion accessor"))
                }
            })
            .collect()
    }
    pub fn get_sampler_weights(&self, sampler: GltfAnimationSampler) -> Result<Vec<f32>, Error> {
        Ok(self
            .accessor_values(sampler.output)?
            .into_iter()
            .flatten()
            .collect())
    }

    pub fn to_json(&self, pretty: Option<bool>) -> Result<String, Error> {
        let value = self.json_value(true)?;
        if pretty.unwrap_or(false) {
            serde_json::to_string_pretty(&value)
        } else {
            serde_json::to_string(&value)
        }
        .map_err(|_| parse_error("glTF JSON serialization"))
    }

    pub fn to_glb(&self) -> Result<Vec<u8>, Error> {
        let mut clone = self.clone();
        let mut binary = Vec::new();
        if !clone.buffers.is_empty() {
            let mut offsets = Vec::with_capacity(clone.buffers.len());
            for buffer in &clone.buffers {
                while binary.len() % 4 != 0 {
                    binary.push(0);
                }
                offsets.push(binary.len());
                let data = buffer
                    .data
                    .as_ref()
                    .ok_or_else(|| parse_error("glTF unresolved buffer"))?;
                binary.extend_from_slice(data);
            }
            for view in &mut clone.buffer_views {
                let buffer_index = usize_from_i32(view.buffer, "GLB buffer view index")?;
                let base = *offsets
                    .get(buffer_index)
                    .ok_or_else(|| parse_error("GLB buffer view index"))?;
                let adjusted = base
                    .checked_add(usize_from_i32(view.byte_offset, "GLB buffer view offset")?)
                    .ok_or_else(|| parse_error("GLB buffer view offset overflow"))?;
                view.buffer = 0;
                view.byte_offset =
                    i32::try_from(adjusted).map_err(|_| parse_error("GLB buffer view offset"))?;
            }
            clone.buffers = vec![GltfBuffer {
                byte_length: i32::try_from(binary.len())
                    .map_err(|_| parse_error("GLB buffer length"))?,
                data: Some(binary.clone()),
                name: clone.buffers[0].name.clone(),
                uri: None,
            }];
        }
        let mut json = serde_json::to_vec(&clone.json_value(false)?)
            .map_err(|_| parse_error("GLB JSON serialization"))?;
        while json.len() % 4 != 0 {
            json.push(b' ');
        }
        while binary.len() % 4 != 0 {
            binary.push(0);
        }
        let total = 12usize
            .checked_add(8 + json.len())
            .and_then(|v| {
                v.checked_add(if binary.is_empty() {
                    0
                } else {
                    8 + binary.len()
                })
            })
            .ok_or_else(|| parse_error("GLB size overflow"))?;
        if total > MAX_DOCUMENT_BYTES {
            return Err(parse_error("GLB size limit"));
        }
        let mut out = Vec::with_capacity(total);
        out.extend_from_slice(b"glTF");
        out.extend_from_slice(&2u32.to_le_bytes());
        out.extend_from_slice(&(total as u32).to_le_bytes());
        out.extend_from_slice(&(json.len() as u32).to_le_bytes());
        out.extend_from_slice(b"JSON");
        out.extend_from_slice(&json);
        if !binary.is_empty() {
            out.extend_from_slice(&(binary.len() as u32).to_le_bytes());
            out.extend_from_slice(b"BIN\0");
            out.extend_from_slice(&binary);
        }
        Ok(out)
    }

    fn json_value(&self, include_data_uris: bool) -> Result<Value, Error> {
        let mut root = Map::new();
        let mut asset = Map::new();
        asset.insert("version".into(), json!(self.version));
        insert_opt(&mut asset, "generator", self.generator.clone());
        insert_opt(&mut asset, "copyright", self.copyright.clone());
        insert_opt(&mut asset, "minVersion", self.min_version.clone());
        root.insert("asset".into(), Value::Object(asset));
        if self.default_scene >= 0 {
            root.insert("scene".into(), json!(self.default_scene));
        }
        insert_strings(&mut root, "extensionsRequired", &self.extensions_required);
        insert_strings(&mut root, "extensionsUsed", &self.extensions_used);
        insert_values(
            &mut root,
            "scenes",
            self.scenes.iter().map(scene_json).collect(),
        );
        insert_values(
            &mut root,
            "nodes",
            self.nodes.iter().map(node_json).collect(),
        );
        insert_values(
            &mut root,
            "meshes",
            self.meshes.iter().map(mesh_json).collect(),
        );
        insert_values(
            &mut root,
            "materials",
            self.materials.iter().map(material_json).collect(),
        );
        insert_values(
            &mut root,
            "images",
            self.images.iter().map(image_json).collect(),
        );
        insert_values(
            &mut root,
            "samplers",
            self.samplers.iter().map(sampler_json).collect(),
        );
        insert_values(
            &mut root,
            "textures",
            self.textures.iter().map(texture_json).collect(),
        );
        insert_values(
            &mut root,
            "skins",
            self.skins.iter().map(skin_json).collect(),
        );
        insert_values(
            &mut root,
            "animations",
            self.animations.iter().map(animation_json).collect(),
        );
        insert_values(
            &mut root,
            "accessors",
            self.accessors.iter().map(accessor_json).collect(),
        );
        insert_values(
            &mut root,
            "bufferViews",
            self.buffer_views.iter().map(buffer_view_json).collect(),
        );
        let buffers = self
            .buffers
            .iter()
            .map(|b| buffer_json(b, include_data_uris))
            .collect::<Result<Vec<_>, _>>()?;
        insert_values(&mut root, "buffers", buffers);
        Ok(Value::Object(root))
    }

    pub fn accessors(&self) -> Vec<GltfAccessor> {
        self.accessors.clone()
    }
    pub fn animations(&self) -> Vec<GltfAnimation> {
        self.animations.clone()
    }
    pub fn buffer_views(&self) -> Vec<GltfBufferView> {
        self.buffer_views.clone()
    }
    pub fn buffers(&self) -> Vec<GltfBuffer> {
        self.buffers.clone()
    }
    pub fn copyright(&self) -> Option<String> {
        self.copyright.clone()
    }
    pub fn set_copyright(&mut self, value: Option<String>) {
        self.copyright = value;
    }
    pub fn default_scene(&self) -> i32 {
        self.default_scene
    }
    pub fn set_default_scene(&mut self, value: i32) {
        self.default_scene = value;
    }
    pub fn extensions_required(&self) -> Vec<String> {
        self.extensions_required.clone()
    }
    pub fn extensions_used(&self) -> Vec<String> {
        self.extensions_used.clone()
    }
    pub fn generator(&self) -> Option<String> {
        self.generator.clone()
    }
    pub fn set_generator(&mut self, value: Option<String>) {
        self.generator = value;
    }
    pub fn images(&self) -> Vec<GltfImage> {
        self.images.clone()
    }
    pub fn materials(&self) -> Vec<GltfDocumentMaterial> {
        self.materials.clone()
    }
    pub fn meshes(&self) -> Vec<GltfMesh> {
        self.meshes.clone()
    }
    pub fn min_version(&self) -> Option<String> {
        self.min_version.clone()
    }
    pub fn set_min_version(&mut self, value: Option<String>) {
        self.min_version = value;
    }
    pub fn nodes(&self) -> Vec<GltfNode> {
        self.nodes.clone()
    }
    pub fn samplers(&self) -> Vec<GltfSampler> {
        self.samplers.clone()
    }
    pub fn scenes(&self) -> Vec<GltfScene> {
        self.scenes.clone()
    }
    pub fn skins(&self) -> Vec<GltfSkin> {
        self.skins.clone()
    }
    pub fn textures(&self) -> Vec<GltfTexture> {
        self.textures.clone()
    }
    pub fn version(&self) -> String {
        self.version.clone()
    }
    pub fn set_version(&mut self, value: String) {
        self.version = value;
    }
}

fn parse_array<T>(
    value: Option<&Value>,
    parser: impl Fn(&Value) -> Result<T, Error>,
) -> Result<Vec<T>, Error> {
    let Some(value) = value else {
        return Ok(Vec::new());
    };
    let array = value
        .as_array()
        .ok_or_else(|| parse_error("glTF collection"))?;
    if array.len() > MAX_COLLECTION_ITEMS {
        return Err(parse_error("glTF collection limit"));
    }
    array.iter().map(parser).collect()
}
fn parse_strings(value: Option<&Value>) -> Result<Vec<String>, Error> {
    parse_array(value, |v| {
        v.as_str()
            .map(str::to_owned)
            .ok_or_else(|| parse_error("glTF string collection"))
    })
}
fn object(value: &Value) -> Result<&Map<String, Value>, Error> {
    value.as_object().ok_or_else(|| parse_error("glTF object"))
}

fn parse_buffer(
    value: &Value,
    loader: Option<&(dyn Fn(String) -> Vec<u8> + Send + Sync)>,
) -> Result<GltfBuffer, Error> {
    let o = object(value)?;
    let byte_length = i32_value(o.get("byteLength"), 0)?;
    let uri = optional_string(o.get("uri"))?;
    let data = if let Some(uri_value) = &uri {
        if let Some(encoded) = uri_value
            .strip_prefix("data:")
            .and_then(|v| v.split_once(','))
            .filter(|(meta, _)| meta.ends_with(";base64"))
            .map(|(_, data)| data)
        {
            Some(
                base64::engine::general_purpose::STANDARD
                    .decode(encoded)
                    .map_err(|_| parse_error("glTF base64 buffer"))?,
            )
        } else {
            loader.map(|resolve| resolve(uri_value.clone()))
        }
    } else {
        None
    };
    Ok(GltfBuffer {
        byte_length,
        data,
        name: optional_string(o.get("name"))?,
        uri,
    })
}
fn parse_buffer_view(value: &Value) -> Result<GltfBufferView, Error> {
    let o = object(value)?;
    Ok(GltfBufferView {
        buffer: i32_value(o.get("buffer"), -1)?,
        byte_length: i32_value(o.get("byteLength"), 0)?,
        byte_offset: i32_value(o.get("byteOffset"), 0)?,
        byte_stride: i32_value(o.get("byteStride"), 0)?,
        name: optional_string(o.get("name"))?,
        target: i32_value(o.get("target"), -1)?,
    })
}
fn parse_accessor(value: &Value) -> Result<GltfAccessor, Error> {
    let o = object(value)?;
    Ok(GltfAccessor {
        buffer_view: i32_value(o.get("bufferView"), -1)?,
        byte_offset: i32_value(o.get("byteOffset"), 0)?,
        component_type: component_type(i32_value(o.get("componentType"), 5126)?)?,
        normalized: o
            .get("normalized")
            .and_then(Value::as_bool)
            .unwrap_or(false),
        count: i32_value(o.get("count"), 0)?,
        type_: accessor_type(o.get("type").and_then(Value::as_str).unwrap_or("SCALAR"))?,
        max: number_array(o.get("max"))?,
        min: number_array(o.get("min"))?,
        name: optional_string(o.get("name"))?,
    })
}
fn parse_image(value: &Value) -> Result<GltfImage, Error> {
    let o = object(value)?;
    Ok(GltfImage {
        buffer_view: i32_value(o.get("bufferView"), -1)?,
        mime_type: optional_string(o.get("mimeType"))?,
        name: optional_string(o.get("name"))?,
        uri: optional_string(o.get("uri"))?,
    })
}
fn parse_texture_ref(value: Option<&Value>) -> Result<Option<GltfTextureRef>, Error> {
    let Some(value) = value else { return Ok(None) };
    let o = object(value)?;
    let extension = o
        .get("extensions")
        .and_then(Value::as_object)
        .and_then(|extensions| extensions.get("KHR_texture_transform"))
        .and_then(Value::as_object);
    let offset = finite_vec(
        extension.and_then(|value| value.get("offset")),
        2,
        &[0.0, 0.0],
    )?;
    let scale = finite_vec(
        extension.and_then(|value| value.get("scale")),
        2,
        &[1.0, 1.0],
    )?;
    Ok(Some(GltfTextureRef {
        index: i32_value(o.get("index"), -1)?,
        tex_coord: i32_value(
            extension
                .and_then(|value| value.get("texCoord"))
                .or_else(|| o.get("texCoord")),
            0,
        )?,
        transform: GltfTextureTransform {
            offset: Vector2 {
                x: offset[0],
                y: offset[1],
            },
            rotation: f32_value(extension.and_then(|value| value.get("rotation")), 0.0)?,
            scale: Vector2 {
                x: scale[0],
                y: scale[1],
            },
        },
    }))
}
fn parse_material(value: &Value) -> Result<GltfDocumentMaterial, Error> {
    let o = object(value)?;
    let pbr = o.get("pbrMetallicRoughness").and_then(Value::as_object);
    let color = finite_vec(pbr.and_then(|p| p.get("baseColorFactor")), 4, &[1.0; 4])?;
    let emissive = finite_vec(o.get("emissiveFactor"), 3, &[0.0; 3])?;
    let normal_texture = if let Some(v) = o.get("normalTexture") {
        let no = object(v)?;
        Some(GltfNormalTextureRef {
            inner: parse_texture_ref(Some(v))?.unwrap_or_default(),
            scale: f32_value(no.get("scale"), 1.0)?,
        })
    } else {
        None
    };
    let occlusion_texture = if let Some(v) = o.get("occlusionTexture") {
        let oo = object(v)?;
        Some(GltfOcclusionTextureRef {
            inner: parse_texture_ref(Some(v))?.unwrap_or_default(),
            strength: f32_value(oo.get("strength"), 1.0)?,
        })
    } else {
        None
    };
    Ok(GltfDocumentMaterial {
        name: optional_string(o.get("name"))?,
        base_color_factor: Color4 {
            r: color[0],
            g: color[1],
            b: color[2],
            a: color[3],
        },
        base_color_texture: parse_texture_ref(pbr.and_then(|p| p.get("baseColorTexture")))?,
        metallic_factor: f32_value(pbr.and_then(|p| p.get("metallicFactor")), 1.0)?,
        metallic_roughness_texture: parse_texture_ref(
            pbr.and_then(|p| p.get("metallicRoughnessTexture")),
        )?,
        roughness_factor: f32_value(pbr.and_then(|p| p.get("roughnessFactor")), 1.0)?,
        normal_texture,
        occlusion_texture,
        emissive_texture: parse_texture_ref(o.get("emissiveTexture"))?,
        emissive_factor: Vector3 {
            x: emissive[0],
            y: emissive[1],
            z: emissive[2],
        },
        alpha_mode: match o
            .get("alphaMode")
            .and_then(Value::as_str)
            .unwrap_or("OPAQUE")
        {
            "BLEND" => GltfAlphaMode::Blend,
            "MASK" => GltfAlphaMode::Mask,
            "OPAQUE" => GltfAlphaMode::Opaque,
            _ => return Err(parse_error("glTF alpha mode")),
        },
        alpha_cutoff: f32_value(o.get("alphaCutoff"), 0.5)?,
        double_sided: o
            .get("doubleSided")
            .and_then(Value::as_bool)
            .unwrap_or(false),
    })
}
fn parse_primitive(value: &Value) -> Result<GltfPrimitive, Error> {
    let o = object(value)?;
    let attrs = o
        .get("attributes")
        .and_then(Value::as_object)
        .ok_or_else(|| parse_error("glTF primitive attributes"))?;
    let mut attributes = HashMap::with_capacity(attrs.len());
    for (k, v) in attrs {
        attributes.insert(k.clone(), i32_value(Some(v), -1)?);
    }
    Ok(GltfPrimitive {
        attributes,
        indices: i32_value(o.get("indices"), -1)?,
        material: i32_value(o.get("material"), -1)?,
        mode: primitive_mode(i32_value(o.get("mode"), 4)?)?,
    })
}
fn parse_mesh(value: &Value) -> Result<GltfMesh, Error> {
    let o = object(value)?;
    Ok(GltfMesh {
        name: optional_string(o.get("name"))?,
        primitives: parse_array(o.get("primitives"), parse_primitive)?,
        weights: number_array(o.get("weights"))?,
    })
}
fn parse_node(value: &Value) -> Result<GltfNode, Error> {
    let o = object(value)?;
    let t = finite_vec(o.get("translation"), 3, &[0.0; 3])?;
    let r = finite_vec(o.get("rotation"), 4, &[0.0, 0.0, 0.0, 1.0])?;
    let s = finite_vec(o.get("scale"), 3, &[1.0; 3])?;
    let matrix = if o.contains_key("matrix") {
        Some(Some(matrix_from_gltf(finite_vec(
            o.get("matrix"),
            16,
            &[],
        )?)?))
    } else {
        None
    };
    Ok(GltfNode {
        children: int_array(o.get("children"))?,
        matrix,
        mesh: i32_value(o.get("mesh"), -1)?,
        name: optional_string(o.get("name"))?,
        rotation: Quaternion {
            x: r[0],
            y: r[1],
            z: r[2],
            w: r[3],
        },
        scale: Vector3 {
            x: s[0],
            y: s[1],
            z: s[2],
        },
        skin: i32_value(o.get("skin"), -1)?,
        translation: Vector3 {
            x: t[0],
            y: t[1],
            z: t[2],
        },
        weights: number_array(o.get("weights"))?,
    })
}
fn parse_scene(value: &Value) -> Result<GltfScene, Error> {
    let o = object(value)?;
    Ok(GltfScene {
        name: optional_string(o.get("name"))?,
        nodes: int_array(o.get("nodes"))?,
    })
}
fn parse_skin(value: &Value) -> Result<GltfSkin, Error> {
    let o = object(value)?;
    Ok(GltfSkin {
        inverse_bind_matrices: i32_value(o.get("inverseBindMatrices"), -1)?,
        joints: int_array(o.get("joints"))?,
        name: optional_string(o.get("name"))?,
        skeleton: i32_value(o.get("skeleton"), -1)?,
    })
}
fn parse_sampler(value: &Value) -> Result<GltfSampler, Error> {
    let o = object(value)?;
    Ok(GltfSampler {
        mag_filter: sampler_filter(i32_value(o.get("magFilter"), 9729)?)?,
        min_filter: sampler_filter(i32_value(o.get("minFilter"), 9987)?)?,
        name: optional_string(o.get("name"))?,
        wrap_s: sampler_wrap(i32_value(o.get("wrapS"), 10497)?)?,
        wrap_t: sampler_wrap(i32_value(o.get("wrapT"), 10497)?)?,
    })
}
fn parse_texture(value: &Value) -> Result<GltfTexture, Error> {
    let o = object(value)?;
    Ok(GltfTexture {
        name: optional_string(o.get("name"))?,
        sampler: i32_value(o.get("sampler"), -1)?,
        source: i32_value(o.get("source"), -1)?,
    })
}
fn parse_animation(value: &Value) -> Result<GltfAnimation, Error> {
    let o = object(value)?;
    let channels = parse_array(o.get("channels"), |v| {
        let c = object(v)?;
        let t = object(
            c.get("target")
                .ok_or_else(|| parse_error("glTF animation target"))?,
        )?;
        Ok(GltfAnimationChannel {
            sampler: i32_value(c.get("sampler"), -1)?,
            target: GltfAnimationChannelTarget {
                node: i32_value(t.get("node"), -1)?,
                path: match t
                    .get("path")
                    .and_then(Value::as_str)
                    .unwrap_or("translation")
                {
                    "translation" => GltfAnimationTargetPath::Translation,
                    "rotation" => GltfAnimationTargetPath::Rotation,
                    "scale" => GltfAnimationTargetPath::Scale,
                    "weights" => GltfAnimationTargetPath::Weights,
                    _ => return Err(parse_error("glTF animation path")),
                },
            },
        })
    })?;
    let samplers = parse_array(o.get("samplers"), |v| {
        let s = object(v)?;
        Ok(GltfAnimationSampler {
            input: i32_value(s.get("input"), -1)?,
            interpolation: match s
                .get("interpolation")
                .and_then(Value::as_str)
                .unwrap_or("LINEAR")
            {
                "LINEAR" => GltfAnimationInterpolation::Linear,
                "STEP" => GltfAnimationInterpolation::Step,
                "CUBICSPLINE" => GltfAnimationInterpolation::CubicSpline,
                _ => return Err(parse_error("glTF interpolation")),
            },
            output: i32_value(s.get("output"), -1)?,
        })
    })?;
    Ok(GltfAnimation {
        channels,
        name: optional_string(o.get("name"))?,
        samplers,
    })
}

fn component_type(v: i32) -> Result<GltfComponentType, Error> {
    match v {
        5120 => Ok(GltfComponentType::Byte),
        5121 => Ok(GltfComponentType::UnsignedByte),
        5122 => Ok(GltfComponentType::Short),
        5123 => Ok(GltfComponentType::UnsignedShort),
        5125 => Ok(GltfComponentType::UnsignedInt),
        5126 => Ok(GltfComponentType::Float),
        _ => Err(parse_error("glTF component type")),
    }
}
fn accessor_type(v: &str) -> Result<GltfAccessorType, Error> {
    match v {
        "SCALAR" => Ok(GltfAccessorType::Scalar),
        "VEC2" => Ok(GltfAccessorType::Vec2),
        "VEC3" => Ok(GltfAccessorType::Vec3),
        "VEC4" => Ok(GltfAccessorType::Vec4),
        "MAT2" => Ok(GltfAccessorType::Mat2),
        "MAT3" => Ok(GltfAccessorType::Mat3),
        "MAT4" => Ok(GltfAccessorType::Mat4),
        _ => Err(parse_error("glTF accessor type")),
    }
}
fn primitive_mode(v: i32) -> Result<GltfPrimitiveMode, Error> {
    match v {
        0 => Ok(GltfPrimitiveMode::Points),
        1 => Ok(GltfPrimitiveMode::Lines),
        2 => Ok(GltfPrimitiveMode::LineLoop),
        3 => Ok(GltfPrimitiveMode::LineStrip),
        4 => Ok(GltfPrimitiveMode::Triangles),
        5 => Ok(GltfPrimitiveMode::TriangleStrip),
        6 => Ok(GltfPrimitiveMode::TriangleFan),
        _ => Err(parse_error("glTF primitive mode")),
    }
}
fn sampler_filter(v: i32) -> Result<GltfSamplerFilter, Error> {
    match v {
        0 => Ok(GltfSamplerFilter::None),
        9728 => Ok(GltfSamplerFilter::Nearest),
        9729 => Ok(GltfSamplerFilter::Linear),
        9984 => Ok(GltfSamplerFilter::NearestMipmapNearest),
        9985 => Ok(GltfSamplerFilter::LinearMipmapNearest),
        9986 => Ok(GltfSamplerFilter::NearestMipmapLinear),
        9987 => Ok(GltfSamplerFilter::LinearMipmapLinear),
        _ => Err(parse_error("glTF sampler filter")),
    }
}
fn sampler_wrap(v: i32) -> Result<GltfSamplerWrap, Error> {
    match v {
        33071 => Ok(GltfSamplerWrap::ClampToEdge),
        33648 => Ok(GltfSamplerWrap::MirroredRepeat),
        10497 => Ok(GltfSamplerWrap::Repeat),
        _ => Err(parse_error("glTF sampler wrap")),
    }
}
fn component_size(v: GltfComponentType) -> usize {
    match v {
        GltfComponentType::Byte | GltfComponentType::UnsignedByte => 1,
        GltfComponentType::Short | GltfComponentType::UnsignedShort => 2,
        GltfComponentType::UnsignedInt | GltfComponentType::Float => 4,
    }
}
fn accessor_components(v: GltfAccessorType) -> usize {
    match v {
        GltfAccessorType::Scalar => 1,
        GltfAccessorType::Vec2 => 2,
        GltfAccessorType::Vec3 => 3,
        GltfAccessorType::Vec4 | GltfAccessorType::Mat2 => 4,
        GltfAccessorType::Mat3 => 9,
        GltfAccessorType::Mat4 => 16,
    }
}
fn read_component(bytes: &[u8], kind: GltfComponentType, normalized: bool) -> Result<f32, Error> {
    let value = match kind {
        GltfComponentType::Byte => {
            let v = i8::from_le_bytes([bytes[0]]);
            if normalized {
                (f32::from(v) / 127.0).max(-1.0)
            } else {
                f32::from(v)
            }
        }
        GltfComponentType::UnsignedByte => {
            let v = bytes[0];
            if normalized {
                f32::from(v) / 255.0
            } else {
                f32::from(v)
            }
        }
        GltfComponentType::Short => {
            let v = i16::from_le_bytes(
                bytes
                    .try_into()
                    .map_err(|_| parse_error("glTF component"))?,
            );
            if normalized {
                (f32::from(v) / 32767.0).max(-1.0)
            } else {
                f32::from(v)
            }
        }
        GltfComponentType::UnsignedShort => {
            let v = u16::from_le_bytes(
                bytes
                    .try_into()
                    .map_err(|_| parse_error("glTF component"))?,
            );
            if normalized {
                f32::from(v) / 65535.0
            } else {
                f32::from(v)
            }
        }
        GltfComponentType::UnsignedInt => {
            let v = u32::from_le_bytes(
                bytes
                    .try_into()
                    .map_err(|_| parse_error("glTF component"))?,
            );
            if normalized {
                v as f32 / u32::MAX as f32
            } else {
                v as f32
            }
        }
        GltfComponentType::Float => f32::from_le_bytes(
            bytes
                .try_into()
                .map_err(|_| parse_error("glTF component"))?,
        ),
    };
    if value.is_finite() {
        Ok(value)
    } else {
        Err(parse_error("glTF non-finite component"))
    }
}
fn vectors2(rows: Vec<Vec<f32>>) -> Result<Vec<Vector2>, Error> {
    rows.into_iter()
        .map(|v| {
            if v.len() == 2 {
                Ok(Vector2 { x: v[0], y: v[1] })
            } else {
                Err(parse_error("glTF VEC2 accessor"))
            }
        })
        .collect()
}
fn vectors3(rows: Vec<Vec<f32>>) -> Result<Vec<Vector3>, Error> {
    rows.into_iter()
        .map(|v| {
            if v.len() == 3 {
                Ok(Vector3 {
                    x: v[0],
                    y: v[1],
                    z: v[2],
                })
            } else {
                Err(parse_error("glTF VEC3 accessor"))
            }
        })
        .collect()
}
fn vectors4(rows: Vec<Vec<f32>>) -> Result<Vec<Vector4>, Error> {
    rows.into_iter()
        .map(|v| {
            if v.len() == 4 {
                Ok(Vector4 {
                    x: v[0],
                    y: v[1],
                    z: v[2],
                    w: v[3],
                })
            } else {
                Err(parse_error("glTF VEC4 accessor"))
            }
        })
        .collect()
}
fn matrix_from_gltf(v: Vec<f32>) -> Result<Matrix4, Error> {
    if v.len() != 16 {
        return Err(parse_error("glTF MAT4 accessor"));
    }
    Ok(Matrix4 {
        m11: v[0],
        m12: v[4],
        m13: v[8],
        m14: v[12],
        m21: v[1],
        m22: v[5],
        m23: v[9],
        m24: v[13],
        m31: v[2],
        m32: v[6],
        m33: v[10],
        m34: v[14],
        m41: v[3],
        m42: v[7],
        m43: v[11],
        m44: v[15],
    })
}

fn insert_opt(map: &mut Map<String, Value>, key: &str, value: Option<String>) {
    if let Some(value) = value {
        map.insert(key.into(), json!(value));
    }
}
fn insert_strings(map: &mut Map<String, Value>, key: &str, values: &[String]) {
    if !values.is_empty() {
        map.insert(key.into(), json!(values));
    }
}
fn insert_values(map: &mut Map<String, Value>, key: &str, values: Vec<Value>) {
    if !values.is_empty() {
        map.insert(key.into(), Value::Array(values));
    }
}
fn scene_json(v: &GltfScene) -> Value {
    let mut o = Map::new();
    insert_opt(&mut o, "name", v.name.clone());
    if !v.nodes.is_empty() {
        o.insert("nodes".into(), json!(v.nodes));
    }
    Value::Object(o)
}
fn node_json(v: &GltfNode) -> Value {
    let mut o = Map::new();
    insert_opt(&mut o, "name", v.name.clone());
    if !v.children.is_empty() {
        o.insert("children".into(), json!(v.children));
    }
    if v.mesh >= 0 {
        o.insert("mesh".into(), json!(v.mesh));
    }
    if v.skin >= 0 {
        o.insert("skin".into(), json!(v.skin));
    }
    if let Some(Some(m)) = v.matrix {
        o.insert(
            "matrix".into(),
            json!([
                m.m11, m.m21, m.m31, m.m41, m.m12, m.m22, m.m32, m.m42, m.m13, m.m23, m.m33, m.m43,
                m.m14, m.m24, m.m34, m.m44
            ]),
        );
    } else {
        if v.translation != Vector3::zero() {
            o.insert(
                "translation".into(),
                json!([v.translation.x, v.translation.y, v.translation.z]),
            );
        }
        if v.rotation != Quaternion::identity() {
            o.insert(
                "rotation".into(),
                json!([v.rotation.x, v.rotation.y, v.rotation.z, v.rotation.w]),
            );
        }
        if v.scale != Vector3::one() {
            o.insert("scale".into(), json!([v.scale.x, v.scale.y, v.scale.z]));
        }
    }
    if let Some(w) = &v.weights {
        o.insert("weights".into(), json!(w));
    }
    Value::Object(o)
}
fn primitive_json(v: &GltfPrimitive) -> Value {
    let mut attrs = Map::new();
    let mut keys = v.attributes.keys().collect::<Vec<_>>();
    keys.sort();
    for k in keys {
        attrs.insert(k.clone(), json!(v.attributes[k]));
    }
    let mut o = Map::new();
    o.insert("attributes".into(), Value::Object(attrs));
    if v.indices >= 0 {
        o.insert("indices".into(), json!(v.indices));
    }
    if v.material >= 0 {
        o.insert("material".into(), json!(v.material));
    }
    if v.mode != GltfPrimitiveMode::Triangles {
        o.insert("mode".into(), json!(v.mode as u8));
    }
    Value::Object(o)
}
fn mesh_json(v: &GltfMesh) -> Value {
    let mut o = Map::new();
    insert_opt(&mut o, "name", v.name.clone());
    o.insert(
        "primitives".into(),
        Value::Array(v.primitives.iter().map(primitive_json).collect()),
    );
    if let Some(w) = &v.weights {
        o.insert("weights".into(), json!(w));
    }
    Value::Object(o)
}
fn texture_ref_json(v: &GltfTextureRef) -> Value {
    let mut object = Map::new();
    object.insert("index".into(), json!(v.index));
    if v.tex_coord != 0 {
        object.insert("texCoord".into(), json!(v.tex_coord));
    }
    if !v.transform.is_default() {
        object.insert(
            "extensions".into(),
            json!({"KHR_texture_transform": {
                "offset": [v.transform.offset.x, v.transform.offset.y],
                "rotation": v.transform.rotation,
                "scale": [v.transform.scale.x, v.transform.scale.y],
                "texCoord": v.tex_coord
            }}),
        );
    }
    Value::Object(object)
}
fn material_json(v: &GltfDocumentMaterial) -> Value {
    let mut o = Map::new();
    insert_opt(&mut o, "name", v.name.clone());
    let mut p = Map::new();
    p.insert(
        "baseColorFactor".into(),
        json!([
            v.base_color_factor.r,
            v.base_color_factor.g,
            v.base_color_factor.b,
            v.base_color_factor.a
        ]),
    );
    p.insert("metallicFactor".into(), json!(v.metallic_factor));
    p.insert("roughnessFactor".into(), json!(v.roughness_factor));
    if let Some(t) = &v.base_color_texture {
        p.insert("baseColorTexture".into(), texture_ref_json(t));
    }
    if let Some(t) = &v.metallic_roughness_texture {
        p.insert("metallicRoughnessTexture".into(), texture_ref_json(t));
    }
    o.insert("pbrMetallicRoughness".into(), Value::Object(p));
    if let Some(texture) = &v.normal_texture {
        let mut reference = texture_ref_json(&texture.inner)
            .as_object()
            .cloned()
            .unwrap_or_default();
        reference.insert("scale".into(), json!(texture.scale));
        o.insert("normalTexture".into(), Value::Object(reference));
    }
    if let Some(texture) = &v.occlusion_texture {
        let mut reference = texture_ref_json(&texture.inner)
            .as_object()
            .cloned()
            .unwrap_or_default();
        reference.insert("strength".into(), json!(texture.strength));
        o.insert("occlusionTexture".into(), Value::Object(reference));
    }
    if let Some(texture) = &v.emissive_texture {
        o.insert("emissiveTexture".into(), texture_ref_json(texture));
    }
    o.insert(
        "emissiveFactor".into(),
        json!([
            v.emissive_factor.x,
            v.emissive_factor.y,
            v.emissive_factor.z
        ]),
    );
    o.insert(
        "alphaMode".into(),
        json!(match v.alpha_mode {
            GltfAlphaMode::Opaque => "OPAQUE",
            GltfAlphaMode::Blend => "BLEND",
            GltfAlphaMode::Mask => "MASK",
        }),
    );
    o.insert("alphaCutoff".into(), json!(v.alpha_cutoff));
    o.insert("doubleSided".into(), json!(v.double_sided));
    Value::Object(o)
}
fn image_json(v: &GltfImage) -> Value {
    let mut object = Map::new();
    insert_opt(&mut object, "name", v.name.clone());
    insert_opt(&mut object, "uri", v.uri.clone());
    insert_opt(&mut object, "mimeType", v.mime_type.clone());
    if v.buffer_view >= 0 {
        object.insert("bufferView".into(), json!(v.buffer_view));
    }
    Value::Object(object)
}
fn sampler_json(v: &GltfSampler) -> Value {
    let mut object = Map::new();
    insert_opt(&mut object, "name", v.name.clone());
    if v.mag_filter != GltfSamplerFilter::None {
        object.insert("magFilter".into(), json!(v.mag_filter as i32));
    }
    if v.min_filter != GltfSamplerFilter::None {
        object.insert("minFilter".into(), json!(v.min_filter as i32));
    }
    object.insert("wrapS".into(), json!(v.wrap_s as i32));
    object.insert("wrapT".into(), json!(v.wrap_t as i32));
    Value::Object(object)
}
fn texture_json(v: &GltfTexture) -> Value {
    let mut object = Map::new();
    insert_opt(&mut object, "name", v.name.clone());
    if v.sampler >= 0 {
        object.insert("sampler".into(), json!(v.sampler));
    }
    if v.source >= 0 {
        object.insert("source".into(), json!(v.source));
    }
    Value::Object(object)
}
fn skin_json(v: &GltfSkin) -> Value {
    let mut object = Map::new();
    insert_opt(&mut object, "name", v.name.clone());
    if v.inverse_bind_matrices >= 0 {
        object.insert("inverseBindMatrices".into(), json!(v.inverse_bind_matrices));
    }
    if v.skeleton >= 0 {
        object.insert("skeleton".into(), json!(v.skeleton));
    }
    object.insert("joints".into(), json!(v.joints));
    Value::Object(object)
}
fn animation_json(v: &GltfAnimation) -> Value {
    let mut object = Map::new();
    insert_opt(&mut object, "name", v.name.clone());
    object.insert(
        "channels".into(),
        Value::Array(
            v.channels
                .iter()
                .map(|channel| {
                    json!({
                        "sampler": channel.sampler,
                        "target": {
                            "node": channel.target.node,
                            "path": match channel.target.path {
                                GltfAnimationTargetPath::Translation => "translation",
                                GltfAnimationTargetPath::Rotation => "rotation",
                                GltfAnimationTargetPath::Scale => "scale",
                                GltfAnimationTargetPath::Weights => "weights",
                            }
                        }
                    })
                })
                .collect(),
        ),
    );
    object.insert(
        "samplers".into(),
        Value::Array(
            v.samplers
                .iter()
                .map(|sampler| {
                    json!({
                        "input": sampler.input,
                        "interpolation": match sampler.interpolation {
                            GltfAnimationInterpolation::Linear => "LINEAR",
                            GltfAnimationInterpolation::Step => "STEP",
                            GltfAnimationInterpolation::CubicSpline => "CUBICSPLINE",
                        },
                        "output": sampler.output
                    })
                })
                .collect(),
        ),
    );
    Value::Object(object)
}
fn accessor_json(v: &GltfAccessor) -> Value {
    let mut o = Map::new();
    if v.buffer_view >= 0 {
        o.insert("bufferView".into(), json!(v.buffer_view));
    }
    if v.byte_offset != 0 {
        o.insert("byteOffset".into(), json!(v.byte_offset));
    }
    o.insert("componentType".into(), json!(v.component_type as i32));
    o.insert("count".into(), json!(v.count));
    o.insert(
        "type".into(),
        json!(match v.type_ {
            GltfAccessorType::Scalar => "SCALAR",
            GltfAccessorType::Vec2 => "VEC2",
            GltfAccessorType::Vec3 => "VEC3",
            GltfAccessorType::Vec4 => "VEC4",
            GltfAccessorType::Mat2 => "MAT2",
            GltfAccessorType::Mat3 => "MAT3",
            GltfAccessorType::Mat4 => "MAT4",
        }),
    );
    if v.normalized {
        o.insert("normalized".into(), json!(true));
    }
    if let Some(x) = &v.max {
        o.insert("max".into(), json!(x));
    }
    if let Some(x) = &v.min {
        o.insert("min".into(), json!(x));
    }
    insert_opt(&mut o, "name", v.name.clone());
    Value::Object(o)
}
fn buffer_view_json(v: &GltfBufferView) -> Value {
    let mut o = Map::new();
    o.insert("buffer".into(), json!(v.buffer));
    o.insert("byteLength".into(), json!(v.byte_length));
    if v.byte_offset != 0 {
        o.insert("byteOffset".into(), json!(v.byte_offset));
    }
    if v.byte_stride != 0 {
        o.insert("byteStride".into(), json!(v.byte_stride));
    }
    if v.target >= 0 {
        o.insert("target".into(), json!(v.target));
    }
    insert_opt(&mut o, "name", v.name.clone());
    Value::Object(o)
}
fn buffer_json(v: &GltfBuffer, include_data: bool) -> Result<Value, Error> {
    let mut o = Map::new();
    o.insert("byteLength".into(), json!(v.byte_length));
    insert_opt(&mut o, "name", v.name.clone());
    if include_data {
        if let Some(data) = &v.data {
            o.insert(
                "uri".into(),
                json!(format!(
                    "data:application/octet-stream;base64,{}",
                    base64::engine::general_purpose::STANDARD.encode(data)
                )),
            );
        } else {
            insert_opt(&mut o, "uri", v.uri.clone());
        }
    }
    Value::Object(o).pipe(Ok)
}

trait Pipe: Sized {
    fn pipe<T>(self, f: impl FnOnce(Self) -> T) -> T {
        f(self)
    }
}
impl<T> Pipe for T {}
