//! Golden fixtures for the native visual-parameter and foliage generators.

#![allow(clippy::float_cmp)] // Golden XML values must retain their exact f32 representation.

use libremetaverse::{
    DrivenParamInfo, Grass, GrassDefinitions, SkeletalBoneInfo, Tree, TreeDefinitions,
    VisualAlphaParam, VisualColorOperation, VisualColorParam, VisualParam, VisualParams,
    VolumeMorphInfo,
};
use libremetaverse_types::{Color4, UUID, Vector3};

#[test]
fn visual_catalog_matches_the_pinned_character_fixture() {
    let params = VisualParams::params();
    let transmitted = VisualParams::group0_param_ids();

    assert_eq!(params.0.len(), 672);
    assert_eq!(transmitted.len(), 253);
    assert_eq!(transmitted.first(), Some(&1));
    assert_eq!(transmitted.last(), Some(&11_001));
    assert!(transmitted.windows(2).all(|ids| ids[0] < ids[1]));
    assert!(transmitted.iter().all(|id| {
        params
            .0
            .get(id)
            .is_some_and(|param| matches!(param.group, 0 | 3))
    }));

    assert_eq!(
        params
            .0
            .values()
            .filter(|param| param.alpha_params.is_some())
            .count(),
        94
    );
    assert_eq!(
        params
            .0
            .values()
            .filter(|param| param.color_params.is_some())
            .count(),
        104
    );
    assert_eq!(
        params
            .0
            .values()
            .filter(|param| param.driven_params.is_some())
            .count(),
        130
    );
    assert_eq!(
        params
            .0
            .values()
            .filter(|param| param.skeletal_distortions.is_some())
            .count(),
        82
    );
    assert_eq!(
        params
            .0
            .values()
            .filter(|param| param.volume_morphs.is_some())
            .count(),
        30
    );

    let height = VisualParams::find("Height".to_owned(), Some("shape".to_owned())).unwrap();
    assert_eq!(height.param_id, 33);
    assert_eq!(height.group, 0);
    assert_eq!(height.label_min, "Short");
    assert_eq!(height.label_max, "Tall");
    assert_eq!(height.default_value, 0.0);
    assert_eq!(height.min_value, -2.3);
    assert_eq!(height.max_value, 2.0);
    assert_eq!(height.skeletal_distortions.as_ref().unwrap().len(), 35);

    let missing = VisualParams::find("missing".to_owned(), None).unwrap();
    assert_eq!(missing, VisualParam::default());
}

#[test]
fn visual_metadata_constructors_preserve_every_argument() {
    let alpha = VisualAlphaParam::new(0.25, "mask.tga".to_owned(), true, false).unwrap();
    assert_eq!(alpha.domain, 0.25);
    assert_eq!(alpha.tga_file, "mask.tga");
    assert!(alpha.skip_if_zero);
    assert!(!alpha.multiply_blend);

    let colors = vec![Color4 {
        r: 0.1,
        g: 0.2,
        b: 0.3,
        a: 0.4,
    }];
    let color = VisualColorParam::new(VisualColorOperation::Blend, colors.clone()).unwrap();
    assert_eq!(color.operation, VisualColorOperation::Blend);
    assert_eq!(color.colors, colors);

    let driven = DrivenParamInfo::new(91, 0.1, 0.2, 0.8, 0.9, true).unwrap();
    assert_eq!(driven.param_id, 91);
    assert_eq!(driven.min1, 0.1);
    assert_eq!(driven.max1, 0.2);
    assert_eq!(driven.max2, 0.8);
    assert_eq!(driven.min2, 0.9);
    assert!(driven.has_range);

    let scale = Vector3 {
        x: 1.0,
        y: 2.0,
        z: 3.0,
    };
    let position = Vector3 {
        x: 4.0,
        y: 5.0,
        z: 6.0,
    };
    let skeletal = SkeletalBoneInfo::new("mBone".to_owned(), scale, position, true).unwrap();
    assert_eq!(skeletal.bone_name, "mBone");
    assert_eq!(skeletal.scale_deformation, scale);
    assert_eq!(skeletal.position_deformation, position);
    assert!(skeletal.has_position_deformation);

    let volume = VolumeMorphInfo::new("BELLY".to_owned(), scale, true, position, true).unwrap();
    assert_eq!(volume.bone_name, "BELLY");
    assert_eq!(volume.scale_delta, scale);
    assert_eq!(volume.position_delta, position);
    assert!(volume.has_scale);
    assert!(volume.has_position);

    let param = VisualParam::new(
        7,
        "Test".to_owned(),
        3,
        Some("shape".to_owned()),
        "Label".to_owned(),
        "Low".to_owned(),
        "High".to_owned(),
        0.5,
        -1.0,
        2.0,
        true,
        Some(vec![91]),
        Some(Some(alpha.clone())),
        Some(Some(color.clone())),
        Some(vec![driven.clone()]),
        Some(vec![skeletal.clone()]),
        Some(vec![volume.clone()]),
    )
    .unwrap();
    assert_eq!(param.param_id, 7);
    assert_eq!(param.name, "Test");
    assert_eq!(param.group, 3);
    assert_eq!(param.wearable.as_deref(), Some("shape"));
    assert_eq!(param.label, "Label");
    assert_eq!(param.label_min, "Low");
    assert_eq!(param.label_max, "High");
    assert_eq!(param.default_value, 0.5);
    assert_eq!(param.min_value, -1.0);
    assert_eq!(param.max_value, 2.0);
    assert!(param.is_bump_attribute);
    assert_eq!(param.drivers.as_deref(), Some([91].as_slice()));
    assert_eq!(param.alpha_params, Some(Some(alpha)));
    assert_eq!(param.color_params, Some(Some(color)));
    assert_eq!(param.driven_params, Some(vec![driven]));
    assert_eq!(param.skeletal_distortions, Some(vec![skeletal]));
    assert_eq!(param.volume_morphs, Some(vec![volume]));
}

