use crate::{context::GameContext, game::GameObject};
use gltf::animation::Interpolation;
use keket::database::{AssetDatabase, handle::AssetHandle};
use nodio::{
    AnyIndex,
    graph::Graph,
    query::{Node, QueryFetch, Related},
};
use send_wrapper::SendWrapper;
use serde_json::Value;
use spitfire_core::Triangle;
use spitfire_draw::{
    context::DrawContext,
    sprite::SpriteTexture,
    utils::{Drawable, ShaderRef, Vertex, transform_to_matrix},
};
use spitfire_glow::{
    graphics::{GraphicsBatch, GraphicsTarget},
    renderer::{GlowBlending, GlowUniformValue},
};
use std::{
    borrow::Cow,
    cmp::Ordering,
    collections::{HashMap, HashSet},
    error::Error,
    f32,
    hash::Hash,
    ops::Range,
    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, atomic::AtomicU32},
};
use vek::{Mat4, Quaternion, Rgba, Transform, Vec2, Vec3};

#[derive(Debug, Default, Clone)]
pub struct GltfMesh {
    pub primitives: Vec<GltfPrimitive>,
}

#[derive(Debug, Default, Clone, Copy)]
pub struct GltfVertex {
    pub position: Vec3<f32>,
    pub uv: Vec2<f32>,
    pub color: Rgba<f32>,
    pub joints: Option<[u16; 4]>,
    pub weights: Option<[f32; 4]>,
}

#[derive(Debug, Default, Clone)]
pub struct GltfPrimitive {
    pub main_texture: Option<SendWrapper<SpriteTexture>>,
    pub blending: GlowBlending,
    pub triangles: Vec<Triangle>,
    pub vertices: Vec<GltfVertex>,
}

#[derive(Debug, Clone)]
pub struct GltfSkin {
    pub inverse_bind_matrices: Vec<Mat4<f32>>,
    pub bones: Vec<GltfSkeletonBone>,
}

#[derive(Debug, Clone)]
pub struct GltfAnimation {
    pub channels: Vec<GltfAnimationChannel>,
    pub duration: f32,
}

#[derive(Debug, Clone)]
pub struct GltfAnimationChannel {
    pub target_node: GltfNodeId,
    pub times: Vec<f32>,
    pub duration: f32,
    pub values: GltfAnimationValues,
    pub interpolation: Interpolation,
}

#[derive(Debug, Clone)]
pub enum GltfAnimationValues {
    Translation(Vec<Vec3<f32>>),
    Rotation(Vec<Quaternion<f32>>),
    Scale(Vec<Vec3<f32>>),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GltfNodeId {
    pub container_handle: AssetHandle,
    pub node_index: usize,
}

impl std::fmt::Display for GltfNodeId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}-{}", self.container_handle, self.node_index)
    }
}

#[derive(Debug, Clone)]
pub struct GltfSkeletonBone {
    pub id: GltfNodeId,
    pub ibm_index: usize,
}

pub struct GltfSceneRoot;
pub struct GltfSceneParent;
pub struct GltfSceneChild;
pub struct GltfSceneAttribute;
pub struct GltfSceneBone;

pub struct GltfSceneMesh(AssetHandle);

impl GltfSceneMesh {
    pub fn handle(&self) -> AssetHandle {
        self.0
    }
}

pub struct GltfSceneSkin(AssetHandle);

impl GltfSceneSkin {
    pub fn handle(&self) -> AssetHandle {
        self.0
    }
}

#[derive(Debug, Default, Clone)]
pub struct GltfSceneTransform {
    pub transform: Transform<f32, f32, f32>,
    bind_transform: Transform<f32, f32, f32>,
    local_matrix: Mat4<f32>,
    local_inverse_matrix: Mat4<f32>,
    global_matrix: Mat4<f32>,
    global_inverse_matrix: Mat4<f32>,
}

impl GltfSceneTransform {
    pub fn bind_transform(&self) -> Transform<f32, f32, f32> {
        self.bind_transform
    }

    pub fn local_matrix(&self) -> Mat4<f32> {
        self.local_matrix
    }

    pub fn local_inverse_matrix(&self) -> Mat4<f32> {
        self.local_inverse_matrix
    }

    pub fn world_matrix(&self) -> Mat4<f32> {
        self.global_matrix
    }

    pub fn world_inverse_matrix(&self) -> Mat4<f32> {
        self.global_inverse_matrix
    }
}

#[derive(Debug, Clone)]
pub struct GltfNode {
    pub id: GltfNodeId,
    pub name: String,
    pub extras: Value,
    pub transform: Transform<f32, f32, f32>,
    pub mesh_handle: Option<AssetHandle>,
    pub skin_handle: Option<AssetHandle>,
    pub children: Vec<Self>,
}

#[derive(Default)]
pub struct GltfSceneInstantiateOptions {
    #[allow(clippy::type_complexity)]
    pub remap_node_names: Option<Box<dyn Fn(&str) -> String + Send + Sync>>,
    #[allow(clippy::type_complexity)]
    pub extract_extras: Option<Box<dyn Fn(&Value, &mut Graph, AnyIndex) + Send + Sync>>,
}

impl GltfSceneInstantiateOptions {
    pub fn remap_node_names(mut self, f: impl Fn(&str) -> String + Send + Sync + 'static) -> Self {
        self.remap_node_names = Some(Box::new(f));
        self
    }

    pub fn extract_extras(
        mut self,
        f: impl Fn(&Value, &mut Graph, AnyIndex) + Send + Sync + 'static,
    ) -> Self {
        self.extract_extras = Some(Box::new(f));
        self
    }
}

#[derive(Debug, Clone)]
pub struct GltfSceneTemplate {
    pub name: String,
    pub container_handle: AssetHandle,
    pub root_nodes: Vec<GltfNode>,
}

impl GltfSceneTemplate {
    pub fn instantiate(&self, database: &AssetDatabase) -> GltfSceneInstance {
        self.instantiate_with_options(database, &Default::default())
    }

    pub fn instantiate_with_options(
        &self,
        database: &AssetDatabase,
        options: &GltfSceneInstantiateOptions,
    ) -> GltfSceneInstance {
        let mut graph = Graph::default();
        let mut bone_nodes = HashSet::new();

        let roots = self
            .root_nodes
            .iter()
            .map(|root_node| {
                Self::instantiate_node(
                    &mut graph,
                    root_node,
                    None,
                    database,
                    &mut bone_nodes,
                    options,
                )
            })
            .collect();

        for bone_id in &bone_nodes {
            let Some(index) = graph
                .iter::<GltfNodeId>()
                .find(|(_, id)| **id == *bone_id)
                .map(|(index, _)| index)
            else {
                continue;
            };
            let bone = graph.insert(GltfSceneBone);
            graph.relate::<GltfSceneAttribute>(index, bone);
        }

        let result = GltfSceneInstance {
            blend_only_affected_animations: false,
            container_handle: self.container_handle,
            graph,
            roots,
            animations: Default::default(),
            parameters: Default::default(),
            animation_node: None,
        };
        result.recompute_matrices();
        result
    }

