// Exact Rust translation of LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs.
// Source SHA-256: 3bd6f2996fc1948e3f7c3087642ef68e0b959e49100cc70d443c5f110d0e3001

use libremetaverse_compat_tests::assert_close;
use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, guid};
use libremetaverse_rlv::CameraRestrictions;
use libremetaverse_types::compat::{Guid, Vector3};

const SENDER_2: &str = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
const SENDER_3: &str = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb";
const TEXTURE_1: &str = "00000000-0000-4000-8000-000000000000";
const TEXTURE_2: &str = "11111111-1111-4111-8111-111111111111";

fn harness() -> RlvHarness {
    RlvHarness::new().expect("RlvService constructor")
}

fn restrictions(commands: &[(&str, Option<(&str, &str)>)]) -> CameraRestrictions {
    let h = harness();
    for (command, sender) in commands {
        let result = match sender {
            Some((id, name)) => block_on(h.process_from(command, id, name)),
            None => block_on(h.process(command)),
        };
        result.unwrap();
    }
    h.service.permissions().get_camera_restrictions().unwrap()
}

fn scalar_matches(
    commands: &[(&str, Option<(&str, &str)>)],
    getter: fn(&CameraRestrictions) -> Option<Option<f32>>,
    expected: Option<f32>,
) -> bool {
    let value = getter(&restrictions(commands)).flatten();
    match expected {
        Some(expected) => {
            value.is_some_and(|actual| (f64::from(actual) - f64::from(expected)).abs() <= 0.00001)
        }
        None => value.is_none(),
    }
}

fn texture(command: &str) -> Option<Guid> {
    restrictions(&[(command, None)]).texture().flatten()
}

fn camera_query(command: &str) -> (bool, Vec<(i32, String)>) {
    let h = harness();
    h.queries.lock().unwrap().camera_settings = Some([1.23, 123.45, 45.60, 130.0, 12.34, 91.34]);
    let processed = block_on(h.process(command)).unwrap();
    let replies = h.actions.lock().unwrap().replies.clone();
    (processed, replies)
}

