use crate::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum GizmoMode {
    #[default]
    Translation,
    Rotation,
    Scale,
}

pub struct GizmoState {
    pub root: Entity,
    pub target: Entity,
    pub mode: GizmoMode,
    pub axis_entities: GizmoAxisEntities,
}

#[derive(Debug, Clone, Default)]
pub struct GizmoAxisEntities {
    pub x_axis: Option<Entity>,
    pub y_axis: Option<Entity>,
    pub z_axis: Option<Entity>,
    pub center_cube: Option<Entity>,
    pub xy_plane: Option<Entity>,
    pub yz_plane: Option<Entity>,
    pub xz_plane: Option<Entity>,
    pub x_axis_line: Option<Entity>,
    pub y_axis_line: Option<Entity>,
    pub z_axis_line: Option<Entity>,
}

#[derive(Debug, Clone, Copy)]
pub struct GizmoConfig {
    pub arrow_length: f32,
    pub arrow_radius: f32,
    pub cone_height: f32,
    pub cone_radius: f32,
    pub highlight_factor: f32,
    pub normal_factor: f32,
    pub scale_factor: f32,
    pub min_scale: f32,
    pub max_scale: f32,
}

impl Default for GizmoConfig {
    fn default() -> Self {
        Self {
            arrow_length: 2.0,
            arrow_radius: 0.06,
            cone_height: 0.5,
            cone_radius: 0.15,
            highlight_factor: 1.0,
            normal_factor: 1.0,
            scale_factor: 0.08,
            min_scale: 0.3,
            max_scale: 50.0,
        }
    }
}

fn set_gizmo_layer(world: &mut World, entity: Entity) {
    if let Some(render_layer) = world.get_render_layer_mut(entity) {
        render_layer.0 = RenderLayer::OVERLAY;
    }
}

struct AxisArrowParams {
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    length: f32,
    cylinder_radius: f32,
    cone_height: f32,
    cone_radius: f32,
    axis_name: String,
}

struct AxisWithCubeParams {
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    length: f32,
    cylinder_radius: f32,
    cube_size: f32,
    axis_name: String,
}

struct RotationAxisParams {
    parent: Entity,
    axis: Vec3,
    color: Vec3,
    axis_length: f32,
    axis_radius: f32,
    cube_size: f32,
    axis_name: String,
}

pub fn create_translation_gizmo(world: &mut World, target_entity: Entity) -> GizmoState {
    create_translation_gizmo_with_config(world, target_entity, GizmoConfig::default())
}

pub fn create_translation_gizmo_with_config(
    world: &mut World,
    target_entity: Entity,
    config: GizmoConfig,
) -> GizmoState {
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = "Translation Gizmo".to_string();
    }

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    let mut axis_entities = GizmoAxisEntities::default();

    let arrow_length = config.arrow_length;
    let arrow_radius = config.arrow_radius;
    let cone_height = config.cone_height;
    let cone_radius = config.cone_radius;

    axis_entities.x_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(1.0, 0.0, 0.0),
            color: nalgebra_glm::vec3(0.7, 0.0, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "X Axis".to_string(),
        },
    ));

    axis_entities.y_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 1.0, 0.0),
            color: nalgebra_glm::vec3(0.0, 0.7, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "Y Axis".to_string(),
        },
    ));

    axis_entities.z_axis = Some(create_axis_arrow(
        world,
        AxisArrowParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 0.0, 1.0),
            color: nalgebra_glm::vec3(0.0, 0.0, 0.7),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cone_height,
            cone_radius,
            axis_name: "Z Axis".to_string(),
        },
    ));

    let center_cube = create_center_cube(world, gizmo_root);
    axis_entities.center_cube = Some(center_cube);

    axis_entities.xy_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.4, 0.4, 0.0),
        arrow_length * 0.3,
        "XY Plane",
    ));

    axis_entities.yz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.4, 0.4),
        arrow_length * 0.3,
        "YZ Plane",
    ));

    axis_entities.xz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.4, 0.0, 0.4),
        arrow_length * 0.3,
        "XZ Plane",
    ));

    axis_entities.x_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.7, 0.0, 0.0),
        "X Axis Line",
    ));

    axis_entities.y_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.7, 0.0),
        "Y Axis Line",
    ));

    axis_entities.z_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.0, 0.0, 0.7),
        "Z Axis Line",
    ));

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode: GizmoMode::Translation,
        axis_entities,
    }
}

pub fn create_scale_gizmo(world: &mut World, target_entity: Entity) -> GizmoState {
    create_scale_gizmo_with_config(world, target_entity, GizmoConfig::default())
}