    fn instantiate_node(
        graph: &mut Graph,
        node: &GltfNode,
        parent_index: Option<AnyIndex>,
        database: &AssetDatabase,
        bone_nodes: &mut HashSet<GltfNodeId>,
        options: &GltfSceneInstantiateOptions,
    ) -> AnyIndex {
        let index = graph.insert(node.id);

        if let Some(parent_index) = parent_index {
            graph.relate_pair::<GltfSceneParent, GltfSceneChild>(parent_index, index);
        } else {
            let root = graph.insert(GltfSceneRoot);
            graph.relate::<GltfSceneAttribute>(index, root);
        }

        let name = if let Some(remap) = &options.remap_node_names {
            remap(&node.name)
        } else {
            node.name.clone()
        };
        let name = graph.insert(name);
        graph.relate::<GltfSceneAttribute>(index, name);

        let transform = graph.insert(GltfSceneTransform {
            transform: node.transform,
            bind_transform: node.transform,
            local_matrix: Default::default(),
            local_inverse_matrix: Default::default(),
            global_matrix: Default::default(),
            global_inverse_matrix: Default::default(),
        });
        graph.relate::<GltfSceneAttribute>(index, transform);

        if let Some(mesh_handle) = node.mesh_handle {
            let mesh = graph.insert(GltfSceneMesh(mesh_handle));
            graph.relate::<GltfSceneAttribute>(index, mesh);
        }

        if let Some(skin_handle) = node.skin_handle {
            let skin = graph.insert(GltfSceneSkin(skin_handle));
            graph.relate::<GltfSceneAttribute>(index, skin);

            if let Some(asset) = skin_handle.access_checked::<&GltfSkin>(database) {
                for bone in asset.bones.iter() {
                    bone_nodes.insert(bone.id);
                }
            }
        }

        if let Some(extract_extras) = &options.extract_extras {
            extract_extras(&node.extras, graph, index);
        }

        for child in &node.children {
            Self::instantiate_node(graph, child, Some(index), database, bone_nodes, options);
        }

        index
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct GltfAnimationEvent {
    pub time: f32,
    pub duration: f32,
    pub id: Cow<'static, str>,
    pub value: Value,
}

impl GltfAnimationEvent {
    pub fn new(id: impl Into<Cow<'static, str>>) -> Self {
        Self {
            id: id.into(),
            value: Value::Null,
            time: 0.0,
            duration: 0.0,
        }
    }

    pub fn value(mut self, value: Value) -> Self {
        self.value = value;
        self
    }

    pub fn time(mut self, time: f32) -> Self {
        self.time = time;
        self
    }

    pub fn duration(mut self, duration: f32) -> Self {
        self.duration = duration;
        self
    }

    pub fn time_range(mut self, start: f32, end: f32) -> Self {
        self.time = start;
        self.duration = end - start;
        self
    }
}

#[derive(Debug, Clone)]
pub struct GltfSceneAnimation {
    pub animation_handle: AssetHandle,
    pub time: f32,
    duration: f32,
    cycle_completed: bool,
    pub weight: f32,
    pub looped: bool,
    pub playing: bool,
    pub speed: f32,
    pub affected_nodes: HashSet<AnyIndex>,
    pub nodes_weight_override: HashMap<AnyIndex, f32>,
    pub events_timeline: Vec<GltfAnimationEvent>,
    events_passed: HashSet<usize>,
}

impl GltfSceneAnimation {
    pub fn new(animation_handle: AssetHandle, database: &AssetDatabase) -> Option<Self> {
        let duration = animation_handle
            .access_checked::<&GltfAnimation>(database)?
            .duration;
        Some(Self {
            animation_handle,
            time: 0.0,
            duration,
            cycle_completed: false,
            weight: 1.0,
            looped: false,
            playing: false,
            speed: 1.0,
            affected_nodes: Default::default(),
            nodes_weight_override: Default::default(),
            events_timeline: Default::default(),
            events_passed: Default::default(),
        })
    }

    pub fn setup(self, mut f: impl FnMut(Self) -> Self) -> Self {
        f(self)
    }

    pub fn time(mut self, time: f32) -> Self {
        self.time = time;
        self
    }

    pub fn duration(&self) -> f32 {
        self.duration
    }

    pub fn weight(mut self, weight: f32) -> Self {
        self.weight = weight;
        self
    }

    pub fn looped(mut self, looped: bool) -> Self {
        self.looped = looped;
        self
    }

    pub fn playing(mut self, playing: bool) -> Self {
        self.playing = playing;
        self
    }

    pub fn start(&mut self) {
        self.playing = true;
        self.time = 0.0;
        self.weight = 1.0;
    }

    pub fn stop(&mut self) {
        self.playing = false;
        self.time = 0.0;
        self.weight = 0.0;
    }

    pub fn has_completed(&self) -> bool {
        if !self.looped && self.speed >= 0.0 {
            self.time >= self.duration
        } else {
            self.time <= 0.0
        }
    }

    pub fn has_cycle_completed(&self) -> bool {
        self.cycle_completed
    }

    pub fn speed(mut self, speed: f32) -> Self {
        self.speed = speed;
        self
    }

    pub fn affected_nodes(mut self, affected_nodes: impl IntoIterator<Item = AnyIndex>) -> Self {
        self.affected_nodes.extend(affected_nodes);
        self
    }

    pub fn affected_node(mut self, affected_node: AnyIndex) -> Self {
        self.affected_nodes.insert(affected_node);
        self
    }

    pub fn node_weight_overrides(
        mut self,
        nodes_weight_override: impl IntoIterator<Item = (AnyIndex, f32)>,
    ) -> Self {
        self.nodes_weight_override.extend(nodes_weight_override);
        self
    }

    pub fn node_weight_override(mut self, node: AnyIndex, weight: f32) -> Self {
        self.nodes_weight_override.insert(node, weight);
        self
    }

    pub fn event(mut self, event: GltfAnimationEvent) -> Self {
        self.events_timeline.push(event);
        self
    }

    pub fn events(mut self, events: impl IntoIterator<Item = GltfAnimationEvent>) -> Self {
        self.events_timeline.extend(events);
        self
    }

    pub fn passed_events(&self) -> impl Iterator<Item = &GltfAnimationEvent> + '_ {
        self.events_passed
            .iter()
            .filter_map(move |&index| self.events_timeline.get(index))
    }

    pub fn sanitize_time(&mut self) {
        self.cycle_completed = false;
        if self.time < 0.0 {
            self.cycle_completed = true;
            if self.looped {
                self.time = self.duration + (self.time % self.duration);
            } else {
                self.time = 0.0;
                self.playing = false;
            }
        } else if self.time > self.duration {
            self.cycle_completed = true;
            if self.looped {
                self.time %= self.duration;
            } else {
                self.time = self.duration;
                self.playing = false;
            }
        }
    }

    pub fn update(&mut self, delta_time: f32, forced: bool) {
        self.events_passed.clear();
        if !forced && !self.playing {
            return;
        }
        let previous_time = self.time;
        self.time += delta_time * self.speed;
        self.events_passed = self
            .events_timeline
            .iter()
            .enumerate()
            .filter_map(|(index, event)| {
                let (frame_from, frame_to) =
                    (previous_time.min(self.time), previous_time.max(self.time));
                let event_from = event.time;
                let event_end = event.time + event.duration;
                let (event_from, event_end) =
                    (event_from.min(event_end), event_from.max(event_end));
                if frame_from <= event_end && frame_to >= event_from {
                    Some(index)
                } else {
                    None
                }
            })
            .collect();
        self.sanitize_time();
    }
}

#[derive(Debug, Clone)]
pub struct GltfSceneAnimationHandle(Arc<RwLock<GltfSceneAnimation>>);

impl GltfSceneAnimationHandle {
    pub fn read(&self) -> Option<RwLockReadGuard<'_, GltfSceneAnimation>> {
        self.0.try_read().ok()
    }