fn missing_camera_settings(command: &str) -> (bool, Vec<(i32, String)>) {
    let h = harness();
    let processed = block_on(h.process(command)).unwrap();
    let replies = h.actions.lock().unwrap().replies.clone();
    (processed, replies)
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin_Single::test 5f3010d46f3f18ed57ea52916dd9e8bde27d75c707dbcc0c6d68eb9bbe6c3591 translated
#[test]
fn cam_zoom_min_single() {
    assert!(scalar_matches(
        &[("@CamZoomMin:1.5=n", None)],
        CameraRestrictions::zoom_min,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin_Multiple_SingleSender::test 0df818c7a6c90358cb52f59f972e16c769d38f448619dbdfeddd5a602f64d1d9 translated
#[test]
fn cam_zoom_min_multiple_single_sender() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMin:3.5=n", None),
            ("@CamZoomMin:4.5=n", None),
            ("@CamZoomMin:1.5=n", None),
        ],
        CameraRestrictions::zoom_min,
        Some(4.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin_Multiple_SingleSender_WithRemoval::test ed19a63ef16a600c9367f45d1a704739fd8e20e7811c7055df009676884de7dd translated
#[test]
fn cam_zoom_min_removes_matching_value() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMin:3.5=n", None),
            ("@CamZoomMin:4.5=n", None),
            ("@CamZoomMin:1.5=n", None),
            ("@CamZoomMin:8.5=n", None),
            ("@CamZoomMin:8.5=y", None),
        ],
        CameraRestrictions::zoom_min,
        Some(4.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin_Multiple_MultipleSenders::test c8ce609cf7506ae2df6e17126ebe5174f958245bedea26fe39600fb90195bdda translated
#[test]
fn cam_zoom_min_multiple_senders() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMin:3.5=n", None),
            ("@CamZoomMin:4.5=n", Some((SENDER_2, "Sender 2"))),
            ("@CamZoomMin:1.5=n", Some((SENDER_3, "Sender 3"))),
        ],
        CameraRestrictions::zoom_min,
        Some(4.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin_Off::test 8f8ec9da2b0196a51dfb3fea709de597bdb440196697523824f78ef359a5482a translated
#[test]
fn cam_zoom_min_off() {
    assert!(scalar_matches(
        &[("@CamZoomMin:1.5=n", None), ("@CamZoomMin:1.5=y", None)],
        CameraRestrictions::zoom_min,
        None,
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Default::test 5997b7c86a1b1dac59b7f6c03d38cd09f0fb12619f03f68e1167c85ebe02f7d0 translated
#[test]
fn cam_zoom_max_default() {
    assert!(scalar_matches(&[], CameraRestrictions::zoom_max, None));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Single::test 75530a051696ffd586b5643f0c9b9f1ae1c05bd30f9c3068f94cb30abbdc6d2d translated
#[test]
fn cam_zoom_max_single() {
    assert!(scalar_matches(
        &[("@CamZoomMax:1.5=n", None)],
        CameraRestrictions::zoom_max,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Multiple_SingleSender::test 44d6a5b2a9462c83047f5b4ec9f4e66bb518f6547ec08df2b5420850129ff7cb translated
#[test]
fn cam_zoom_max_multiple_single_sender() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMax:3.5=n", None),
            ("@CamZoomMax:4.5=n", None),
            ("@CamZoomMax:1.5=n", None),
        ],
        CameraRestrictions::zoom_max,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Multiple_SingleSender_WithRemoval::test 4958ce547ff71cf18d31cd557ecaecb8ae0a19b169c2051bd6797769bbcde26e translated
#[test]
fn cam_zoom_max_removes_matching_value() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMax:3.5=n", None),
            ("@CamZoomMax:4.5=n", None),
            ("@CamZoomMax:1.5=n", None),
            ("@CamZoomMax:0.5=n", None),
            ("@CamZoomMax:0.5=y", None),
        ],
        CameraRestrictions::zoom_max,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Multiple_MultipleSenders::test 76346d0c1f4ecf22d09c1950aef99207d28ffe5576b7bc0be1212b91d6518f2c translated
#[test]
fn cam_zoom_max_multiple_senders() {
    assert!(scalar_matches(
        &[
            ("@CamZoomMax:3.5=n", None),
            ("@CamZoomMax:4.5=n", Some((SENDER_2, "Sender 2"))),
            ("@CamZoomMax:1.5=n", Some((SENDER_3, "Sender 3"))),
        ],
        CameraRestrictions::zoom_max,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax_Off::test 88e2ec791d3f78bb6551b7d37a52cc5dc18c156ddd483bd6ba55288144005e5c translated
#[test]
fn cam_zoom_max_off() {
    assert!(scalar_matches(
        &[("@CamZoomMax:1.5=n", None), ("@CamZoomMax:1.5=y", None)],
        CameraRestrictions::zoom_max,
        None,
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMin::test cf5f8bfbea933b64bc282211f498b01eefab0b31728800e2965a8a489f85b15d translated
#[test]
fn cam_zoom_min_command() {
    assert!(scalar_matches(
        &[("@CamZoomMin:0.5=n", None)],
        CameraRestrictions::zoom_min,
        Some(0.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamZoomMax::test 7441bff0655432365bb702cce92465ab0ab7b7dd195b732312a4e243f975ba48 translated
#[test]
fn cam_zoom_max_command() {
    assert!(scalar_matches(
        &[("@CamZoomMax:1.5=n", None)],
        CameraRestrictions::zoom_max,
        Some(1.5),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamFovMin::test b433f776bfa059a889b1a13ebc0841c824715194b6b0634dea1dd5740d5b7bbe translated
#[test]
fn set_cam_fov_min() {
    assert!(scalar_matches(
        &[("@setcam_fovmin:15=n", None)],
        CameraRestrictions::fov_min,
        Some(15.0),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamFovMax::test e0f313a03073b283400130e5a91c8c4157c9b83f9001f1ad1badf15f80821663 translated
#[test]
fn set_cam_fov_max() {
    assert!(scalar_matches(
        &[("@setcam_fovmax:45=n", None)],
        CameraRestrictions::fov_max,
        Some(45.0),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamFov::test 02118d87dfda863c31de56267a31beba94ff124ca225f56d67222b6ffaf0c6f9 translated
#[test]
fn set_cam_fov_records_exact_value() {
    let h = harness();
    assert!(block_on(h.process("@setcam_fov:1.75=force")).unwrap());
    assert_eq!(h.actions.lock().unwrap().camera_fovs, vec![1.75]);
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamFov_Restricted::test 34b9c620b61777ece7ad140fc15dde6d2b77d877af597cf2a1b5d5f768cd5fc7 translated
#[test]
fn set_cam_fov_restricted() {
    let h = harness();
    block_on(h.process("@setcam_unlock=n")).unwrap();
    assert!(!block_on(h.process("@setcam_fov:1.75=force")).unwrap());
    assert!(h.actions.lock().unwrap().camera_fovs.is_empty());
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamFov_Restricted_Synonym::test d8824c6f673ea7c101d309643ed4dbacc0fd6b17c0bda13f8cbb16b956529db4 translated
#[test]
fn set_cam_fov_restricted_by_synonym() {
    let h = harness();
    block_on(h.process("@camunlock=n")).unwrap();
    assert!(!block_on(h.process("@setcam_fov:1.75=force")).unwrap());
    assert!(h.actions.lock().unwrap().camera_fovs.is_empty());
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamAvDistMax::test 61206ccc9664f63057389d0d9540022ba82da88b1e213387a9a7f0d0cec050b3 translated
#[test]
fn set_cam_av_dist_max() {
    assert!(scalar_matches(
        &[("@setcam_avdistmax:30=n", None)],
        CameraRestrictions::av_dist_max,
        Some(30.0),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamAvDistMax_Synonym::test 9742afe1eb83fe3b63f6ce195bb1fb08058a6272e9817b3d1e09c50ca914a66a translated
#[test]
fn set_cam_av_dist_max_synonym() {
    assert!(scalar_matches(
        &[("@camdistmax:30=n", None)],
        CameraRestrictions::av_dist_max,
        Some(30.0),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamAvDistMin::test be661f7ae623eafe2c00a7eb80a716ab832cd56be904725cfa01c16ab219d10e translated
#[test]
fn set_cam_av_dist_min() {
    assert!(scalar_matches(
        &[("@setcam_avdistmin:0.3=n", None)],
        CameraRestrictions::av_dist_min,
        Some(0.3),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamAvDistMin_Synonym::test da29486a2c8bc2801805774269743566457ca1b9b316dc6846cf499b60aa588d translated
#[test]
fn set_cam_av_dist_min_synonym() {
    assert!(scalar_matches(
        &[("@camdistmin:0.3=n", None)],
        CameraRestrictions::av_dist_min,
        Some(0.3),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawAlphaMax::test 96e0f72499cdf258062ca0610594137c72f232ddf936c0cc929429368957d3ed translated
#[test]
fn cam_draw_alpha_max() {
    assert!(scalar_matches(
        &[("@CamDrawAlphaMax:0.9=n", None)],
        CameraRestrictions::draw_alpha_max,
        Some(0.9),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawMin::test 53a901a99baeb1f8d45a07d1347a6491d66ad8ca76cdef5304c64738e486e7a9 translated
#[test]
fn cam_draw_min() {
    assert!(scalar_matches(
        &[("@camdrawmin:1.75=n", None)],
        CameraRestrictions::draw_min,
        Some(1.75),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawMin_Small::test 3f531bfcd6e08830912fd4d619a8ffc5e3ab40e9d036769f742ae0d06207400e translated
#[test]
fn cam_draw_min_rejects_too_small_value() {
    let h = harness();
    assert!(!block_on(h.process("@camdrawmin:0.15=n")).unwrap());
    assert!(
        h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .draw_min()
            .flatten()
            .is_none()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawMax::test 71ab081b330b9fc4cc73a06b214f52e577ab25c0ff15439e18737dfb220d7aaf translated
#[test]
fn cam_draw_max() {
    assert!(scalar_matches(
        &[("@camdrawmax:1.75=n", None)],
        CameraRestrictions::draw_max,
        Some(1.75),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawMax_Small::test 72d0752ae72ba5288d2b96fca3e0a29c162406b38588160e6801b211c82fac11 translated
#[test]
fn cam_draw_max_rejects_too_small_value() {
    let h = harness();
    assert!(!block_on(h.process("@camdrawmax:0.15=n")).unwrap());
    assert!(
        h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .draw_max()
            .flatten()
            .is_none()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawAlphaMin::test 697fafc1837d56255cdf5e2a09634dcc740651d45df04a712c030a224a58d0c2 translated
#[test]
fn cam_draw_alpha_min() {
    assert!(scalar_matches(
        &[("@camdrawalphamin:1.75=n", None)],
        CameraRestrictions::draw_alpha_min,
        Some(1.75),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor::test b3bc0346dbcf6c6b03d9c2c38b225e7903754fcbc56c74d098dbdcb617d2426d translated
#[test]
fn cam_draw_color() {
    let Vector3(value) = restrictions(&[("@CamDrawColor:0.1;0.2;0.3=n", None)])
        .draw_color()
        .flatten()
        .unwrap();
    for (actual, expected) in value.into_iter().zip([0.1, 0.2, 0.3]) {
        assert_close(f64::from(actual), expected, 0.00001);
    }
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor_Default::test fedba7874c0b897309caca0e007cb9fbcbce8cd5155c91ec94bf6d1f391d47a1 translated
#[test]
fn cam_draw_color_default() {
    assert!(restrictions(&[]).draw_color().flatten().is_none());
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor_Large::test 6d2f0a4a24e71afbca88f3dfe574ff8ee5825e4df9a08552ac4af22a0a6b9f5e translated
#[test]
#[allow(clippy::float_cmp)] // The C# test requires exact clamped endpoint values.
fn cam_draw_color_clamps_large_components() {
    let Vector3(value) = restrictions(&[("@CamDrawColor:5;6;7=n", None)])
        .draw_color()
        .flatten()
        .unwrap();
    assert_eq!(value, [1.0, 1.0, 1.0]);
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor_Negative::test 5f7ae954b3f75c5a86da8222a5eadc42ac3fee6ac4134455fb2a7a2e24101081 translated
#[test]
#[allow(clippy::float_cmp)] // The C# test requires exact clamped endpoint values.
fn cam_draw_color_clamps_negative_components() {
    let Vector3(value) = restrictions(&[("@CamDrawColor:-5;-6;-7=n", None)])
        .draw_color()
        .flatten()
        .unwrap();
    assert_eq!(value, [0.0, 0.0, 0.0]);
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor_Removal::test 81322059c2a6e0054c1d2131e0698c614942eec69958f72315545e9b1517fcb5 translated
#[test]
fn cam_draw_color_removal() {
    assert!(
        restrictions(&[
            ("@CamDrawColor:0.1;0.2;0.3=n", None),
            ("@CamDrawColor:0.1;0.2;0.3=y", None),
        ])
        .draw_color()
        .flatten()
        .is_none()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamDrawColor_Multi::test 21b9615f5a7e809ec83442035588414101fdf3862f282f1a501fb15d904f981e translated
#[test]
fn cam_draw_color_averages_multiple_values() {
    let Vector3(value) = restrictions(&[
        ("@CamDrawColor:0.1;0.2;0.3=n", None),
        ("@CamDrawColor:0.2;0.3;0.6=n", None),
    ])
    .draw_color()
    .flatten()
    .unwrap();
    for (actual, expected) in value.into_iter().zip([0.15, 0.25, 0.45]) {
        assert_close(f64::from(actual), expected, 0.00001);
    }
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CanSetCamUnlock::test 3983d83fb0086bc71a585eeda9b561c738ba1eb99365182115a673ab38e166a1 translated
#[test]
fn set_cam_unlock_toggles_lock() {
    let h = harness();
    block_on(h.process("@setcam_unlock=n")).unwrap();
    assert!(
        h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .is_locked()
    );
    block_on(h.process("@setcam_unlock=y")).unwrap();
    assert!(
        !h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .is_locked()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CanCamUnlock::test b116d0b63222b30c67da7c08d48f10701f5def2bdd3584166939d713a86a8cd4 translated
#[test]
fn cam_unlock_synonym_toggles_lock() {
    let h = harness();
    block_on(h.process("@camunlock=n")).unwrap();
    assert!(
        h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .is_locked()
    );
    block_on(h.process("@camunlock=y")).unwrap();
    assert!(
        !h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .is_locked()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CamAvDist::test 4c5641b0e975136e815bfaf8d9821240f0ac2bc615ed105fa5190dfdbcddc158 translated
#[test]
fn cam_av_dist() {
    assert!(scalar_matches(
        &[("@CamAvDist:5=n", None)],
        CameraRestrictions::av_dist,
        Some(5.0),
    ));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures::case:0fdad12f3bfb0585 4b5058bfacb3e7f282bb83d0068ff9c7c7464b0d4119f879d670ddaf34d469b6 translated
#[test]
fn set_cam_textures_empty_uuid() {
    assert_eq!(texture("@setcam_textures=n"), Some(Guid([0; 16])));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures::case:19c11fd923cc347d 4b5058bfacb3e7f282bb83d0068ff9c7c7464b0d4119f879d670ddaf34d469b6 translated
#[test]
fn cam_textures_empty_uuid() {
    assert_eq!(texture("@camtextures=n"), Some(Guid([0; 16])));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Single::case:0fdad12f3bfb0585 abe9a6cff61faf1afbd29c4b073d34d67cf5e77058df44b68cbed7e1d9569814 translated
#[test]
fn set_cam_textures_single() {
    assert_eq!(
        texture(&format!("@setcam_textures:{TEXTURE_1}=n")),
        Some(guid(TEXTURE_1))
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Single::case:19c11fd923cc347d abe9a6cff61faf1afbd29c4b073d34d67cf5e77058df44b68cbed7e1d9569814 translated
#[test]
fn cam_textures_single() {
    assert_eq!(
        texture(&format!("@camtextures:{TEXTURE_1}=n")),
        Some(guid(TEXTURE_1))
    );
}

fn texture_synonyms(first: &str, second: &str) -> [Option<Guid>; 2] {
    let h = harness();
    block_on(h.process(&format!("@{first}:{TEXTURE_1}=n"))).unwrap();
    block_on(h.process(&format!("@{second}:{TEXTURE_2}=n"))).unwrap();
    let second_texture = h
        .service
        .permissions()
        .get_camera_restrictions()
        .unwrap()
        .texture()
        .flatten();
    block_on(h.process(&format!("@{first}:{TEXTURE_2}=y"))).unwrap();
    [
        second_texture,
        h.service
            .permissions()
            .get_camera_restrictions()
            .unwrap()
            .texture()
            .flatten(),
    ]
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Multiple_Synonyms::case:1c2423e3f02a5f03 42abc61468c54b5e6e95d27fbadfef6010ce7d4d9b6dba8238f9e863a2ec23c7 translated
#[test]
fn set_cam_textures_then_set_cam_textures() {
    assert_eq!(
        texture_synonyms("setcam_textures", "setcam_textures"),
        [Some(guid(TEXTURE_2)), Some(guid(TEXTURE_1))]
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Multiple_Synonyms::case:57689650dc307766 42abc61468c54b5e6e95d27fbadfef6010ce7d4d9b6dba8238f9e863a2ec23c7 translated
#[test]
fn set_cam_textures_then_cam_textures() {
    assert_eq!(
        texture_synonyms("setcam_textures", "camtextures"),
        [Some(guid(TEXTURE_2)), Some(guid(TEXTURE_1))]
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Multiple_Synonyms::case:081becc69dc8e183 42abc61468c54b5e6e95d27fbadfef6010ce7d4d9b6dba8238f9e863a2ec23c7 translated
#[test]
fn cam_textures_then_cam_textures() {
    assert_eq!(
        texture_synonyms("camtextures", "camtextures"),
        [Some(guid(TEXTURE_2)), Some(guid(TEXTURE_1))]
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.SetCamTextures_Multiple_Synonyms::case:d96d157e365ded15 42abc61468c54b5e6e95d27fbadfef6010ce7d4d9b6dba8238f9e863a2ec23c7 translated
#[test]
fn cam_textures_then_set_cam_textures() {
    assert_eq!(
        texture_synonyms("camtextures", "setcam_textures"),
        [Some(guid(TEXTURE_2)), Some(guid(TEXTURE_1))]
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamAvDistMin::test 60d28627494c147228930839a18016e3cbfa8eb042167965647794902eb340a6 translated
#[test]
fn get_cam_av_dist_min() {
    assert_eq!(
        camera_query("@getcam_avdistmin=1234"),
        (true, vec![(1234, "1.23".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamAvDistMax::test a7cf7887d2934984cc13e10f3afa3248a5e677c9fac680af2a68c3a27d76c3da translated
#[test]
fn get_cam_av_dist_max() {
    assert_eq!(
        camera_query("@getcam_avdistmax=1234"),
        (true, vec![(1234, "123.45".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamFovMin::test 25a040ca69bf4c2a303d45314fec99e70371bf6ac46e1793384e0a626deb4e75 translated
#[test]
fn get_cam_fov_min() {
    assert_eq!(
        camera_query("@getcam_fovmin=1234"),
        (true, vec![(1234, "45.6".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamFovMax::test 19153fa188ef6bd763235a03048771d1a1d824c1aeaf64841b7e6e70c35813ae translated
#[test]
fn get_cam_fov_max() {
    assert_eq!(
        camera_query("@getcam_fovmax=1234"),
        (true, vec![(1234, "130".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamZoomMin::test 8cd72b3f1922b913c6bde59b928f362fa191574dc1df3cfb4668d4ccf00515a9 translated
#[test]
fn get_cam_zoom_min() {
    assert_eq!(
        camera_query("@getcam_zoommin=1234"),
        (true, vec![(1234, "12.34".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.GetCamFov::test f4031d095b9a7e87d1f1d94e52c15c3c9b2b43aa1ba039c83e391e58ea994c40 translated
#[test]
fn get_cam_fov() {
    assert_eq!(
        camera_query("@getcam_fov=1234"),
        (true, vec![(1234, "91.34".into())])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:6e89e27b90f28739 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_zoom_min() {
    assert_eq!(
        missing_camera_settings("@getcam_zoommin=1234"),
        (false, vec![])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:82ff323c4cfaad18 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_fov() {
    assert_eq!(missing_camera_settings("@getcam_fov=1234"), (false, vec![]));
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:c64b136643c55ed8 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_fov_max() {
    assert_eq!(
        missing_camera_settings("@getcam_fovmax=1234"),
        (false, vec![])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:32ad2cfbbfcf0638 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_av_dist_min() {
    assert_eq!(
        missing_camera_settings("@getcam_avdistmin=1234"),
        (false, vec![])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:21855f1f68a52025 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_av_dist_max() {
    assert_eq!(
        missing_camera_settings("@getcam_avdistmax=1234"),
        (false, vec![])
    );
}

// parity-case: LibreMetaverse.Tests/RLV/RestrictionsCameraTests.cs::RestrictionsCameraTests.CameraSettings_Default::case:065ec3bc69e3f2b0 4775ab3cf13fcafd3f6177ac11cecc144040d1e5c11293b442591bdfd525c0fe translated
#[test]
fn camera_settings_default_get_fov_min() {
    assert_eq!(
        missing_camera_settings("@getcam_fovmin=1234"),
        (false, vec![])
    );
}