pub fn create_scale_gizmo_with_config(
    world: &mut World,
    target_entity: Entity,
    config: GizmoConfig,
) -> GizmoState {
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = "Scale Gizmo".to_string();
    }

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    let mut axis_entities = GizmoAxisEntities::default();

    let arrow_length = config.arrow_length;
    let arrow_radius = config.arrow_radius;
    let cube_size = config.cone_height * 0.5;

    axis_entities.x_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(1.0, 0.0, 0.0),
            color: nalgebra_glm::vec3(0.7, 0.0, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "X Axis".to_string(),
        },
    ));

    axis_entities.y_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 1.0, 0.0),
            color: nalgebra_glm::vec3(0.0, 0.7, 0.0),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "Y Axis".to_string(),
        },
    ));

    axis_entities.z_axis = Some(create_axis_with_cube(
        world,
        AxisWithCubeParams {
            parent: gizmo_root,
            direction: nalgebra_glm::vec3(0.0, 0.0, 1.0),
            color: nalgebra_glm::vec3(0.0, 0.0, 0.7),
            length: arrow_length,
            cylinder_radius: arrow_radius,
            cube_size,
            axis_name: "Z Axis".to_string(),
        },
    ));

    let center_cube = create_center_cube(world, gizmo_root);
    axis_entities.center_cube = Some(center_cube);

    axis_entities.xy_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.4, 0.4, 0.0),
        arrow_length * 0.3,
        "XY Plane",
    ));

    axis_entities.yz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.4, 0.4),
        arrow_length * 0.3,
        "YZ Plane",
    ));

    axis_entities.xz_plane = Some(create_plane_cube(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.4, 0.0, 0.4),
        arrow_length * 0.3,
        "XZ Plane",
    ));

    axis_entities.x_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(1.0, 0.0, 0.0),
        nalgebra_glm::vec3(0.7, 0.0, 0.0),
        "X Axis Line",
    ));

    axis_entities.y_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 1.0, 0.0),
        nalgebra_glm::vec3(0.0, 0.7, 0.0),
        "Y Axis Line",
    ));

    axis_entities.z_axis_line = Some(create_infinite_axis_line(
        world,
        gizmo_root,
        nalgebra_glm::vec3(0.0, 0.0, 1.0),
        nalgebra_glm::vec3(0.0, 0.0, 0.7),
        "Z Axis Line",
    ));

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode: GizmoMode::Scale,
        axis_entities,
    }
}

pub fn create_rotation_gizmo(world: &mut World, target_entity: Entity) -> GizmoState {
    let gizmo_root = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | NAME
            | VISIBILITY
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(gizmo_root) {
        name.0 = "Rotation Gizmo".to_string();
    }

    set_gizmo_layer(world, gizmo_root);

    if let Some(transform) = world.get_local_transform(gizmo_root) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(gizmo_root, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(gizmo_root) {
        visibility.visible = true;
    }

    let config = GizmoConfig::default();
    let arrow_length = config.arrow_length;
    let arrow_radius = config.arrow_radius;
    let cube_size = 0.15;

    let axis_entities = GizmoAxisEntities {
        x_axis: Some(create_rotation_axis_with_cube(
            world,
            RotationAxisParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(1.0, 0.0, 0.0),
                color: nalgebra_glm::vec3(0.7, 0.0, 0.0),
                axis_length: arrow_length,
                axis_radius: arrow_radius,
                cube_size,
                axis_name: "X Rotation Axis".to_string(),
            },
        )),
        y_axis: Some(create_rotation_axis_with_cube(
            world,
            RotationAxisParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(0.0, 1.0, 0.0),
                color: nalgebra_glm::vec3(0.0, 0.7, 0.0),
                axis_length: arrow_length,
                axis_radius: arrow_radius,
                cube_size,
                axis_name: "Y Rotation Axis".to_string(),
            },
        )),
        z_axis: Some(create_rotation_axis_with_cube(
            world,
            RotationAxisParams {
                parent: gizmo_root,
                axis: nalgebra_glm::vec3(0.0, 0.0, 1.0),
                color: nalgebra_glm::vec3(0.0, 0.0, 0.7),
                axis_length: arrow_length,
                axis_radius: arrow_radius,
                cube_size,
                axis_name: "Z Rotation Axis".to_string(),
            },
        )),
        x_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(1.0, 0.0, 0.0),
            nalgebra_glm::vec3(0.7, 0.0, 0.0),
            "X Axis Line",
        )),
        y_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(0.0, 1.0, 0.0),
            nalgebra_glm::vec3(0.0, 0.7, 0.0),
            "Y Axis Line",
        )),
        z_axis_line: Some(create_infinite_axis_line(
            world,
            gizmo_root,
            nalgebra_glm::vec3(0.0, 0.0, 1.0),
            nalgebra_glm::vec3(0.0, 0.0, 0.7),
            "Z Axis Line",
        )),
        ..Default::default()
    };

    GizmoState {
        root: gizmo_root,
        target: target_entity,
        mode: GizmoMode::Rotation,
        axis_entities,
    }
}