    pub fn write(&self) -> Option<RwLockWriteGuard<'_, GltfSceneAnimation>> {
        self.0.try_write().ok()
    }

    pub fn get(&self) -> Option<GltfSceneAnimation> {
        self.read().map(|guard| guard.clone())
    }

    pub fn set(&self, animation: GltfSceneAnimation) {
        if let Ok(mut guard) = self.0.write() {
            *guard = animation;
        }
    }
}

#[derive(Debug, Default, Clone)]
pub struct GltfAnimationParameter(Arc<AtomicU32>);

impl GltfAnimationParameter {
    pub fn new(value: f32) -> Self {
        Self(Arc::new(AtomicU32::new(value.to_bits())))
    }

    pub fn get(&self) -> f32 {
        f32::from_bits(self.0.load(std::sync::atomic::Ordering::SeqCst))
    }

    pub fn set(&self, value: f32) -> f32 {
        f32::from_bits(
            self.0
                .swap(value.to_bits(), std::sync::atomic::Ordering::SeqCst),
        )
    }
}

#[derive(Debug, Default)]
pub struct GltfAnimationBlender {
    animation_weights: HashMap<String, f32>,
}

impl GltfAnimationBlender {
    pub fn animation(&mut self, name: impl ToString, weight: f32) {
        *self.animation_weights.entry(name.to_string()).or_default() += weight;
    }
}

pub trait GltfAnimationNode: Send + Sync {
    fn produce_weights(
        &self,
        instance: &GltfSceneInstance,
        master_weight: f32,
        output: &mut GltfAnimationBlender,
    );

    #[allow(unused_variables)]
    fn update(&mut self, delta_time: f32);
}

impl GltfAnimationNode for () {
    fn produce_weights(&self, _: &GltfSceneInstance, _: f32, _: &mut GltfAnimationBlender) {}
    fn update(&mut self, _: f32) {}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GltfAnimationTarget(pub String);

impl GltfAnimationTarget {
    pub fn new(name: impl ToString) -> Self {
        Self(name.to_string())
    }
}

impl GltfAnimationNode for GltfAnimationTarget {
    fn produce_weights(
        &self,
        _: &GltfSceneInstance,
        master_weight: f32,
        output: &mut GltfAnimationBlender,
    ) {
        output.animation(self.0.clone(), master_weight);
    }

    fn update(&mut self, _delta_time: f32) {}
}

#[derive(Debug, Clone, PartialEq)]
pub enum GltfAnimationWeight {
    Fixed(f32),
    Parameter(String),
}

impl Default for GltfAnimationWeight {
    fn default() -> Self {
        Self::Fixed(1.0)
    }
}

impl GltfAnimationWeight {
    pub fn fixed(weight: f32) -> Self {
        Self::Fixed(weight)
    }

    pub fn parameter(name: impl ToString) -> Self {
        Self::Parameter(name.to_string())
    }

    fn get(&self, instance: &GltfSceneInstance) -> f32 {
        match self {
            GltfAnimationWeight::Fixed(weight) => *weight,
            GltfAnimationWeight::Parameter(name) => instance
                .parameter(name)
                .map(|p| p.get())
                .unwrap_or_default(),
        }
    }
}

pub struct GltfAnimationTransition {
    pub controller: GltfAnimationTransitionController,
    pub layers: Vec<GltfAnimationTransitionLayer>,
    pub default_layer: Option<String>,
    pub change_speed: Option<f32>,
}

impl GltfAnimationTransition {
    pub fn new(controller: GltfAnimationTransitionController) -> Self {
        Self {
            controller,
            layers: Default::default(),
            default_layer: None,
            change_speed: None,
        }
    }

    pub fn layer(mut self, layer: GltfAnimationTransitionLayer) -> Self {
        self.layers.push(layer);
        self
    }

    pub fn layers(
        mut self,
        layers: impl IntoIterator<Item = GltfAnimationTransitionLayer>,
    ) -> Self {
        self.layers.extend(layers);
        self
    }

    pub fn default_layer(mut self, name: impl ToString) -> Self {
        self.default_layer = Some(name.to_string());
        self
    }

    pub fn change_speed(mut self, speed: f32) -> Self {
        self.change_speed = Some(speed);
        self
    }
}

impl GltfAnimationNode for GltfAnimationTransition {
    fn produce_weights(
        &self,
        instance: &GltfSceneInstance,
        master_weight: f32,
        output: &mut GltfAnimationBlender,
    ) {
        for layer in &self.layers {
            layer
                .node
                .produce_weights(instance, master_weight * layer.current_weight, output);
        }
    }

    fn update(&mut self, delta_time: f32) {
        if let Ok(controller) = self.controller.active.read() {
            for layer in &mut self.layers {
                let target_weight = if (controller.is_empty()
                    && self
                        .default_layer
                        .as_ref()
                        .map(|name| name == &layer.name)
                        .unwrap_or_default())
                    || controller.contains(&layer.name)
                {
                    1.0
                } else {
                    0.0
                };
                if let Some(change_speed) = layer.change_speed.or(self.change_speed) {
                    if (layer.current_weight - target_weight).abs() < f32::EPSILON {
                        layer.current_weight = target_weight;
                    } else if layer.current_weight < target_weight {
                        layer.current_weight =
                            (layer.current_weight + change_speed * delta_time).clamp(0.0, 1.0);
                    } else {
                        layer.current_weight =
                            (layer.current_weight - change_speed * delta_time).clamp(0.0, 1.0);
                    }
                } else {
                    layer.current_weight = target_weight;
                }
            }
        }
        for layer in &mut self.layers {
            layer.node.update(delta_time);
        }
    }
}

pub struct GltfAnimationTransitionLayer {
    pub node: Box<dyn GltfAnimationNode>,
    pub change_speed: Option<f32>,
    name: String,
    current_weight: f32,
}

impl GltfAnimationTransitionLayer {
    pub fn new(name: impl ToString, node: impl GltfAnimationNode + 'static) -> Self {
        Self {
            node: Box::new(node),
            change_speed: None,
            name: name.to_string(),
            current_weight: 0.0,
        }
    }

    pub fn change_speed(mut self, speed: f32) -> Self {
        self.change_speed = Some(speed);
        self
    }
}

#[derive(Default, Clone)]
pub struct GltfAnimationTransitionController {
    active: Arc<RwLock<HashSet<String>>>,
}

impl GltfAnimationTransitionController {
    pub fn clear(&self) {
        if let Ok(mut guard) = self.active.write() {
            guard.clear();
        }
    }

    pub fn change_to(&self, names: impl IntoIterator<Item = impl ToString>) {
        if let Ok(mut guard) = self.active.write() {
            guard.clear();
            for name in names {
                guard.insert(name.to_string());
            }
        }
    }
}

#[derive(Default)]
pub struct GltfAnimationMixer {
    pub layers: Vec<GltfAnimationMixerLayer>,
}

impl GltfAnimationMixer {
    pub fn layer(mut self, layer: GltfAnimationMixerLayer) -> Self {
        self.layers.push(layer);
        self
    }

    pub fn layers(mut self, layers: impl IntoIterator<Item = GltfAnimationMixerLayer>) -> Self {
        self.layers.extend(layers);
        self
    }
}

impl GltfAnimationNode for GltfAnimationMixer {
    fn produce_weights(
        &self,
        instance: &GltfSceneInstance,
        master_weight: f32,
        output: &mut GltfAnimationBlender,
    ) {
        for layer in &self.layers {
            layer.node.produce_weights(
                instance,
                master_weight * layer.weight.get(instance),
                output,
            );
        }
    }