#[test]
fn tree_catalog_matches_every_pine_one_field() {
    let trees = TreeDefinitions::all();
    assert_eq!(trees.len(), 21);
    for (species_id, tree) in trees.iter().enumerate() {
        assert_eq!(tree.species_id(), i32::try_from(species_id).unwrap());
    }

    let pine = TreeDefinitions::get(Tree::Pine1).unwrap();
    assert_eq!(pine.name(), "Pine 1");
    assert_eq!(pine.species_id(), 0);
    assert_eq!(
        pine.texture_id(),
        UUID::parse("0187babf-6c0d-5891-ebed-4ecab1426683".to_owned()).unwrap()
    );
    assert_eq!(pine.droop(), 60.0);
    assert_eq!(pine.twist(), 5.0);
    assert_eq!(pine.branches(), 5.0);
    assert_eq!(pine.depth(), 1);
    assert_eq!(pine.scale_step(), 0.7);
    assert_eq!(pine.trunk_depth(), 6.0);
    assert_eq!(pine.branch_length(), 8.0);
    assert_eq!(pine.trunk_length(), 11.5);
    assert_eq!(pine.leaf_scale(), 22.0);
    assert_eq!(pine.billboard_scale(), 39.5);
    assert_eq!(pine.billboard_ratio(), 1.1);
    assert_eq!(pine.trunk_aspect(), 0.1);
    assert_eq!(pine.branch_aspect(), 0.05);
    assert_eq!(pine.leaf_rotate(), 20.0);
    assert_eq!(pine.noise_mag(), 0.5);
    assert_eq!(pine.noise_scale(), 2.5);
    assert_eq!(pine.taper(), 0.8);
    assert_eq!(pine.repeat_z(), 3);

    let replacement_texture =
        UUID::parse("00000000-0000-0000-0000-000000000001".to_owned()).unwrap();
    let mut changed = pine;
    changed.set_name("Changed".to_owned());
    changed.set_species_id(42);
    changed.set_texture_id(replacement_texture);
    changed.set_droop(1.0);
    changed.set_twist(2.0);
    changed.set_branches(3.0);
    changed.set_depth(4);
    changed.set_scale_step(5.0);
    changed.set_trunk_depth(6.0);
    changed.set_branch_length(7.0);
    changed.set_trunk_length(8.0);
    changed.set_leaf_scale(9.0);
    changed.set_billboard_scale(10.0);
    changed.set_billboard_ratio(11.0);
    changed.set_trunk_aspect(12.0);
    changed.set_branch_aspect(13.0);
    changed.set_leaf_rotate(14.0);
    changed.set_noise_mag(15.0);
    changed.set_noise_scale(16.0);
    changed.set_taper(17.0);
    changed.set_repeat_z(18);
    assert_eq!(changed.name(), "Changed");
    assert_eq!(changed.species_id(), 42);
    assert_eq!(changed.texture_id(), replacement_texture);
    assert_eq!(changed.droop(), 1.0);
    assert_eq!(changed.twist(), 2.0);
    assert_eq!(changed.branches(), 3.0);
    assert_eq!(changed.depth(), 4);
    assert_eq!(changed.scale_step(), 5.0);
    assert_eq!(changed.trunk_depth(), 6.0);
    assert_eq!(changed.branch_length(), 7.0);
    assert_eq!(changed.trunk_length(), 8.0);
    assert_eq!(changed.leaf_scale(), 9.0);
    assert_eq!(changed.billboard_scale(), 10.0);
    assert_eq!(changed.billboard_ratio(), 11.0);
    assert_eq!(changed.trunk_aspect(), 12.0);
    assert_eq!(changed.branch_aspect(), 13.0);
    assert_eq!(changed.leaf_rotate(), 14.0);
    assert_eq!(changed.noise_mag(), 15.0);
    assert_eq!(changed.noise_scale(), 16.0);
    assert_eq!(changed.taper(), 17.0);
    assert_eq!(changed.repeat_z(), 18);
}

#[test]
fn grass_catalog_matches_the_pinned_fixture_and_properties_are_mutable() {
    let grasses = GrassDefinitions::all();
    assert_eq!(grasses.len(), 6);
    for (species_id, grass) in grasses.iter().enumerate() {
        assert_eq!(grass.species_id(), i32::try_from(species_id).unwrap());
    }

    let mut undergrowth = GrassDefinitions::get(Grass::Undergrowth1).unwrap();
    assert_eq!(undergrowth.name(), "undergrowth_1");
    assert_eq!(
        undergrowth.texture_id(),
        UUID::parse("8f458549-173b-23ff-d4ff-bfaa5ea2371b".to_owned()).unwrap()
    );
    assert_eq!(undergrowth.blade_size_x(), 2.0);
    assert_eq!(undergrowth.blade_size_y(), 2.0);

    undergrowth.set_name("custom".to_owned());
    undergrowth.set_species_id(12);
    let replacement_texture =
        UUID::parse("00000000-0000-0000-0000-000000000002".to_owned()).unwrap();
    undergrowth.set_texture_id(replacement_texture);
    undergrowth.set_blade_size_x(3.5);
    undergrowth.set_blade_size_y(4.5);
    assert_eq!(undergrowth.name(), "custom");
    assert_eq!(undergrowth.species_id(), 12);
    assert_eq!(undergrowth.texture_id(), replacement_texture);
    assert_eq!(undergrowth.blade_size_x(), 3.5);
    assert_eq!(undergrowth.blade_size_y(), 4.5);
}
