// Exact Rust translations of far-touch, touch, sit-teleport, and teleport restrictions.

use libremetaverse_compat_tests::block_on;
use libremetaverse_compat_tests::rlv_support::{RlvHarness, guid};
use libremetaverse_rlv::RlvPermissionsServiceTouchLocation as TouchLocation;

const OBJECT_1: &str = "00000000-0000-4000-8000-000000000000";
const OBJECT_2: &str = "11111111-1111-4111-8111-111111111111";
const USER_1: &str = "55555555-5555-4555-8555-555555555555";
const USER_2: &str = "66666666-6666-4666-8666-666666666666";
const SENDER_2: &str = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
const SENDER_3: &str = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb";

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

fn process(h: &RlvHarness, commands: &[&str]) {
    for command in commands {
        block_on(h.process(command)).unwrap();
    }
}

fn far_touch(command: &str) -> (bool, f32) {
    let h = harness();
    process(&h, &[command]);
    let mut distance = 0.0;
    let found = h
        .service
        .permissions()
        .try_get_max_far_touch_distance(&mut distance);
    (found, distance)
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch::case:ccd63fc5b9c064fb a5568e0040a3c0ac8875a11596b7d0799ac5169b919e4fc65587555b2d3edd52 translated
#[test]
fn far_touch_fartouch_value() {
    assert_eq!(far_touch("@fartouch:0.9=n"), (true, 0.9));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch::case:ae3d093c6c7267ac a5568e0040a3c0ac8875a11596b7d0799ac5169b919e4fc65587555b2d3edd52 translated
#[test]
fn far_touch_touchfar_value() {
    assert_eq!(far_touch("@touchfar:0.9=n"), (true, 0.9));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Synonym::case:ccd63fc5b9c064fb c7aff866d539ceb26cc028532a901f18b58bb020f34da56444f08c9f4df1eb52 translated
#[test]
fn far_touch_fartouch_synonym() {
    assert_eq!(far_touch("@fartouch:0.9=n"), (true, 0.9));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Synonym::case:ae3d093c6c7267ac c7aff866d539ceb26cc028532a901f18b58bb020f34da56444f08c9f4df1eb52 translated
#[test]
fn far_touch_touchfar_synonym() {
    assert_eq!(far_touch("@touchfar:0.9=n"), (true, 0.9));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Default::case:ccd63fc5b9c064fb e894c744ea780a0a46cd2387157077c1a62c3c6b982303f3b7bff0b5d950816a translated
#[test]
fn far_touch_fartouch_default() {
    assert_eq!(far_touch("@fartouch=n"), (true, 1.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Default::case:ae3d093c6c7267ac e894c744ea780a0a46cd2387157077c1a62c3c6b982303f3b7bff0b5d950816a translated
#[test]
fn far_touch_touchfar_default() {
    assert_eq!(far_touch("@touchfar=n"), (true, 1.5));
}

fn far_touch_multiple_matches(first: &str, second: &str) -> bool {
    let h = harness();
    process(
        &h,
        &[&format!("@{first}:12.34=n"), &format!("@{second}:6.78=n")],
    );
    let mut before_removal = 0.0;
    let found_before = h
        .service
        .permissions()
        .try_get_max_far_touch_distance(&mut before_removal);
    process(&h, &[&format!("@{first}:6.78=y")]);
    let mut after_removal = 0.0;
    let found_after = h
        .service
        .permissions()
        .try_get_max_far_touch_distance(&mut after_removal);
    found_before
        && found_after
        && (after_removal - 12.34).abs() < 0.0001
        && (before_removal - 6.78).abs() < 0.0001
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Multiple_Synonyms::case:5788809ef8877342 423a8781a2de94943fa028ac64d61532509c90375cf1ca80edf1d0ef8edd9905 translated
#[test]
fn far_touch_multiple_fartouch_fartouch() {
    assert!(far_touch_multiple_matches("fartouch", "fartouch"));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Multiple_Synonyms::case:b3ebeae1885ed5db 423a8781a2de94943fa028ac64d61532509c90375cf1ca80edf1d0ef8edd9905 translated
#[test]
fn far_touch_multiple_fartouch_touchfar() {
    assert!(far_touch_multiple_matches("fartouch", "touchfar"));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Multiple_Synonyms::case:07cf5c5cabecfebf 423a8781a2de94943fa028ac64d61532509c90375cf1ca80edf1d0ef8edd9905 translated
#[test]
fn far_touch_multiple_touchfar_touchfar() {
    assert!(far_touch_multiple_matches("touchfar", "touchfar"));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/FarTouchRestrictionTests.cs::FarTouchRestrictionTests.CanFarTouch_Multiple_Synonyms::case:8008f95bc76b47e9 423a8781a2de94943fa028ac64d61532509c90375cf1ca80edf1d0ef8edd9905 translated
#[test]
fn far_touch_multiple_touchfar_fartouch() {
    assert!(far_touch_multiple_matches("touchfar", "fartouch"));
}

fn sit_tp(h: &RlvHarness) -> (bool, f32) {
    let mut distance = 0.0;
    let active = h.service.permissions().can_sit_tp(&mut distance).unwrap();
    (active, distance)
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Default::test ddcdba94af060ede43f0d585688966ccd923238a2365164b32a301b04d83cdfc translated
#[test]
fn sit_tp_default() {
    assert_eq!(sit_tp(&harness()), (false, 1.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Single::test 041c57829ab1032eed79e6f47c8b9026124b681fb6ffa4b3c5ac16b16d0b354b translated
#[test]
fn sit_tp_single() {
    let h = harness();
    process(&h, &["@SitTp:2.5=n"]);
    assert_eq!(sit_tp(&h), (true, 2.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Multiple_SingleSender::test feed9f3cca3395e1eb5a8d831304a4171e79f6e1d93f03a4aca0e27edba5ed6d translated
#[test]
fn sit_tp_multiple_single_sender() {
    let h = harness();
    process(&h, &["@SitTp:3.5=n", "@SitTp:4.5=n", "@SitTp:2.5=n"]);
    assert_eq!(sit_tp(&h), (true, 2.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Multiple_SingleSender_WithRemoval::test 4b26c525e0a13fbc79b280f0a6c3b0f9e3e0e664241e9f55a88eda3dd80b203a translated
#[test]
fn sit_tp_multiple_single_sender_with_removal() {
    let h = harness();
    process(
        &h,
        &[
            "@SitTp:3.5=n",
            "@SitTp:4.5=n",
            "@SitTp:2.5=n",
            "@SitTp:8.5=n",
            "@SitTp:8.5=y",
        ],
    );
    assert_eq!(sit_tp(&h), (true, 2.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Multiple_MultipleSenders::test cd9e3508d44e2e008609ac0cc12d3e207ec454a3f5c6e36aa325d33be9b6718f translated
#[test]
fn sit_tp_multiple_senders() {
    let h = harness();
    process(&h, &["@SitTp:3.5=n"]);
    block_on(h.process_from("@SitTp:4.5=n", SENDER_2, "Sender 2")).unwrap();
    block_on(h.process_from("@SitTp:2.5=n", SENDER_3, "Sender 3")).unwrap();
    assert_eq!(sit_tp(&h), (true, 2.5));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/SitTpRestrictionTests.cs::SitTpRestrictionTests.CanSitTp_Off::test e871d15d797e0f8f0b02613c2a421b47b87068c8757bbd0104cd15d406137854 translated
#[test]
fn sit_tp_off() {
    let h = harness();
    process(&h, &["@SitTp:2.5=n", "@SitTp:2.5=y"]);
    assert_eq!(sit_tp(&h), (false, 1.5));
}

fn touch(
    h: &RlvHarness,
    location: TouchLocation,
    object: &str,
    user: Option<&str>,
    distance: Option<f32>,
) -> bool {
    h.service
        .permissions()
        .can_touch(
            location,
            guid(object),
            user.map(|value| Some(guid(value))),
            distance.map(Some),
        )
        .unwrap()
}

fn touch_matrix(h: &RlvHarness) -> [bool; 4] {
    [
        touch(h, TouchLocation::AttachedSelf, OBJECT_1, None, None),
        touch(
            h,
            TouchLocation::AttachedOther,
            OBJECT_1,
            Some(USER_1),
            None,
        ),
        touch(h, TouchLocation::RezzedInWorld, OBJECT_1, None, Some(5.0)),
        touch(h, TouchLocation::Hud, OBJECT_1, None, None),
    ]
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAllRestrictionTests.cs::TouchAllRestrictionTests.TouchAll::test 06da3251125bf4d3b2d542cfba7fa0a7b0c18dc0f97024a57c0e0ddf6cfc8dd6 translated
#[test]
fn touch_all_default_permissions() {
    assert_eq!(touch_matrix(&harness()), [true, true, true, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAllRestrictionTests.cs::TouchAllRestrictionTests.TouchAll_default::test 9526d7bed6473afd41f97fb11ec950caae157865cec8eee719030ca109ab2681 translated
#[test]
fn touch_all_restricted() {
    let h = harness();
    process(&h, &["@touchall=n"]);
    assert_eq!(touch_matrix(&h), [false, false, false, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAttachOtherRestrictionTests.cs::TouchAttachOtherRestrictionTests.TouchAttachOther_default::test 1f8157f5fd5db8b5a4596d5be6a5d780704cac85370204c77eca08da286adb86 translated
#[test]
fn touch_attach_other_default() {
    let h = harness();
    process(&h, &["@touchattachother=n"]);
    assert_eq!(touch_matrix(&h), [true, false, true, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAttachOtherRestrictionTests.cs::TouchAttachOtherRestrictionTests.TouchAttachOther_Specific::test add9d393d4aaf03768239611a8a64b2ef73fc4c8561dbbc51950a10c006198e6 translated
#[test]
fn touch_attach_other_specific_user() {
    let h = harness();
    process(&h, &[&format!("@touchattachother:{USER_2}=n")]);
    assert!(touch(&h, TouchLocation::AttachedSelf, OBJECT_1, None, None));
    assert!(touch(
        &h,
        TouchLocation::AttachedOther,
        OBJECT_1,
        Some(USER_1),
        None
    ));
    assert!(!touch(
        &h,
        TouchLocation::AttachedOther,
        OBJECT_1,
        Some(USER_2),
        None
    ));
    assert!(touch(
        &h,
        TouchLocation::RezzedInWorld,
        OBJECT_1,
        None,
        Some(5.0)
    ));
    assert!(touch(&h, TouchLocation::Hud, OBJECT_1, None, None));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAttachRestrictionTests.cs::TouchAttachRestrictionTests.TouchAttach_default::test 4c4c65f8ed4b29367c353a02e52c91e9402788ffd35c75e5a6a40a827710e20a translated
#[test]
fn touch_attach_default() {
    let h = harness();
    process(&h, &["@touchattach=n"]);
    assert_eq!(touch_matrix(&h), [false, false, true, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchAttachSelfRestrictionTests.cs::TouchAttachSelfRestrictionTests.TouchAttachSelf_default::test 09f7d7a45a9899746c1c63608f95db89ef2ae4a875437a2b594bb120da8139ff translated
#[test]
fn touch_attach_self_default() {
    let h = harness();
    process(&h, &["@touchattachself=n"]);
    assert_eq!(touch_matrix(&h), [false, true, true, true]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchHudRestrictionTests.cs::TouchHudRestrictionTests.TouchHud_default::test b15afadb1f45b51eb47651a9d890ed2a6b1de8066d986d9b1ccc33da734c0194 translated
#[test]
fn touch_hud_default() {
    let h = harness();
    process(&h, &["@touchhud=n"]);
    assert_eq!(touch_matrix(&h), [true, true, true, false]);
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchHudRestrictionTests.cs::TouchHudRestrictionTests.TouchHud_specific::test 4c4e1a9493e5cf34f220ebec54c5373e1b30c81044803deece263dced5f7155d translated
#[test]
fn touch_hud_specific_object() {
    let h = harness();
    process(&h, &[&format!("@touchhud:{OBJECT_2}=n")]);
    assert_eq!(touch_matrix(&h), [true, true, true, true]);
    assert!(!touch(&h, TouchLocation::Hud, OBJECT_2, None, None));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TouchWorldRestrictionTests.cs::TouchWorldRestrictionTests.TouchWorld_default::test 5995288721c44beaa329b266e15d619f2314c9ca74386145294183c7c509b42a translated
#[test]
fn touch_world_default() {
    let h = harness();
    process(&h, &["@touchworld=n"]);
    assert_eq!(touch_matrix(&h), [true, true, false, true]);
}

fn tp_local_matches(command: &str, expected_distance: f32) -> bool {
    let h = harness();
    process(&h, &[command]);
    let mut distance = -1.0;
    h.service.permissions().can_tp_local(&mut distance).unwrap()
        && (distance - expected_distance).abs() < 0.0001
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLocalRestrictionTests.cs::TpLocalRestrictionTests.CanTpLocal_Default::test 4b251429c6561c1d29f19f8fbc33dd0fb11344fe0d10962e789823ffedb3777b translated
#[test]
fn tp_local_default_distance() {
    assert!(tp_local_matches("@TpLocal=n", 0.0));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLocalRestrictionTests.cs::TpLocalRestrictionTests.CanTpLocal::test f9dd3daf337533beab1f4c2dce488f44dea5d17880e8f61b33e3c17c1a4c776f translated
#[test]
fn tp_local_explicit_distance() {
    assert!(tp_local_matches("@TpLocal:0.9=n", 0.9));
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLureRestrictionTests.cs::TpLureRestrictionTests.CanTpLure_Default::test 595e468736aab4136404842adf36a79f37b9900c3ee3993079621a5a6c96dec1 translated
#[test]
fn tp_lure_default() {
    let permissions = harness().service.permissions();
    assert!(permissions.can_tp_lure(None).unwrap());
    assert!(permissions.can_tp_lure(Some(Some(guid(SENDER_2)))).unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLureRestrictionTests.cs::TpLureRestrictionTests.CanTpLure::test a57f7803128d75a0f1ba35a1c7489f78ca99d7e5e540f06b84725da8a5cd10da translated
#[test]
fn tp_lure_restricted() {
    let h = harness();
    process(&h, &["@tplure=n"]);
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_lure(None).unwrap());
    assert!(!permissions.can_tp_lure(Some(Some(guid(SENDER_2)))).unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLureSecRestrictionTests.cs::TpLureSecRestrictionTests.CanTpLure_Secure_Default::test 577c2c8d22ba1aa8d4426832807ea4ed8cd243836b0d38b0ebc9f32a851c34f4 translated
#[test]
fn secure_tp_lure_default() {
    let h = harness();
    process(&h, &["@tplure_sec=n"]);
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_lure(None).unwrap());
    assert!(!permissions.can_tp_lure(Some(Some(guid(OBJECT_1)))).unwrap());
    assert!(!permissions.can_tp_lure(Some(Some(guid(OBJECT_2)))).unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpLureSecRestrictionTests.cs::TpLureSecRestrictionTests.CanTpLure_Secure::test 38f2894e39bdd7c3c8e1520fa6ed02573be3dfdef0bc5bd1073d9d2240917804 translated
#[test]
fn secure_tp_lure_exceptions_are_sender_scoped() {
    let h = harness();
    process(&h, &["@tplure_sec=n", &format!("@tplure:{OBJECT_1}=add")]);
    block_on(h.process_from(&format!("@tplure:{OBJECT_2}=add"), SENDER_2, "Sender 2")).unwrap();
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_lure(None).unwrap());
    assert!(permissions.can_tp_lure(Some(Some(guid(OBJECT_1)))).unwrap());
    assert!(!permissions.can_tp_lure(Some(Some(guid(OBJECT_2)))).unwrap());
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpRequestRestrictionTests.cs::TpRequestRestrictionTests.CanTpRequest::test 08ddf0e6dd1faf1f249bc7f7a9545d9e33efb5a438eacc3606b1495a83cf57db translated
#[test]
fn tp_request_restricted() {
    let h = harness();
    process(&h, &["@tprequest=n"]);
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_request(None).unwrap());
    assert!(
        !permissions
            .can_tp_request(Some(Some(guid(SENDER_2))))
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpRequestRestrictionTests.cs::TpRequestRestrictionTests.CanTpRequest_Except::test 89ccb1dd172928c41079555cb6d7ff3fcbd34594dcff0399670cdebc739ac109 translated
#[test]
fn tp_request_exception() {
    let h = harness();
    process(&h, &["@tprequest=n", &format!("@tprequest:{OBJECT_1}=add")]);
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_request(None).unwrap());
    assert!(
        permissions
            .can_tp_request(Some(Some(guid(OBJECT_1))))
            .unwrap()
    );
    assert!(
        !permissions
            .can_tp_request(Some(Some(guid(OBJECT_2))))
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpRequestSecRestrictionTests.cs::TpRequestSecRestrictionTests.CanTpRequest_Secure_Default::test 87cb0ffa95d408d55045ade0966979360c28762e05729893b068801ed3b877ed translated
#[test]
fn secure_tp_request_default() {
    let h = harness();
    process(&h, &["@tprequest_sec=n"]);
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_request(None).unwrap());
    assert!(
        !permissions
            .can_tp_request(Some(Some(guid(OBJECT_1))))
            .unwrap()
    );
    assert!(
        !permissions
            .can_tp_request(Some(Some(guid(OBJECT_2))))
            .unwrap()
    );
}

// parity-case: LibreMetaverse.Tests/RLV/Restrictions/TpRequestSecRestrictionTests.cs::TpRequestSecRestrictionTests.CanTpRequest_Secure::test 623310f5c9150f6d6527b0db0446e2c33bf873a849a8dc68b78a4173d130da93 translated
#[test]
fn secure_tp_request_exceptions_are_sender_scoped() {
    let h = harness();
    process(
        &h,
        &["@tprequest_sec=n", &format!("@tprequest:{OBJECT_1}=add")],
    );
    block_on(h.process_from(&format!("@tprequest:{OBJECT_2}=add"), SENDER_2, "Sender 2")).unwrap();
    let permissions = h.service.permissions();
    assert!(!permissions.can_tp_request(None).unwrap());
    assert!(
        permissions
            .can_tp_request(Some(Some(guid(OBJECT_1))))
            .unwrap()
    );
    assert!(
        !permissions
            .can_tp_request(Some(Some(guid(OBJECT_2))))
            .unwrap()
    );
}