    fn update(&mut self, delta_time: f32) {
        for layer in &mut self.layers {
            layer.node.update(delta_time);
        }
    }
}

pub struct GltfAnimationMixerLayer {
    pub weight: GltfAnimationWeight,
    pub node: Box<dyn GltfAnimationNode>,
}

impl GltfAnimationMixerLayer {
    pub fn new(weight: GltfAnimationWeight, node: impl GltfAnimationNode + 'static) -> Self {
        Self {
            weight,
            node: Box::new(node),
        }
    }
}

pub struct GltfAnimationBlendSpace<const N: usize> {
    pub parameters: [Cow<'static, str>; N],
    pub points: Vec<GltfAnimationBlendSpacePoint<N>>,
}

impl<const N: usize> GltfAnimationBlendSpace<N> {
    pub fn new(parameters: [Cow<'static, str>; N]) -> Self {
        Self {
            parameters,
            points: Default::default(),
        }
    }

    pub fn points(
        mut self,
        points: impl IntoIterator<Item = GltfAnimationBlendSpacePoint<N>>,
    ) -> Self {
        self.points.extend(points);
        self
    }

    pub fn point(mut self, point: GltfAnimationBlendSpacePoint<N>) -> Self {
        self.points.push(point);
        self
    }
}

impl<const N: usize> GltfAnimationNode for GltfAnimationBlendSpace<N> {
    fn produce_weights(
        &self,
        instance: &GltfSceneInstance,
        master_weight: f32,
        output: &mut GltfAnimationBlender,
    ) {
        fn distance<const N: usize>(a: [f32; N], b: [f32; N]) -> f32 {
            let mut sum = 0.0;
            for i in 0..N {
                let diff = a[i] - b[i];
                sum += diff * diff;
            }
            sum.sqrt()
        }

        let parameters = std::array::from_fn::<_, N, _>(|i| self.parameters[i].as_ref())
            .map(|name| instance.parameter(name).map(|p| p.get()).unwrap_or(0.0));
        let distances = self
            .points
            .iter()
            .map(|point| distance(point.parameters, parameters))
            .collect::<Vec<f32>>();

        let mut weights = distances
            .iter()
            .map(|&d| 1.0 / (d * d))
            .collect::<Vec<f32>>();

        if let Some(found) = distances.iter().position(|&d| d <= 1.0e-6_f32) {
            weights.fill(0.0);
            weights[found] = 1.0;
        } else {
            let total_weight: f32 = weights.iter().sum();
            for weight in &mut weights {
                *weight = (*weight / total_weight).clamp(0.0, 1.0);
            }
        }

        for (point, &weight) in self.points.iter().zip(weights.iter()) {
            point
                .node
                .produce_weights(instance, weight * master_weight, output);
        }
    }

    fn update(&mut self, delta_time: f32) {
        for point in &mut self.points {
            point.node.update(delta_time);
        }
    }
}

pub struct GltfAnimationBlendSpacePoint<const N: usize> {
    pub parameters: [f32; N],
    pub node: Box<dyn GltfAnimationNode>,
}

impl<const N: usize> GltfAnimationBlendSpacePoint<N> {
    pub fn new(parameters: [f32; N], node: impl GltfAnimationNode + 'static) -> Self {
        Self {
            parameters,
            node: Box::new(node),
        }
    }
}

pub struct GltfSceneInstance {
    pub blend_only_affected_animations: bool,
    container_handle: AssetHandle,
    graph: Graph,
    roots: Vec<AnyIndex>,
    animations: HashMap<String, GltfSceneAnimationHandle>,
    parameters: HashMap<String, GltfAnimationParameter>,
    animation_node: Option<Box<dyn GltfAnimationNode>>,
}

impl GltfSceneInstance {
    pub fn blend_only_affected_animations(mut self, value: bool) -> Self {
        self.blend_only_affected_animations = value;
        self
    }

    pub fn with_animation(mut self, name: impl ToString, animation: GltfSceneAnimation) -> Self {
        self.add_animation(name, animation);
        self
    }

    pub fn with_parameter(
        mut self,
        name: impl ToString,
        parameter: GltfAnimationParameter,
    ) -> Self {
        self.add_parameter(name, parameter);
        self
    }

    pub fn with_animation_node(mut self, animation_node: impl GltfAnimationNode + 'static) -> Self {
        self.set_animation_node(animation_node);
        self
    }

    pub fn animation(&self, name: &str) -> Option<&GltfSceneAnimationHandle> {
        self.animations.get(name)
    }

    pub fn animations(&self) -> impl Iterator<Item = (&str, &GltfSceneAnimationHandle)> + '_ {
        self.animations
            .iter()
            .map(|(name, handle)| (name.as_str(), handle))
    }

    pub fn parameter(&self, name: &str) -> Option<&GltfAnimationParameter> {
        self.parameters.get(name)
    }

    pub fn parameters(&self) -> impl Iterator<Item = (&str, &GltfAnimationParameter)> + '_ {
        self.parameters
            .iter()
            .map(|(name, parameter)| (name.as_str(), parameter))
    }

    pub fn container_handle(&self) -> AssetHandle {
        self.container_handle
    }

    pub fn graph(&self) -> &Graph {
        &self.graph
    }

    pub fn roots(&self) -> impl Iterator<Item = AnyIndex> + '_ {
        self.roots.iter().copied()
    }

    pub fn find_bone_by_name(&self, name: &str) -> Option<AnyIndex> {
        self.graph.iter::<GltfNodeId>().find_map(|(index, _)| {
            let (_, bone_name) = self
                .graph
                .query::<(
                    Related<GltfSceneAttribute, &GltfSceneBone>,
                    Related<GltfSceneAttribute, &String>,
                )>(index)
                .next()?;
            if bone_name.as_str() == name {
                Some(index)
            } else {
                None
            }
        })
    }

    pub fn query_bone_by_name<'a, Fetch: QueryFetch<'a>>(
        &'a self,
        name: &str,
    ) -> Option<Fetch::Value> {
        let index = self.find_bone_by_name(name)?;
        self.graph.query::<Fetch>(index).next()
    }

    pub fn add_animation(
        &mut self,
        name: impl ToString,
        animation: GltfSceneAnimation,
    ) -> GltfSceneAnimationHandle {
        let handle = GltfSceneAnimationHandle(Arc::new(RwLock::new(animation)));
        self.animations.insert(name.to_string(), handle.clone());
        handle
    }

    pub fn remove_animation(&mut self, name: &str) {
        self.animations.remove(name);
    }

    pub fn add_parameter(
        &mut self,
        name: impl ToString,
        parameter: GltfAnimationParameter,
    ) -> GltfAnimationParameter {
        self.parameters.insert(name.to_string(), parameter.clone());
        parameter
    }

    pub fn remove_parameter(&mut self, name: &str) {
        self.parameters.remove(name);
    }

    pub fn animation_node(&self) -> Option<&dyn GltfAnimationNode> {
        self.animation_node.as_deref()
    }

    pub fn animation_node_mut(&mut self) -> Option<&mut (dyn GltfAnimationNode + 'static)> {
        self.animation_node.as_deref_mut()
    }

    pub fn set_animation_node(&mut self, animation_node: impl GltfAnimationNode + 'static) {
        self.animation_node = Some(Box::new(animation_node));
    }

    pub fn unset_animation_node(&mut self) {
        self.animation_node = None;
    }