fn create_axis_arrow(world: &mut World, params: AxisArrowParams) -> Entity {
    let axis_group = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(axis_group) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(axis_group, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(axis_group) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(axis_group, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(axis_group) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, axis_group);

    let rotation = if params.direction.y.abs() > 0.99 {
        Quat::identity()
    } else if params.direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if params.direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    let cylinder_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cylinder_entity) {
        name.0 = format!("{} Cylinder", params.axis_name);
    }

    set_gizmo_layer(world, cylinder_entity);

    world.update_parent(cylinder_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cylinder_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * (params.length * 0.5);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cylinder_radius / 0.5,
            params.length / 1.0,
            params.cylinder_radius / 0.5,
        );
        world.assign_local_transform(cylinder_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cylinder_entity) {
        mesh.name = "Cylinder".to_string();
    }

    if let Some(material) = world.get_material_mut(cylinder_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    let cone_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cone_entity) {
        name.0 = format!("{} Cone", params.axis_name);
    }

    set_gizmo_layer(world, cone_entity);

    world.update_parent(cone_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cone_entity) {
        let mut new_transform = *transform;
        let cone_scale_radius = 3.0;
        let cone_scale_height = 2.0;
        let scaled_cone_height = params.cone_height * cone_scale_height;
        new_transform.translation = params.direction * (params.length + scaled_cone_height / 4.0);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cone_radius / 1.0 * cone_scale_radius,
            params.cone_height / 2.0 * cone_scale_height,
            params.cone_radius / 1.0 * cone_scale_radius,
        );
        world.assign_local_transform(cone_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cone_entity) {
        mesh.name = "Cone".to_string();
    }

    if let Some(material) = world.get_material_mut(cone_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(axis_group) {
        let total_length = params.length + params.cone_height;
        let click_radius = 0.15;

        let center = params.direction * (total_length * 0.5);

        bounding.obb.center = center;

        if params.direction.y.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, total_length * 0.5, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.5, click_radius, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, click_radius, total_length * 0.5);
            bounding.obb.orientation = Quat::identity();
        }

        bounding.sphere_radius =
            (click_radius * click_radius + (total_length * 0.5) * (total_length * 0.5)).sqrt();
    }

    axis_group
}