    pub fn update_animations(&mut self, delta_time: f32) {
        for handle in self.animations.values() {
            if let Some(mut animation) = handle.write() {
                animation.update(delta_time, false);
            }
        }
        if let Some(mut animation_node) = self.animation_node.take() {
            animation_node.update(delta_time);
            let mut blender = GltfAnimationBlender::default();
            animation_node.produce_weights(self, 1.0, &mut blender);
            self.animation_node = Some(animation_node);
            if self.blend_only_affected_animations {
                for (name, weight) in blender.animation_weights {
                    if let Some(handle) = self.animations.get(&name)
                        && let Some(mut animation) = handle.write()
                    {
                        animation.weight = weight;
                    }
                }
            } else {
                for (name, handle) in self.animations.iter() {
                    let weight = blender
                        .animation_weights
                        .iter()
                        .find(|(n, _)| n.as_str() == name.as_str())
                        .map(|(_, w)| *w)
                        .unwrap_or_default();
                    if let Some(mut animation) = handle.write() {
                        animation.weight = weight;
                    }
                }
            };
        }
    }

    pub fn apply_animations(&self, database: &AssetDatabase) {
        let mut delta_changes = HashMap::<
            AnyIndex,
            (
                Vec<(Vec3<f32>, f32)>,
                Vec<(Quaternion<f32>, f32)>,
                Vec<(Vec3<f32>, f32)>,
            ),
        >::default();
        for handle in self.animations.values() {
            let Some(animation) = handle.read() else {
                continue;
            };
            let Some(asset) = animation
                .animation_handle
                .access_checked::<&GltfAnimation>(database)
            else {
                continue;
            };
            for channel in &asset.channels {
                if channel.times.len() < 2 {
                    continue;
                }
                let Some(node_index) = self
                    .graph
                    .iter::<GltfNodeId>()
                    .find(|(_, id)| **id == channel.target_node)
                    .map(|(index, _)| index)
                else {
                    continue;
                };
                if !animation.affected_nodes.is_empty()
                    && !animation.affected_nodes.contains(&node_index)
                {
                    continue;
                }
                let max_time = asset.duration;
                let time_to_sample = if animation.looped {
                    animation.time % max_time
                } else {
                    animation.time.min(max_time)
                };
                let result_index = channel
                    .times
                    .iter()
                    .position(|&t| t > time_to_sample)
                    .unwrap_or(channel.times.len());
                let (i0, i1) = if animation.looped {
                    let i1 = result_index % channel.times.len();
                    let i0 = if i1 == 0 {
                        channel.times.len() - 1
                    } else {
                        i1 - 1
                    };
                    (i0, i1)
                } else {
                    let i1 = result_index.min(channel.times.len() - 1);
                    let i0 = i1.saturating_sub(1);
                    (i0, i1)
                };
                let Some(transform) = self
                    .graph
                    .query::<Related<GltfSceneAttribute, &GltfSceneTransform>>(node_index)
                    .next()
                else {
                    continue;
                };
                match &channel.values {
                    GltfAnimationValues::Translation(values) => match channel.interpolation {
                        Interpolation::Linear => {
                            let t0 = channel.times[i0];
                            let t1 = channel.times[i1];
                            let v0 = values[i0] - transform.bind_transform.position;
                            let v1 = values[i1] - transform.bind_transform.position;
                            let factor = if (t0 - t1).abs() < f32::EPSILON {
                                0.0
                            } else {
                                (time_to_sample - t0) / (t1 - t0)
                            };
                            let value = v0 + (v1 - v0) * factor;
                            delta_changes.entry(node_index).or_default().0.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::Step => {
                            let value = if i1 == 0 { values[0] } else { values[i0] }
                                - transform.bind_transform.position;
                            delta_changes.entry(node_index).or_default().0.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::CubicSpline => {
                            println!("CubicSpline interpolation not implemented yet.");
                        }
                    },
                    GltfAnimationValues::Rotation(values) => match channel.interpolation {
                        Interpolation::Linear => {
                            let t0 = channel.times[i0];
                            let t1 = channel.times[i1];
                            let v0 = (values[i0] * transform.bind_transform.orientation.inverse())
                                .normalized();
                            let v1 = (values[i1] * transform.bind_transform.orientation.inverse())
                                .normalized();
                            let factor = if (t0 - t1).abs() < f32::EPSILON {
                                0.0
                            } else {
                                (time_to_sample - t0) / (t1 - t0)
                            };
                            let value = shortest_slerp(v0, v1, factor);
                            delta_changes.entry(node_index).or_default().1.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::Step => {
                            let value = if i1 == 0 { values[0] } else { values[i0] };
                            let value = (value * transform.bind_transform.orientation.inverse())
                                .normalized();
                            delta_changes.entry(node_index).or_default().1.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::CubicSpline => {
                            println!("CubicSpline interpolation not implemented yet.");
                        }
                    },
                    GltfAnimationValues::Scale(values) => match channel.interpolation {
                        Interpolation::Linear => {
                            let t0 = channel.times[i0];
                            let t1 = channel.times[i1];
                            let v0 = values[i0] / transform.bind_transform.scale;
                            let v1 = values[i1] / transform.bind_transform.scale;
                            let factor = if (t0 - t1).abs() < f32::EPSILON {
                                0.0
                            } else {
                                (time_to_sample - t0) / (t1 - t0)
                            };
                            let value = v0 + (v1 - v0) * factor;
                            delta_changes.entry(node_index).or_default().2.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::Step => {
                            let value = if i1 == 0 { values[0] } else { values[i0] }
                                / transform.bind_transform.scale;
                            delta_changes.entry(node_index).or_default().2.push((
                                value,
                                animation
                                    .nodes_weight_override
                                    .get(&node_index)
                                    .copied()
                                    .unwrap_or(animation.weight),
                            ));
                        }
                        Interpolation::CubicSpline => {
                            println!("CubicSpline interpolation not implemented yet.");
                        }
                    },
                }
            }
        }
        for (node_index, (translations, rotations, scales)) in delta_changes {
            let Some(mut transform) = self
                .graph
                .query::<Related<GltfSceneAttribute, &mut GltfSceneTransform>>(node_index)
                .next()
            else {
                continue;
            };
            if !translations.is_empty() {
                if translations.len() == 1 {
                    transform.transform.position =
                        translations[0].0 + transform.bind_transform.position;
                } else {
                    let total_weight = translations.iter().map(|(_, w)| *w).sum::<f32>();
                    if total_weight > f32::EPSILON {
                        let mut accumulated = Vec3::zero();
                        for (value, weight) in translations {
                            accumulated += value * weight;
                        }
                        transform.transform.position =
                            (accumulated / total_weight) + transform.bind_transform.position;
                    }
                }
            }
            if !rotations.is_empty() {
                if rotations.len() == 1 {
                    transform.transform.orientation =
                        (rotations[0].0 * transform.bind_transform.orientation).normalized();
                } else {
                    let total_weight = rotations.iter().map(|(_, w)| *w).sum::<f32>();
                    if total_weight > f32::EPSILON {
                        let mut accumulated = Quaternion::identity();
                        for (value, weight) in rotations {
                            let factor = weight / total_weight;
                            accumulated = shortest_slerp(accumulated, value, factor);
                        }
                        transform.transform.orientation =
                            (accumulated * transform.bind_transform.orientation).normalized();
                    }
                }
            }
            if !scales.is_empty() {
                if scales.len() == 1 {
                    transform.transform.scale = scales[0].0 * transform.bind_transform.scale;
                } else {
                    let total_weight = scales.iter().map(|(_, w)| *w).sum::<f32>();
                    if total_weight > f32::EPSILON {
                        let mut accumulated = Vec3::zero();
                        for (value, weight) in scales {
                            accumulated += value * weight;
                        }
                        transform.transform.scale =
                            (accumulated / total_weight) * transform.bind_transform.scale;
                    }
                }
            }
        }
        self.recompute_matrices();
    }

    pub fn update_and_apply_animations(&mut self, delta_time: f32, database: &AssetDatabase) {
        self.update_animations(delta_time);
        self.apply_animations(database);
    }

    pub fn visit_tree(
        &self,
        f: &mut impl FnMut(
            usize,
            AnyIndex,
            GltfNodeId,
            Option<&String>,
            Option<&GltfSceneTransform>,
            Option<&GltfSceneMesh>,
            Option<&GltfSceneSkin>,
            Option<&GltfSceneBone>,
        ) -> bool,
    ) {
        for index in self.roots() {
            self.visit_tree_inner(0, index, f);
        }
    }

    fn visit_tree_inner(
        &self,
        level: usize,
        index: AnyIndex,
        f: &mut impl FnMut(
            usize,
            AnyIndex,
            GltfNodeId,
            Option<&String>,
            Option<&GltfSceneTransform>,
            Option<&GltfSceneMesh>,
            Option<&GltfSceneSkin>,
            Option<&GltfSceneBone>,
        ) -> bool,
    ) {
        let Some(id) = self.graph.read::<GltfNodeId>(index).ok() else {
            return;
        };
        let name = self
            .graph
            .query::<Related<GltfSceneAttribute, &String>>(index)
            .next();
        let transform = self
            .graph
            .query::<Related<GltfSceneAttribute, &GltfSceneTransform>>(index)
            .next();
        let mesh = self
            .graph
            .query::<Related<GltfSceneAttribute, &GltfSceneMesh>>(index)
            .next();
        let skin = self
            .graph
            .query::<Related<GltfSceneAttribute, &GltfSceneSkin>>(index)
            .next();
        let bone = self
            .graph
            .query::<Related<GltfSceneAttribute, &GltfSceneBone>>(index)
            .next();
        if !f(
            level,
            index,
            *id,
            name.as_deref(),
            transform.as_deref(),
            mesh.as_deref(),
            skin.as_deref(),
            bone.as_deref(),
        ) {
            return;
        }
        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.visit_tree_inner(level + 1, child_index, f);
        }
    }

    pub fn recompute_matrices(&self) {
        for root_index in self.roots() {
            self.recompute_matrix(Default::default(), root_index);
        }
    }

    fn recompute_matrix(&self, parent_matrix: Mat4<f32>, index: AnyIndex) {
        let matrix = if let Some(mut transform) = self
            .graph
            .query::<Related<GltfSceneAttribute, &mut GltfSceneTransform>>(index)
            .next()
        {
            transform.local_matrix = transform_to_matrix(transform.transform);
            transform.local_inverse_matrix = transform.local_matrix.inverted();
            transform.global_matrix = parent_matrix * transform.local_matrix;
            transform.global_inverse_matrix = transform.global_matrix.inverted();
            transform.global_matrix
        } else {
            parent_matrix
        };
        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.recompute_matrix(matrix, child_index);
        }
    }

    pub fn build_renderables(
        &self,
        database: &AssetDatabase,
        options: &GltfRenderablesOptions,
    ) -> Result<GltfSceneRenderables, Box<dyn Error>> {
        let mut result = Default::default();
        if options.meshes {
            self.recompute_matrices();
            let mut final_bone_matrices: HashMap<GltfNodeId, Mat4<f32>> = Default::default();
            for root_index in self.roots() {
                self.compute_final_bone_matrix(root_index, database, &mut final_bone_matrices)?;
            }
            for root_index in self.roots() {
                self.collect_renderables(
                    root_index,
                    database,
                    options,
                    &final_bone_matrices,
                    &mut result,
                )?;
            }
            if let Some(sorting_fn) = options.renderable_sorting {
                result.renderables.sort_by(sorting_fn);
            }
        }
        if options.bones {
            for root_index in self.roots() {
                self.collect_bones(root_index, options, None, &mut result);
            }
        }
        Ok(result)
    }

    pub fn inspect_renderables(
        &self,
        database: &AssetDatabase,
        options: &GltfRenderablesOptions,
        mut f: impl FnMut(&Graph, AnyIndex, GltfSceneRenderable),
    ) -> Result<(), Box<dyn Error>> {
        self.recompute_matrices();
        let mut final_bone_matrices: HashMap<GltfNodeId, Mat4<f32>> = Default::default();
        for root_index in self.roots() {
            self.compute_final_bone_matrix(root_index, database, &mut final_bone_matrices)?;
        }
        for root_index in self.roots() {
            self.produce_renderables(root_index, database, options, &final_bone_matrices, &mut f)?;
        }
        Ok(())
    }

    fn compute_final_bone_matrix(
        &self,
        index: AnyIndex,
        database: &AssetDatabase,
        final_bone_matrices: &mut HashMap<GltfNodeId, Mat4<f32>>,
    ) -> Result<(), Box<dyn Error>> {
        if let Some(skin) = self
            .graph
            .query::<Related<GltfSceneAttribute, &GltfSceneSkin>>(index)
            .next()
        {
            let skin_asset = skin
                .handle()
                .access_checked::<&GltfSkin>(database)
                .ok_or("Skin asset not found")?;

            for index in self.roots() {
                Self::traverse_hierarchy(&self.graph, index, &mut |graph, index| {
                    let id = *self.graph.read::<GltfNodeId>(index).unwrap();

                    if let Some(bone) = skin_asset.bones.iter().find(|bone| bone.id == id)
                        && let Some(transform) = graph
                            .query::<Related<GltfSceneAttribute, &GltfSceneTransform>>(index)
                            .next()
                    {
                        final_bone_matrices.insert(
                            bone.id,
                            transform.global_matrix
                                * skin_asset.inverse_bind_matrices[bone.ibm_index],
                        );
                    }
                    Ok(true)
                })?;
            }
        }

        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.compute_final_bone_matrix(child_index, database, final_bone_matrices)?;
        }

        Ok(())
    }

    fn collect_renderables(
        &self,
        index: AnyIndex,
        database: &AssetDatabase,
        options: &GltfRenderablesOptions,
        final_bone_matrices: &HashMap<GltfNodeId, Mat4<f32>>,
        renderables: &mut GltfSceneRenderables,
    ) -> Result<(), Box<dyn Error>> {
        let start = renderables.renderables.len();
        if (options.renderable_filter)(&self.graph, index)
            && let Some((transform, mesh)) = self
                .graph
                .query::<(
                    Related<GltfSceneAttribute, &GltfSceneTransform>,
                    Related<GltfSceneAttribute, &GltfSceneMesh>,
                )>(index)
                .next()
        {
            let mesh_asset = mesh
                .handle()
                .access_checked::<&GltfMesh>(database)
                .ok_or("Mesh asset not found")?;
            let skin = self
                .graph
                .query::<Related<GltfSceneAttribute, &GltfSceneSkin>>(index)
                .next();
            let skin_asset =
                skin.and_then(|skin| skin.handle().access_checked::<&GltfSkin>(database));

            for primitive in &mesh_asset.primitives {
                let mut renderable = Self::produce_primitive_renderable(
                    options,
                    final_bone_matrices,
                    skin_asset,
                    primitive,
                    &transform,
                );
                (options.renderable_modifier)(&self.graph, index, &mut renderable);
                if let Some(sorting_fn) = options.triangle_sorting {
                    renderable.triangles.sort_by(|a, b| {
                        let a_vertices = [
                            &primitive.vertices[a.a as usize].position,
                            &primitive.vertices[a.b as usize].position,
                            &primitive.vertices[a.c as usize].position,
                        ];
                        let b_vertices = [
                            &primitive.vertices[b.a as usize].position,
                            &primitive.vertices[b.b as usize].position,
                            &primitive.vertices[b.c as usize].position,
                        ];
                        sorting_fn(a_vertices, b_vertices)
                    });
                }
                renderables.renderables.push(renderable);
            }
        }
        (options.custom_renderables)(
            &self.graph,
            index,
            database,
            options,
            final_bone_matrices,
            renderables,
            start..renderables.renderables.len(),
        )?;

        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.collect_renderables(
                child_index,
                database,
                options,
                final_bone_matrices,
                renderables,
            )?;
        }

        Ok(())
    }

    fn produce_renderables(
        &self,
        index: AnyIndex,
        database: &AssetDatabase,
        options: &GltfRenderablesOptions,
        final_bone_matrices: &HashMap<GltfNodeId, Mat4<f32>>,
        f: &mut impl FnMut(&Graph, AnyIndex, GltfSceneRenderable),
    ) -> Result<(), Box<dyn Error>> {
        if (options.renderable_filter)(&self.graph, index)
            && let Some((transform, mesh)) = self
                .graph
                .query::<(
                    Related<GltfSceneAttribute, &GltfSceneTransform>,
                    Related<GltfSceneAttribute, &GltfSceneMesh>,
                )>(index)
                .next()
        {
            let mesh_asset = mesh
                .handle()
                .access_checked::<&GltfMesh>(database)
                .ok_or("Mesh asset not found")?;
            let skin = self
                .graph
                .query::<Related<GltfSceneAttribute, &GltfSceneSkin>>(index)
                .next();
            let skin_asset =
                skin.and_then(|skin| skin.handle().access_checked::<&GltfSkin>(database));

            for primitive in &mesh_asset.primitives {
                let renderable = Self::produce_primitive_renderable(
                    options,
                    final_bone_matrices,
                    skin_asset,
                    primitive,
                    &transform,
                );
                f(&self.graph, index, renderable);
            }
        }

        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.produce_renderables(child_index, database, options, final_bone_matrices, f)?;
        }

        Ok(())
    }

    fn produce_primitive_renderable(
        options: &GltfRenderablesOptions,
        final_bone_matrices: &HashMap<GltfNodeId, Mat4<f32>>,
        skin_asset: Option<&GltfSkin>,
        primitive: &GltfPrimitive,
        transform: &GltfSceneTransform,
    ) -> GltfSceneRenderable {
        let triangles = primitive.triangles.clone();
        let vertices = primitive
            .vertices
            .iter()
            .map(|v| {
                let position = if let Some(skin_asset) = skin_asset {
                    if let (Some(joints), Some(weights)) = (v.joints, v.weights) {
                        let mut skinned_position = Vec3::zero();
                        for index in 0..4 {
                            let joint_index = joints[index] as usize;
                            if joint_index < skin_asset.bones.len() {
                                let bone = &skin_asset.bones[joint_index];
                                if let Some(bone_matrix) = final_bone_matrices.get(&bone.id) {
                                    skinned_position +=
                                        bone_matrix.mul_point(v.position) * weights[index];
                                }
                            }
                        }
                        skinned_position
                    } else {
                        v.position
                    }
                } else {
                    v.position
                };
                let position = transform.global_matrix.mul_point(position).into_array();
                let mut position = [position[options.axes[0]], position[options.axes[1]]];
                if options.flip_axes[0] {
                    position[0] = -position[0];
                }
                if options.flip_axes[1] {
                    position[1] = -position[1];
                }

                Vertex {
                    position,
                    uv: [v.uv.x, v.uv.y, 0.0],
                    color: v.color.into_array(),
                }
            })
            .collect();

        GltfSceneRenderable {
            shader: options.shader.clone(),
            main_texture: primitive.main_texture.as_deref().cloned(),
            blending: primitive.blending,
            wireframe: false,
            triangles,
            vertices,
        }
    }