fn create_center_cube(world: &mut World, parent: Entity) -> Entity {
    let center_cube = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(center_cube) {
        name.0 = "Center Cube".to_string();
    }

    set_gizmo_layer(world, center_cube);

    world.update_parent(center_cube, Some(Parent(Some(parent))));

    if let Some(transform) = world.get_local_transform(center_cube) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(0.15, 0.15, 0.15);
        world.assign_local_transform(center_cube, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(center_cube) {
        mesh.name = "Cube".to_string();
    }

    if let Some(material) = world.get_material_mut(center_cube) {
        material.base_color = nalgebra_glm::vec4(0.6, 0.6, 0.6, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    if let Some(visibility) = world.get_visibility_mut(center_cube) {
        visibility.visible = true;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(center_cube) {
        let click_size = 0.25;
        bounding.sphere_radius = click_size * 0.866;
        bounding.obb.center = Vec3::zeros();
        bounding.obb.half_extents =
            nalgebra_glm::vec3(click_size * 0.5, click_size * 0.5, click_size * 0.5);
        bounding.obb.orientation = Quat::identity();
    }

    center_cube
}

fn create_axis_with_cube(world: &mut World, params: AxisWithCubeParams) -> Entity {
    let axis_group = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(axis_group) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(axis_group, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(axis_group) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(axis_group, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(axis_group) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, axis_group);

    let rotation = if params.direction.y.abs() > 0.99 {
        Quat::identity()
    } else if params.direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if params.direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    let cylinder_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cylinder_entity) {
        name.0 = format!("{} Cylinder", params.axis_name);
    }

    set_gizmo_layer(world, cylinder_entity);

    world.update_parent(cylinder_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cylinder_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * (params.length * 0.5);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.cylinder_radius / 0.5,
            params.length / 1.0,
            params.cylinder_radius / 0.5,
        );
        world.assign_local_transform(cylinder_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cylinder_entity) {
        mesh.name = "Cylinder".to_string();
    }

    if let Some(material) = world.get_material_mut(cylinder_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    let cube_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cube_entity) {
        name.0 = format!("{} Cube", params.axis_name);
    }

    set_gizmo_layer(world, cube_entity);

    world.update_parent(cube_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cube_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.direction * params.length;
        new_transform.rotation = Quat::identity();
        new_transform.scale =
            nalgebra_glm::vec3(params.cube_size, params.cube_size, params.cube_size);
        world.assign_local_transform(cube_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cube_entity) {
        mesh.name = "Cube".to_string();
    }

    if let Some(material) = world.get_material_mut(cube_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(axis_group) {
        let total_length = params.length + params.cube_size * 0.5;
        let click_radius = 0.15;

        let center = params.direction * (total_length * 0.5);
        bounding.obb.center = center;

        if params.direction.y.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, total_length * 0.5, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.5, click_radius, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.direction.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_radius, click_radius, total_length * 0.5);
            bounding.obb.orientation = Quat::identity();
        }

        bounding.sphere_radius =
            (click_radius * click_radius + (total_length * 0.5) * (total_length * 0.5)).sqrt();
    }

    axis_group
}

fn create_rotation_axis_with_cube(world: &mut World, params: RotationAxisParams) -> Entity {
    let axis_group = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | PARENT
            | NAME
            | VISIBILITY
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(axis_group) {
        name.0 = params.axis_name.clone();
    }

    world.update_parent(axis_group, Some(Parent(Some(params.parent))));

    if let Some(transform) = world.get_local_transform(axis_group) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = Quat::identity();
        new_transform.scale = nalgebra_glm::vec3(1.0, 1.0, 1.0);
        world.assign_local_transform(axis_group, new_transform);
    }

    if let Some(visibility) = world.get_visibility_mut(axis_group) {
        visibility.visible = true;
    }

    set_gizmo_layer(world, axis_group);

    let rotation = if params.axis.y.abs() > 0.99 {
        Quat::identity()
    } else if params.axis.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.axis.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if params.axis.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if params.axis.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    let cylinder_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cylinder_entity) {
        name.0 = format!("{} Cylinder", params.axis_name);
    }

    set_gizmo_layer(world, cylinder_entity);

    world.update_parent(cylinder_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cylinder_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.axis * (params.axis_length * 0.5);
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.axis_radius / 0.5,
            params.axis_length / 1.0,
            params.axis_radius / 0.5,
        );
        world.assign_local_transform(cylinder_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cylinder_entity) {
        mesh.name = "Cylinder".to_string();
    }

    if let Some(material) = world.get_material_mut(cylinder_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    let perpendicular = if params.axis.y.abs() > 0.99 {
        nalgebra_glm::vec3(1.0, 0.0, 0.0)
    } else if params.axis.x.abs() > 0.99 {
        nalgebra_glm::vec3(0.0, 1.0, 0.0)
    } else {
        nalgebra_glm::vec3(1.0, 0.0, 0.0)
    };

    let cube_position = params.axis * params.axis_length + perpendicular * params.axis_length * 0.3;

    let connector_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(connector_entity) {
        name.0 = format!("{} Connector", params.axis_name);
    }

    set_gizmo_layer(world, connector_entity);

    world.update_parent(connector_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(connector_entity) {
        let midpoint = params.axis * params.axis_length + perpendicular * params.axis_length * 0.15;
        let mut new_transform = *transform;
        new_transform.translation = midpoint;

        let connector_rotation = if params.axis.y.abs() > 0.99 {
            nalgebra_glm::quat_angle_axis(
                std::f32::consts::PI / 2.0,
                &nalgebra_glm::vec3(0.0, 0.0, 1.0),
            )
        } else if params.axis.x.abs() > 0.99 {
            Quat::identity()
        } else {
            nalgebra_glm::quat_angle_axis(
                std::f32::consts::PI / 2.0,
                &nalgebra_glm::vec3(1.0, 0.0, 0.0),
            )
        };

        new_transform.rotation = connector_rotation;
        new_transform.scale = nalgebra_glm::vec3(
            params.axis_radius * 0.5 / 0.5,
            params.axis_length * 0.3 / 1.0,
            params.axis_radius * 0.5 / 0.5,
        );
        world.assign_local_transform(connector_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(connector_entity) {
        mesh.name = "Cylinder".to_string();
    }

    if let Some(material) = world.get_material_mut(connector_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    let torus_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(torus_entity) {
        name.0 = format!("{} Torus", params.axis_name);
    }

    set_gizmo_layer(world, torus_entity);

    world.update_parent(torus_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(torus_entity) {
        let mut new_transform = *transform;
        new_transform.translation = params.axis * params.axis_length;
        new_transform.rotation = rotation;

        let torus_major_radius = params.axis_length * 0.3 * 1.1;
        let torus_minor_radius = 0.02;
        new_transform.scale = nalgebra_glm::vec3(
            torus_major_radius / 1.0,
            torus_minor_radius / 0.25,
            torus_major_radius / 1.0,
        );
        world.assign_local_transform(torus_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(torus_entity) {
        mesh.name = "Torus".to_string();
    }

    if let Some(material) = world.get_material_mut(torus_entity) {
        material.base_color = nalgebra_glm::vec4(
            params.color.x * 0.7,
            params.color.y * 0.7,
            params.color.z * 0.7,
            0.8,
        )
        .into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Blend;
    }

    let cube_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(cube_entity) {
        name.0 = format!("{} Cube", params.axis_name);
    }

    set_gizmo_layer(world, cube_entity);

    world.update_parent(cube_entity, Some(Parent(Some(axis_group))));

    if let Some(transform) = world.get_local_transform(cube_entity) {
        let mut new_transform = *transform;
        new_transform.translation = cube_position;
        new_transform.rotation = Quat::identity();
        new_transform.scale =
            nalgebra_glm::vec3(params.cube_size, params.cube_size, params.cube_size);
        world.assign_local_transform(cube_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(cube_entity) {
        mesh.name = "Cube".to_string();
    }

    if let Some(material) = world.get_material_mut(cube_entity) {
        material.base_color =
            nalgebra_glm::vec4(params.color.x, params.color.y, params.color.z, 1.0).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Opaque;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(axis_group) {
        let total_length = params.axis_length * 1.3;
        let click_radius = params.cube_size * 2.0;

        let center = params.axis * (params.axis_length * 0.5);
        bounding.obb.center = center;

        if params.axis.y.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.3, total_length * 0.5, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.axis.x.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.5, total_length * 0.3, click_radius);
            bounding.obb.orientation = Quat::identity();
        } else if params.axis.z.abs() > 0.99 {
            bounding.obb.half_extents =
                nalgebra_glm::vec3(total_length * 0.3, click_radius, total_length * 0.5);
            bounding.obb.orientation = Quat::identity();
        }

        bounding.sphere_radius =
            (click_radius * click_radius + (total_length * 0.5) * (total_length * 0.5)).sqrt();
    }

    axis_group
}

fn create_plane_cube(
    world: &mut World,
    parent: Entity,
    plane_normal: Vec3,
    color: Vec3,
    size: f32,
    plane_name: &str,
) -> Entity {
    let plane_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | VISIBILITY
            | PARENT
            | NAME
            | RENDER_LAYER
            | BOUNDING_VOLUME,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(plane_entity) {
        name.0 = plane_name.to_string();
    }

    set_gizmo_layer(world, plane_entity);

    world.update_parent(plane_entity, Some(Parent(Some(parent))));

    if let Some(transform) = world.get_local_transform(plane_entity) {
        let mut new_transform = *transform;
        let offset = size * 0.5;

        if plane_normal.z.abs() > 0.5 {
            new_transform.translation = nalgebra_glm::vec3(offset, offset, 0.0);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size, size, size * 0.05);
        } else if plane_normal.x.abs() > 0.5 {
            new_transform.translation = nalgebra_glm::vec3(0.0, offset, offset);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size * 0.05, size, size);
        } else {
            new_transform.translation = nalgebra_glm::vec3(offset, 0.0, offset);
            new_transform.rotation = Quat::identity();
            new_transform.scale = nalgebra_glm::vec3(size, size * 0.05, size);
        }
        world.assign_local_transform(plane_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(plane_entity) {
        mesh.name = "Cube".to_string();
    }

    if let Some(material) = world.get_material_mut(plane_entity) {
        material.base_color = nalgebra_glm::vec4(color.x, color.y, color.z, 0.4).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Blend;
    }

    if let Some(visibility) = world.get_visibility_mut(plane_entity) {
        visibility.visible = true;
    }

    if let Some(bounding) = world.get_bounding_volume_mut(plane_entity) {
        let click_size = size * 1.2;
        bounding.sphere_radius = click_size * 0.866;

        if plane_normal.z.abs() > 0.5 {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_size * 0.5, click_size * 0.5, click_size * 0.05);
            bounding.obb.orientation = Quat::identity();
        } else if plane_normal.x.abs() > 0.5 {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_size * 0.05, click_size * 0.5, click_size * 0.5);
            bounding.obb.orientation = Quat::identity();
        } else {
            bounding.obb.center = Vec3::zeros();
            bounding.obb.half_extents =
                nalgebra_glm::vec3(click_size * 0.5, click_size * 0.05, click_size * 0.5);
            bounding.obb.orientation = Quat::identity();
        }
    }

    plane_entity
}

fn create_infinite_axis_line(
    world: &mut World,
    parent: Entity,
    direction: Vec3,
    color: Vec3,
    line_name: &str,
) -> Entity {
    let line_entity = world.spawn_entities(
        LOCAL_TRANSFORM
            | LOCAL_TRANSFORM_DIRTY
            | GLOBAL_TRANSFORM
            | RENDER_MESH
            | MATERIAL
            | PARENT
            | NAME
            | RENDER_LAYER
            | VISIBILITY,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(line_entity) {
        name.0 = line_name.to_string();
    }

    set_gizmo_layer(world, line_entity);

    world.update_parent(line_entity, Some(Parent(Some(parent))));

    let length = 2000.0;
    let radius = 0.02;

    let rotation = if direction.y.abs() > 0.99 {
        Quat::identity()
    } else if direction.x.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if direction.x > 0.0 {
                -std::f32::consts::PI / 2.0
            } else {
                std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(0.0, 0.0, 1.0),
        )
    } else if direction.z.abs() > 0.99 {
        nalgebra_glm::quat_angle_axis(
            if direction.z > 0.0 {
                std::f32::consts::PI / 2.0
            } else {
                -std::f32::consts::PI / 2.0
            },
            &nalgebra_glm::vec3(1.0, 0.0, 0.0),
        )
    } else {
        Quat::identity()
    };

    if let Some(transform) = world.get_local_transform(line_entity) {
        let mut new_transform = *transform;
        new_transform.translation = Vec3::zeros();
        new_transform.rotation = rotation;
        new_transform.scale = nalgebra_glm::vec3(radius / 0.5, length / 1.0, radius / 0.5);
        world.assign_local_transform(line_entity, new_transform);
    }

    if let Some(mesh) = world.get_render_mesh_mut(line_entity) {
        mesh.name = "Cylinder".to_string();
    }

    if let Some(material) = world.get_material_mut(line_entity) {
        material.base_color = nalgebra_glm::vec4(color.x, color.y, color.z, 0.5).into();
        material.unlit = true;
        material.alpha_mode = AlphaMode::Blend;
    }

    if let Some(visibility) = world.get_visibility_mut(line_entity) {
        visibility.visible = false;
    }

    line_entity
}

pub fn destroy_gizmo(world: &mut World, gizmo: &GizmoState) {
    let descendants = crate::ecs::transform::queries::query_descendants(world, gizmo.root);
    world.despawn_entities(&descendants);
    world.despawn_entities(&[gizmo.root]);
}

pub fn update_gizmo_transform(
    world: &mut World,
    gizmo: &GizmoState,
    target_position: Vec3,
    camera_distance: f32,
) {
    update_gizmo_transform_with_config(
        world,
        gizmo,
        target_position,
        camera_distance,
        GizmoConfig::default(),
    )
}

pub fn update_gizmo_transform_with_config(
    world: &mut World,
    gizmo: &GizmoState,
    target_position: Vec3,
    camera_distance: f32,
    config: GizmoConfig,
) {
    let scale = (camera_distance * config.scale_factor)
        .max(config.min_scale)
        .min(config.max_scale);
    if let Some(transform) = world.get_local_transform(gizmo.root) {
        let mut new_transform = *transform;
        new_transform.translation = target_position;
        new_transform.scale = nalgebra_glm::vec3(scale, scale, scale);
        new_transform.rotation = Quat::identity();
        world.assign_local_transform(gizmo.root, new_transform);
    }
}

fn set_entity_visible_recursive(world: &mut World, entity: Entity, visible: bool) {
    if let Some(vis) = world.get_visibility_mut(entity) {
        vis.visible = visible;
    }

    let descendants: Vec<Entity> = crate::ecs::transform::queries::query_descendants(world, entity)
        .into_iter()
        .collect();

    for descendant in descendants {
        if let Some(vis) = world.get_visibility_mut(descendant) {
            vis.visible = visible;
        }
    }
}

pub fn show_entities(world: &mut World, entities: &[Entity]) {
    for &entity in entities {
        if let Some(visibility) = world.get_visibility_mut(entity) {
            visibility.visible = true;
        }
    }
}

pub fn hide_entities(world: &mut World, entities: &[Entity]) {
    for &entity in entities {
        if let Some(visibility) = world.get_visibility_mut(entity) {
            visibility.visible = false;
        }
    }
}

pub fn check_gizmo_axis_hit(
    world: &World,
    gizmo: &GizmoState,
    picked_entity: Entity,
) -> Option<Vec3> {
    let axes = [
        (
            gizmo.axis_entities.x_axis,
            nalgebra_glm::vec3(1.0, 0.0, 0.0),
        ),
        (
            gizmo.axis_entities.y_axis,
            nalgebra_glm::vec3(0.0, 1.0, 0.0),
        ),
        (
            gizmo.axis_entities.z_axis,
            nalgebra_glm::vec3(0.0, 0.0, 1.0),
        ),
    ];

    for (axis_entity, axis_vec) in axes.iter() {
        if let Some(entity) = axis_entity
            && (picked_entity == *entity || is_child_of(world, picked_entity, *entity))
        {
            return Some(*axis_vec);
        }
    }

    if let Some(center_entity) = gizmo.axis_entities.center_cube
        && (picked_entity == center_entity || is_child_of(world, picked_entity, center_entity))
    {
        return Some(nalgebra_glm::vec3(1.0, 1.0, 1.0));
    }

    let planes = [
        (
            gizmo.axis_entities.xy_plane,
            nalgebra_glm::vec3(1.1, 1.1, 0.0),
        ),
        (
            gizmo.axis_entities.yz_plane,
            nalgebra_glm::vec3(0.0, 1.1, 1.1),
        ),
        (
            gizmo.axis_entities.xz_plane,
            nalgebra_glm::vec3(1.1, 0.0, 1.1),
        ),
    ];

    for (plane_entity, plane_vec) in planes.iter() {
        if let Some(entity) = plane_entity
            && (picked_entity == *entity || is_child_of(world, picked_entity, *entity))
        {
            return Some(*plane_vec);
        }
    }

    None
}

fn is_child_of(world: &World, child: Entity, parent: Entity) -> bool {
    let mut current = child;
    while let Some(parent_comp) = world.get_parent(current) {
        if let Some(parent_entity) = parent_comp.0 {
            if parent_entity == parent {
                return true;
            }
            current = parent_entity;
        } else {
            break;
        }
    }
    false
}

pub fn highlight_gizmo_axis(world: &mut World, gizmo: &GizmoState, axis: Option<Vec3>) {
    highlight_gizmo_axis_with_config(world, gizmo, axis, GizmoConfig::default())
}

pub fn highlight_gizmo_axis_with_config(
    world: &mut World,
    gizmo: &GizmoState,
    axis: Option<Vec3>,
    _config: GizmoConfig,
) {
    let axis_entities = &gizmo.axis_entities;

    let axes = [
        (axis_entities.x_axis, nalgebra_glm::vec3(0.7, 0.0, 0.0)),
        (axis_entities.y_axis, nalgebra_glm::vec3(0.0, 0.7, 0.0)),
        (axis_entities.z_axis, nalgebra_glm::vec3(0.0, 0.0, 0.7)),
    ];

    let highlighted_axis = axis.and_then(|a| {
        if a.x > 0.5 && a.y < 0.1 && a.z < 0.1 {
            Some(0)
        } else if a.y > 0.5 && a.x < 0.1 && a.z < 0.1 {
            Some(1)
        } else if a.z > 0.5 && a.x < 0.1 && a.y < 0.1 {
            Some(2)
        } else {
            None
        }
    });

    let center_highlighted = axis.is_some_and(|a| a.x > 0.9 && a.y > 0.9 && a.z > 0.9);

    let (xy_highlighted, yz_highlighted, xz_highlighted) =
        axis.map_or((false, false, false), |a| {
            if a.x > 1.05 && a.y > 1.05 {
                (true, false, false)
            } else if a.y > 1.05 && a.z > 1.05 {
                (false, true, false)
            } else if a.x > 1.05 && a.z > 1.05 {
                (false, false, true)
            } else {
                (false, false, false)
            }
        });

    for (index, (axis_entity, color)) in axes.iter().enumerate() {
        if let Some(entity) = axis_entity {
            let is_highlighted = Some(index) == highlighted_axis;

            let base_color = if is_highlighted {
                nalgebra_glm::vec4(1.0, 1.0, 0.0, 1.0)
            } else {
                nalgebra_glm::vec4(color.x, color.y, color.z, 1.0)
            };

            let children = crate::ecs::transform::queries::query_children(world, *entity);
            for child in children {
                if let Some(material) = world.get_material_mut(child) {
                    material.base_color = base_color.into();
                }
            }
        }
    }

    if let Some(center_entity) = axis_entities.center_cube
        && let Some(material) = world.get_material_mut(center_entity)
    {
        if center_highlighted {
            material.base_color = nalgebra_glm::vec4(1.0, 1.0, 0.0, 1.0).into();
        } else {
            material.base_color = nalgebra_glm::vec4(0.6, 0.6, 0.6, 1.0).into();
        }
    }

    if let Some(xy_plane) = axis_entities.xy_plane
        && let Some(material) = world.get_material_mut(xy_plane)
    {
        if xy_highlighted {
            material.base_color = nalgebra_glm::vec4(1.0, 1.0, 0.0, 0.7).into();
        } else {
            material.base_color = nalgebra_glm::vec4(0.4, 0.4, 0.0, 0.4).into();
        }
    }

    if let Some(yz_plane) = axis_entities.yz_plane
        && let Some(material) = world.get_material_mut(yz_plane)
    {
        if yz_highlighted {
            material.base_color = nalgebra_glm::vec4(1.0, 1.0, 0.0, 0.7).into();
        } else {
            material.base_color = nalgebra_glm::vec4(0.0, 0.4, 0.4, 0.4).into();
        }
    }

    if let Some(xz_plane) = axis_entities.xz_plane
        && let Some(material) = world.get_material_mut(xz_plane)
    {
        if xz_highlighted {
            material.base_color = nalgebra_glm::vec4(1.0, 1.0, 0.0, 0.7).into();
        } else {
            material.base_color = nalgebra_glm::vec4(0.4, 0.0, 0.4, 0.4).into();
        }
    }
}

#[derive(Clone, Copy)]
pub enum GizmoDragMode {
    None,
    Axis(Vec3),
    Plane(Vec3),
    Free,
}

pub fn set_gizmo_drag_visibility(world: &mut World, gizmo: &GizmoState, drag_mode: GizmoDragMode) {
    let axis_entities = &gizmo.axis_entities;

    match drag_mode {
        GizmoDragMode::None => {
            if let Some(x_axis) = axis_entities.x_axis {
                set_entity_visible_recursive(world, x_axis, true);
            }
            if let Some(y_axis) = axis_entities.y_axis {
                set_entity_visible_recursive(world, y_axis, true);
            }
            if let Some(z_axis) = axis_entities.z_axis {
                set_entity_visible_recursive(world, z_axis, true);
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                set_entity_visible_recursive(world, xy_plane, true);
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                set_entity_visible_recursive(world, yz_plane, true);
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                set_entity_visible_recursive(world, xz_plane, true);
            }
            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, true);
            }

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }
        }
        GizmoDragMode::Axis(axis) => {
            let mut all_parts = Vec::new();

            if let Some(x_axis) = axis_entities.x_axis {
                all_parts.push(x_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, x_axis,
                ));
            }
            if let Some(y_axis) = axis_entities.y_axis {
                all_parts.push(y_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, y_axis,
                ));
            }
            if let Some(z_axis) = axis_entities.z_axis {
                all_parts.push(z_axis);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, z_axis,
                ));
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                all_parts.push(xy_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, xy_plane,
                ));
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                all_parts.push(yz_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, yz_plane,
                ));
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                all_parts.push(xz_plane);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world, xz_plane,
                ));
            }
            if let Some(center_cube) = axis_entities.center_cube {
                all_parts.push(center_cube);
                all_parts.extend(crate::ecs::transform::queries::query_descendants(
                    world,
                    center_cube,
                ));
            }

            hide_entities(world, &all_parts);

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if axis.x > 0.5 {
                if let Some(x_axis) = axis_entities.x_axis {
                    let mut to_show = vec![x_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, x_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
            } else if axis.y > 0.5 {
                if let Some(y_axis) = axis_entities.y_axis {
                    let mut to_show = vec![y_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, y_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
            } else if axis.z > 0.5 {
                if let Some(z_axis) = axis_entities.z_axis {
                    let mut to_show = vec![z_axis];
                    to_show.extend(crate::ecs::transform::queries::query_descendants(
                        world, z_axis,
                    ));
                    show_entities(world, &to_show);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            }
        }
        GizmoDragMode::Plane(normal) => {
            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, false);
            }

            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if normal.z > 0.5 {
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, true);
                }
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, false);
                }
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, false);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, true);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, false);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
            } else if normal.x > 0.5 {
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, true);
                }
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, false);
                }
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, false);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, true);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, false);
                }
                if let Some(y_line) = axis_entities.y_axis_line {
                    show_entities(world, &[y_line]);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            } else {
                if let Some(xz_plane) = axis_entities.xz_plane {
                    set_entity_visible_recursive(world, xz_plane, true);
                }
                if let Some(xy_plane) = axis_entities.xy_plane {
                    set_entity_visible_recursive(world, xy_plane, false);
                }
                if let Some(yz_plane) = axis_entities.yz_plane {
                    set_entity_visible_recursive(world, yz_plane, false);
                }
                if let Some(x_axis) = axis_entities.x_axis {
                    set_entity_visible_recursive(world, x_axis, true);
                }
                if let Some(z_axis) = axis_entities.z_axis {
                    set_entity_visible_recursive(world, z_axis, true);
                }
                if let Some(y_axis) = axis_entities.y_axis {
                    set_entity_visible_recursive(world, y_axis, false);
                }
                if let Some(x_line) = axis_entities.x_axis_line {
                    show_entities(world, &[x_line]);
                }
                if let Some(z_line) = axis_entities.z_axis_line {
                    show_entities(world, &[z_line]);
                }
            }
        }
        GizmoDragMode::Free => {
            if let Some(x_line) = axis_entities.x_axis_line {
                hide_entities(world, &[x_line]);
            }
            if let Some(y_line) = axis_entities.y_axis_line {
                hide_entities(world, &[y_line]);
            }
            if let Some(z_line) = axis_entities.z_axis_line {
                hide_entities(world, &[z_line]);
            }

            if let Some(x_axis) = axis_entities.x_axis {
                set_entity_visible_recursive(world, x_axis, false);
            }
            if let Some(y_axis) = axis_entities.y_axis {
                set_entity_visible_recursive(world, y_axis, false);
            }
            if let Some(z_axis) = axis_entities.z_axis {
                set_entity_visible_recursive(world, z_axis, false);
            }
            if let Some(xy_plane) = axis_entities.xy_plane {
                set_entity_visible_recursive(world, xy_plane, false);
            }
            if let Some(yz_plane) = axis_entities.yz_plane {
                set_entity_visible_recursive(world, yz_plane, false);
            }
            if let Some(xz_plane) = axis_entities.xz_plane {
                set_entity_visible_recursive(world, xz_plane, false);
            }

            if let Some(center_cube) = axis_entities.center_cube {
                set_entity_visible_recursive(world, center_cube, true);
            }
        }
    }
}