    fn collect_bones(
        &self,
        index: AnyIndex,
        options: &GltfRenderablesOptions,
        mut parent_position: Option<[f32; 2]>,
        renderables: &mut GltfSceneRenderables,
    ) {
        let color = options.bones_color.into_array();
        if let Some((transform, _)) = self
            .graph
            .query::<(
                Related<GltfSceneAttribute, &GltfSceneTransform>,
                Related<GltfSceneAttribute, &GltfSceneBone>,
            )>(index)
            .next()
        {
            let position = transform.global_matrix.mul_point(Vec2::zero()).into_array();
            let mut position = [position[options.axes[0]], position[options.axes[1]]];
            if options.flip_axes[0] {
                position[0] = -position[0];
            }
            if options.flip_axes[1] {
                position[1] = -position[1];
            }
            if let Some(parent_position) = parent_position {
                let from = Vec2::<f32>::from(parent_position);
                let to = Vec2::<f32>::from(position);
                let tangent = (to - from).normalized();
                let normal = Vec2::new(-tangent.y, tangent.x) * options.bones_thickness * 0.5;
                let from_left = from - normal;
                let from_right = from + normal;
                let offset = renderables.bones_vertices.len();
                renderables.bones_vertices.push(Vertex {
                    position: [from_left.x, from_left.y],
                    uv: [0.0, 0.0, 0.0],
                    color,
                });
                renderables.bones_vertices.push(Vertex {
                    position: [from_right.x, from_right.y],
                    uv: [0.0, 0.0, 0.0],
                    color,
                });
                renderables.bones_vertices.push(Vertex {
                    position: [to.x, to.y],
                    uv: [0.0, 0.0, 0.0],
                    color,
                });
                renderables
                    .bones_triangles
                    .push(Triangle { a: 0, b: 1, c: 2 }.offset(offset));
            }
            parent_position = Some(position);
        }

        for child_index in self
            .graph
            .query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index)
        {
            self.collect_bones(child_index, options, parent_position, renderables);
        }
    }

    fn traverse_hierarchy(
        graph: &Graph,
        index: AnyIndex,
        f: &mut impl FnMut(&Graph, AnyIndex) -> Result<bool, Box<dyn Error>>,
    ) -> Result<(), Box<dyn Error>> {
        if !f(graph, index)? {
            return Ok(());
        }

        for child in graph.query::<Related<GltfSceneChild, Node<GltfNodeId>>>(index) {
            Self::traverse_hierarchy(graph, child, f)?;
        }

        Ok(())
    }
}

impl GameObject for GltfSceneInstance {
    fn draw(&mut self, context: &mut GameContext) {
        if let Ok(renderables) = self.build_renderables(
            context.assets,
            &GltfRenderablesOptions::default().sort_triangles_by_max_positive_z(),
        ) {
            renderables.draw(context.draw, context.graphics);
        }
    }
}

pub type GltfTriangleSorting = fn([&Vec3<f32>; 3], [&Vec3<f32>; 3]) -> Ordering;
pub type GltfRenderableSorting = fn(&GltfSceneRenderable, &GltfSceneRenderable) -> Ordering;
pub type GltfRenderablesFilter = fn(&Graph, AnyIndex) -> bool;
pub type GltfRenderableModifier = fn(&Graph, AnyIndex, &mut GltfSceneRenderable);
pub type GltfCustomRenderables = fn(
    &Graph,
    AnyIndex,
    &AssetDatabase,
    &GltfRenderablesOptions,
    &HashMap<GltfNodeId, Mat4<f32>>,
    &mut GltfSceneRenderables,
    Range<usize>,
) -> Result<(), Box<dyn Error>>;

#[derive(Debug, Clone)]
pub struct GltfRenderablesOptions {
    pub meshes: bool,
    pub bones: bool,
    pub shader: Option<ShaderRef>,
    pub axes: [usize; 2],
    pub flip_axes: [bool; 2],
    pub triangle_sorting: Option<GltfTriangleSorting>,
    pub renderable_sorting: Option<GltfRenderableSorting>,
    pub bones_shader: Option<ShaderRef>,
    pub bones_color: Rgba<f32>,
    pub bones_thickness: f32,
    pub renderable_filter: GltfRenderablesFilter,
    pub renderable_modifier: GltfRenderableModifier,
    pub custom_renderables: GltfCustomRenderables,
}

impl Default for GltfRenderablesOptions {
    fn default() -> Self {
        Self {
            meshes: true,
            bones: false,
            shader: None,
            axes: [0, 1],
            flip_axes: [false, false],
            triangle_sorting: None,
            renderable_sorting: None,
            bones_shader: None,
            bones_color: Rgba::magenta(),
            bones_thickness: 0.0,
            renderable_filter: |_, _| true,
            renderable_modifier: |_, _, _| {},
            custom_renderables: |_, _, _, _, _, _, _| Ok(()),
        }
    }
}

impl GltfRenderablesOptions {
    pub fn meshes(mut self, meshes: bool) -> Self {
        self.meshes = meshes;
        self
    }

    pub fn bones(mut self, bones: bool) -> Self {
        self.bones = bones;
        self
    }

    pub fn shader(mut self, shader: ShaderRef) -> Self {
        self.shader = Some(shader);
        self
    }

    pub fn axes(mut self, axes: [usize; 2]) -> Self {
        self.axes = axes;
        self
    }

    pub fn flip_axes(mut self, flip_axes: [bool; 2]) -> Self {
        self.flip_axes = flip_axes;
        self
    }

    pub fn triangle_sorting(mut self, sorting: GltfTriangleSorting) -> Self {
        self.triangle_sorting = Some(sorting);
        self
    }

    pub fn sort_triangles_by_max_positive_z(mut self) -> Self {
        self.triangle_sorting = Some(|a, b| {
            let a = a[0].z.max(a[1].z).max(a[2].z);
            let b = b[0].z.max(b[1].z).max(b[2].z);
            a.partial_cmp(&b).unwrap_or(Ordering::Equal)
        });
        self
    }

    pub fn renderable_sorting(mut self, sorting: GltfRenderableSorting) -> Self {
        self.renderable_sorting = Some(sorting);
        self
    }

    pub fn sort_renderables_by_max_positive_z(mut self) -> Self {
        self.renderable_sorting = Some(|a, b| {
            let a = a
                .vertices
                .iter()
                .map(|v| v.position[1])
                .fold(f32::NEG_INFINITY, f32::max);
            let b = b
                .vertices
                .iter()
                .map(|v| v.position[1])
                .fold(f32::NEG_INFINITY, f32::max);
            a.partial_cmp(&b).unwrap_or(Ordering::Equal)
        });
        self
    }

    pub fn bones_shader(mut self, shader: ShaderRef) -> Self {
        self.bones_shader = Some(shader);
        self
    }

    pub fn bones_color(mut self, color: Rgba<f32>) -> Self {
        self.bones_color = color;
        self
    }

    pub fn bones_thickness(mut self, thickness: f32) -> Self {
        self.bones_thickness = thickness;
        self
    }

    pub fn renderable_filter(mut self, filter: GltfRenderablesFilter) -> Self {
        self.renderable_filter = filter;
        self
    }

    pub fn renderable_modifier(mut self, modifier: GltfRenderableModifier) -> Self {
        self.renderable_modifier = modifier;
        self
    }

    pub fn custom_renderables(mut self, custom: GltfCustomRenderables) -> Self {
        self.custom_renderables = custom;
        self
    }
}

#[derive(Debug)]
pub struct GltfSceneRenderable {
    pub shader: Option<ShaderRef>,
    pub main_texture: Option<SpriteTexture>,
    pub blending: GlowBlending,
    pub wireframe: bool,
    pub triangles: Vec<Triangle>,
    pub vertices: Vec<Vertex>,
}

#[derive(Debug, Default)]
pub struct GltfSceneRenderables {
    pub renderables: Vec<GltfSceneRenderable>,
    pub bones_shader: Option<ShaderRef>,
    pub bones_vertices: Vec<Vertex>,
    pub bones_triangles: Vec<Triangle>,
}

impl Drawable for GltfSceneRenderables {
    fn draw(&self, context: &mut DrawContext, graphics: &mut dyn GraphicsTarget<Vertex>) {
        let matrix = context.top_transform();
        for renderable in &self.renderables {
            let batch = GraphicsBatch {
                shader: context.shader(renderable.shader.as_ref()),
                uniforms: std::iter::once((
                    "u_projection_view".into(),
                    GlowUniformValue::M4(
                        graphics.state().main_camera.world_matrix().into_col_array(),
                    ),
                ))
                .chain(
                    renderable
                        .main_texture
                        .iter()
                        .map(|texture| (texture.sampler.clone(), GlowUniformValue::I1(0))),
                )
                .collect(),
                textures: renderable
                    .main_texture
                    .iter()
                    .filter_map(|texture| {
                        Some((context.texture(Some(&texture.texture))?, texture.filtering))
                    })
                    .collect(),
                blending: renderable.blending,
                scissor: None,
                wireframe: renderable.wireframe,
            };
            graphics.state_mut().stream.batch_optimized(batch);
            graphics.state_mut().stream.transformed(
                |stream| {
                    stream.extend(renderable.vertices.clone(), renderable.triangles.clone());
                },
                |vertex| {
                    let point = matrix.mul_point(Vec2::from(vertex.position));
                    vertex.position[0] = point.x;
                    vertex.position[1] = point.y;
                },
            );
        }
        if !self.bones_triangles.is_empty() {
            let batch = GraphicsBatch {
                shader: context.shader(self.bones_shader.as_ref()),
                uniforms: std::iter::once((
                    "u_projection_view".into(),
                    GlowUniformValue::M4(
                        graphics.state().main_camera.world_matrix().into_col_array(),
                    ),
                ))
                .collect(),
                textures: vec![],
                blending: GlowBlending::Alpha,
                scissor: None,
                wireframe: false,
            };
            graphics.state_mut().stream.batch_optimized(batch);
            graphics.state_mut().stream.transformed(
                |stream| {
                    stream.extend(self.bones_vertices.clone(), self.bones_triangles.clone());
                },
                |vertex| {
                    let point = matrix.mul_point(Vec2::from(vertex.position));
                    vertex.position[0] = point.x;
                    vertex.position[1] = point.y;
                },
            );
        }
    }
}

fn shortest_slerp(a: Quaternion<f32>, mut b: Quaternion<f32>, t: f32) -> Quaternion<f32> {
    if a.dot(b) < 0.0 {
        b = -b;
    }
    Quaternion::slerp(a, b, t).normalized()
}
