//! Parcel protocol manager, synchronized simulator parcel state, and land capabilities.

#![allow(clippy::missing_errors_doc)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::assigning_clones)] // Fresh compatibility records are populated field by field.
#![allow(clippy::cast_possible_truncation)] // Protocol coordinates and durations use fixed narrow fields.
#![allow(clippy::cast_precision_loss)] // The LLUDP land protocol represents coordinates and time as f32.
#![allow(clippy::cast_sign_loss)] // Values are range-checked before conversion to protocol indices.
#![allow(clippy::collapsible_if)] // Nested packet/message validation keeps failure boundaries explicit.
#![allow(clippy::inherent_to_string)] // The mapped C# surface requires Parcel.ToString as an inherent method.
#![allow(clippy::struct_excessive_bools)] // Parcel flags mirror independent public C# fields.
#![allow(clippy::unnecessary_wraps)] // Mapped constructors have fixed fallible signatures.

use crate::agent_manager::EventRegistry;
use crate::packet_catalog::{GeneratedPacket, PacketType};
use crate::{Error, GridClient, Simulator};
use libremetaverse_structured_data::{OSD, OSDMap, OSDParser};
use libremetaverse_types::compat::{CancellationToken, EventHandler, Subscription};
use libremetaverse_types::{UUID, Vector3};
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

const PARCEL_MAP_SIZE: usize = 64;
const PARCEL_GRID_STEP: f32 = 4.0;
const MAX_PARCEL_BYTES: usize = 16 * 1024 * 1024;
fn mutex<T>(value: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
    value
        .lock()
        .unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn wire_string(value: &[u8]) -> String {
    String::from_utf8_lossy(value.split(|byte| *byte == 0).next().unwrap_or_default()).into_owned()
}

#[derive(Clone, Debug, PartialEq)]
pub struct ParcelManagerParcelAccessEntry {
    pub agent_id: UUID,
    pub flags: crate::AccessList,
    pub time: SystemTime,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParcelManagerParcelPrimOwners {
    pub count: i32,
    pub is_group_owned: bool,
    pub newest_prim: SystemTime,
    pub online_status: bool,
    pub owner_id: UUID,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParcelMedia {
    pub media_auto_scale: bool,
    pub media_desc: String,
    pub media_height: i32,
    pub media_id: UUID,
    pub media_loop: bool,
    pub media_type: String,
    pub media_url: String,
    pub media_width: i32,
}
impl Default for ParcelMedia {
    fn default() -> Self {
        Self {
            media_auto_scale: false,
            media_desc: String::new(),
            media_height: 0,
            media_id: UUID::zero(),
            media_loop: false,
            media_type: String::new(),
            media_url: String::new(),
            media_width: 0,
        }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Parcel {
    pub aabb_max: Vector3,
    pub aabb_min: Vector3,
    pub access_black_list: Vec<ParcelManagerParcelAccessEntry>,
    pub access_white_list: Vec<ParcelManagerParcelAccessEntry>,
    pub any_av_sounds: bool,
    pub area: i32,
    pub auction_id: u32,
    pub auth_buyer_id: UUID,
    pub bitmap: Vec<u8>,
    pub category: crate::ParcelCategory,
    pub claim_date: SystemTime,
    pub claim_price: i32,
    pub desc: String,
    pub dwell: f32,
    pub flags: crate::ParcelFlags,
    pub group_av_sounds: bool,
    pub group_id: UUID,
    pub group_prims: i32,
    pub is_group_owned: bool,
    pub landing: crate::LandingType,
    pub local_id: i32,
    pub max_prims: i32,
    pub media: ParcelMedia,
    pub music_url: String,
    pub name: String,
    pub obscure_media: bool,
    pub obscure_music: bool,
    pub other_clean_time: i32,
    pub other_count: i32,
    pub other_prims: i32,
    pub owner_id: UUID,
    pub owner_prims: i32,
    pub parcel_prim_bonus: f32,
    pub pass_hours: f32,
    pub pass_price: i32,
    pub public_count: i32,
    pub region_deny_age_unverified: bool,
    pub region_deny_anonymous: bool,
    pub region_push_override: bool,
    pub rent_price: i32,
    pub sale_price: i32,
    pub see_a_vs: bool,
    pub self_count: i32,
    pub sim_wide_max_prims: i32,
    pub sim_wide_total_prims: i32,
    pub snapshot_id: UUID,
    pub status: crate::ParcelStatus,
    pub total_prims: i32,
    pub user_location: Vector3,
    pub user_look_at: Vector3,
}
impl Parcel {
    pub fn new(local_id: i32) -> Result<Self, Error> {
        Ok(Self {
            aabb_max: Vector3::default(),
            aabb_min: Vector3::default(),
            access_black_list: Vec::new(),
            access_white_list: Vec::new(),
            any_av_sounds: true,
            area: 0,
            auction_id: 0,
            auth_buyer_id: UUID::zero(),
            bitmap: Vec::new(),
            category: crate::ParcelCategory::None,
            claim_date: UNIX_EPOCH,
            claim_price: 0,
            desc: String::new(),
            dwell: 0.0,
            flags: crate::ParcelFlags::NONE,
            group_av_sounds: true,
            group_id: UUID::zero(),
            group_prims: 0,
            is_group_owned: false,
            landing: crate::LandingType::None,
            local_id,
            max_prims: 0,
            media: ParcelMedia::default(),
            music_url: String::new(),
            name: String::new(),
            obscure_media: false,
            obscure_music: false,
            other_clean_time: 0,
            other_count: 0,
            other_prims: 0,
            owner_id: UUID::zero(),
            owner_prims: 0,
            parcel_prim_bonus: 0.0,
            pass_hours: 0.0,
            pass_price: 0,
            public_count: 0,
            region_deny_age_unverified: false,
            region_deny_anonymous: false,
            region_push_override: false,
            rent_price: 0,
            sale_price: 0,
            see_a_vs: true,
            self_count: 0,
            sim_wide_max_prims: 0,
            sim_wide_total_prims: 0,
            snapshot_id: UUID::zero(),
            status: crate::ParcelStatus::None,
            total_prims: 0,
            user_location: Vector3::default(),
            user_look_at: Vector3::default(),
        })
    }
    pub fn to_string(&self) -> String {
        format!("{} ({})", self.name, self.local_id)
    }
    pub fn update(
        &self,
        client: GridClient,
        simulator: Simulator,
        want_reply: bool,
    ) -> Result<(), Error> {
        ParcelManager::new(client)?.send_parcel_update(self, simulator, want_reply)
    }
    pub fn update_other_clean_time(&self, simulator: Simulator) -> Result<(), Error> {
        simulator.native_send_parcel_clean_time(self.local_id, self.other_clean_time)
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct ParcelInfo {
    pub actual_area: i32,
    pub auction_id: i32,
    pub billable_area: i32,
    pub description: String,
    pub dwell: f32,
    pub global_x: f32,
    pub global_y: f32,
    pub global_z: f32,
    pub id: UUID,
    pub mature: bool,
    pub name: String,
    pub owner_id: UUID,
    pub sale_price: i32,
    pub sim_name: String,
    pub snapshot_id: UUID,
}

#[derive(Clone, Debug, PartialEq)]
pub struct ObjectResourcesDetail {
    pub group_owned: bool,
    pub id: UUID,
    pub location: libremetaverse_types::Vector3d,
    pub name: String,
    pub owner_id: UUID,
    pub resources: HashMap<String, i32>,
}
impl ObjectResourcesDetail {
    pub fn new() -> Result<Self, Error> {
        Ok(Self {
            group_owned: false,
            id: UUID::zero(),
            location: libremetaverse_types::Vector3d::default(),
            name: String::new(),
            owner_id: UUID::zero(),
            resources: HashMap::new(),
        })
    }
    pub fn deserialize(&mut self, obj: OSDMap) -> Result<(), Error> {
        self.id = obj.get("id").map_or(Ok(UUID::zero()), |v| v.as_uuid())?;
        self.name = obj
            .get("name")
            .map_or(Ok(String::new()), |v| v.as_string())?;
        self.location = obj
            .get("location")
            .map_or(Ok(libremetaverse_types::Vector3d::default()), |v| {
                v.as_vector3d()
            })?;
        self.group_owned = obj
            .get("is_group_owned")
            .is_some_and(|v| v.as_boolean().unwrap_or(false));
        self.owner_id = obj
            .get("owner_id")
            .map_or(Ok(UUID::zero()), |v| v.as_uuid())?;
        self.resources = match obj.get("resources") {
            Some(OSD::Map(values)) => values
                .into_iter()
                .take(4096)
                .filter_map(|(key, value)| value.as_integer().ok().map(|amount| (key, amount)))
                .collect(),
            _ => HashMap::new(),
        };
        Ok(())
    }
    pub fn from_osd(osd: OSD) -> Result<Self, Error> {
        let OSD::Map(values) = osd else {
            return Err(Error::Argument);
        };
        let mut result = Self::new()?;
        result.deserialize(OSDMap::new_with_dictionary(values)?)?;
        Ok(result)
    }
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParcelResourcesDetail {
    pub group_owned: bool,
    pub id: UUID,
    pub local_id: i32,
    pub name: String,
    pub objects: Vec<ObjectResourcesDetail>,
    pub owner_id: UUID,
}
impl ParcelResourcesDetail {
    pub fn new() -> Result<Self, Error> {
        Ok(Self {
            group_owned: false,
            id: UUID::zero(),
            local_id: 0,
            name: String::new(),
            objects: Vec::new(),
            owner_id: UUID::zero(),
        })
    }
    pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> {
        self.id = map.get("id").map_or(Ok(UUID::zero()), |v| v.as_uuid())?;
        self.local_id = map.get("local_id").map_or(Ok(0), |v| v.as_integer())?;
        self.name = map
            .get("name")
            .map_or(Ok(String::new()), |v| v.as_string())?;
        self.group_owned = map
            .get("is_group_owned")
            .is_some_and(|v| v.as_boolean().unwrap_or(false));
        self.owner_id = map
            .get("owner_id")
            .map_or(Ok(UUID::zero()), |v| v.as_uuid())?;
        self.objects = match map.get("objects") {
            Some(OSD::Array(values)) if values.len() <= 65_535 => values
                .into_iter()
                .map(ObjectResourcesDetail::from_osd)
                .collect::<Result<_, _>>()?,
            Some(OSD::Array(_)) => return Err(Error::Argument),
            _ => Vec::new(),
        };
        Ok(())
    }
    pub fn from_osd(osd: OSD) -> Result<Self, Error> {
        let OSD::Map(values) = osd else {
            return Err(Error::Argument);
        };
        let mut result = Self::new()?;
        result.deserialize(OSDMap::new_with_dictionary(values)?)?;
        Ok(result)
    }
}
#[derive(Clone, Debug, PartialEq)]
pub struct LandResourcesInfo {
    pub parcels: Vec<ParcelResourcesDetail>,
    summary_available: HashMap<String, i32>,
    summary_used: HashMap<String, i32>,
}
impl LandResourcesInfo {
    pub fn new() -> Result<Self, Error> {
        Ok(Self {
            parcels: Vec::new(),
            summary_available: HashMap::new(),
            summary_used: HashMap::new(),
        })
    }
    pub fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> {
        if let Some(OSD::Map(summary)) = map.get("summary") {
            self.summary_available = resource_summary(summary.get("available"))?;
            self.summary_used = resource_summary(summary.get("used"))?;
        } else if let Some(OSD::Array(values)) = map.get("parcels") {
            if values.len() > 65_535 {
                return Err(Error::Argument);
            }
            self.parcels = values
                .into_iter()
                .map(ParcelResourcesDetail::from_osd)
                .collect::<Result<_, _>>()?;
        }
        Ok(())
    }
}
fn resource_summary(value: Option<&OSD>) -> Result<HashMap<String, i32>, Error> {
    let Some(OSD::Array(values)) = value else {
        return Ok(HashMap::new());
    };
    if values.len() > 4096 {
        return Err(Error::Argument);
    }
    Ok(values
        .iter()
        .filter_map(|entry| {
            let OSD::Map(map) = entry else {
                return None;
            };
            let name = map.get("type")?.as_string().ok()?;
            let amount = map.get("amount")?.as_integer().ok()?;
            Some((name, amount))
        })
        .collect())
}
impl crate::interfaces::IMessage for LandResourcesInfo {
    fn deserialize(&mut self, map: OSDMap) -> Result<(), Error> {
        self.deserialize(map)
    }
    fn serialize(&self) -> Result<OSDMap, Error> {
        OSDMap::new_with_constructor()
    }
}

macro_rules! simple_event {
    ($name:ident { $($field:ident : $type:ty),* $(,)? }) => {
        #[derive(Clone)] pub struct $name { $(pub(crate) $field: $type),* }
    };
}
simple_event!(ForceSelectObjectsReplyEventArgs { simulator: Simulator, object_i_ds: Vec<u32>, reset_list: bool });
impl ForceSelectObjectsReplyEventArgs {
    pub fn new(
        simulator: Simulator,
        object_i_ds: Vec<u32>,
        reset_list: bool,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            object_i_ds,
            reset_list,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn object_i_ds(&self) -> Vec<u32> {
        self.object_i_ds.clone()
    }
    pub fn reset_list(&self) -> bool {
        self.reset_list
    }
}
simple_event!(ParcelAccessListReplyEventArgs { simulator: Simulator, sequence_id: i32, local_id: i32, flags: u32, access_list: Vec<ParcelManagerParcelAccessEntry> });
impl ParcelAccessListReplyEventArgs {
    pub fn new(
        simulator: Simulator,
        sequence_id: i32,
        local_id: i32,
        flags: u32,
        access_entries: Vec<ParcelManagerParcelAccessEntry>,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            sequence_id,
            local_id,
            flags,
            access_list: access_entries,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn sequence_id(&self) -> i32 {
        self.sequence_id
    }
    pub fn local_id(&self) -> i32 {
        self.local_id
    }
    pub fn flags(&self) -> u32 {
        self.flags
    }
    pub fn access_list(&self) -> Vec<ParcelManagerParcelAccessEntry> {
        self.access_list.clone()
    }
}
simple_event!(ParcelDwellReplyEventArgs {
    parcel_id: UUID,
    local_id: i32,
    dwell: f32
});
impl ParcelDwellReplyEventArgs {
    pub fn new(parcel_id: UUID, local_id: i32, dwell: f32) -> Result<Self, Error> {
        Ok(Self {
            parcel_id,
            local_id,
            dwell,
        })
    }
    pub fn parcel_id(&self) -> UUID {
        self.parcel_id
    }
    pub fn local_id(&self) -> i32 {
        self.local_id
    }
    pub fn dwell(&self) -> f32 {
        self.dwell
    }
}
simple_event!(ParcelInfoReplyEventArgs { parcel: ParcelInfo });
impl ParcelInfoReplyEventArgs {
    pub fn new(parcel: ParcelInfo) -> Result<Self, Error> {
        Ok(Self { parcel })
    }
    pub fn parcel(&self) -> ParcelInfo {
        self.parcel.clone()
    }
}
simple_event!(ParcelMediaCommandEventArgs {
    simulator: Simulator,
    sequence: u32,
    parcel_flags: crate::ParcelFlags,
    media_command: crate::ParcelMediaCommand,
    time: f32
});
impl ParcelMediaCommandEventArgs {
    pub fn new(
        simulator: Simulator,
        sequence: u32,
        flags: crate::ParcelFlags,
        command: crate::ParcelMediaCommand,
        time: f32,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            sequence,
            parcel_flags: flags,
            media_command: command,
            time,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn sequence(&self) -> u32 {
        self.sequence
    }
    pub fn parcel_flags(&self) -> crate::ParcelFlags {
        self.parcel_flags
    }
    pub fn media_command(&self) -> crate::ParcelMediaCommand {
        self.media_command
    }
    pub fn time(&self) -> f32 {
        self.time
    }
}
simple_event!(ParcelMediaUpdateReplyEventArgs {
    simulator: Simulator,
    media: ParcelMedia
});
impl ParcelMediaUpdateReplyEventArgs {
    pub fn new(simulator: Simulator, media: ParcelMedia) -> Result<Self, Error> {
        Ok(Self { simulator, media })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn media(&self) -> ParcelMedia {
        self.media.clone()
    }
}
simple_event!(ParcelObjectOwnersReplyEventArgs { simulator: Simulator, prim_owners: Vec<ParcelManagerParcelPrimOwners> });
impl ParcelObjectOwnersReplyEventArgs {
    pub fn new(
        simulator: Simulator,
        prim_owners: Vec<ParcelManagerParcelPrimOwners>,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            prim_owners,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn prim_owners(&self) -> Vec<ParcelManagerParcelPrimOwners> {
        self.prim_owners.clone()
    }
}
simple_event!(ParcelOverlayEventArgs {
    simulator: Simulator
});
impl ParcelOverlayEventArgs {
    pub fn new(simulator: Simulator) -> Result<Self, Error> {
        Ok(Self { simulator })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
}
simple_event!(ParcelPropertiesEventArgs {
    simulator: Simulator,
    parcel: Parcel,
    result: crate::ParcelResult,
    selected_prims: i32,
    sequence_id: i32,
    snap_selection: bool
});
impl ParcelPropertiesEventArgs {
    pub fn new(
        simulator: Simulator,
        parcel: Parcel,
        result: crate::ParcelResult,
        selected_prims: i32,
        sequence_id: i32,
        snap_selection: bool,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            parcel,
            result,
            selected_prims,
            sequence_id,
            snap_selection,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn parcel(&self) -> Parcel {
        self.parcel.clone()
    }
    pub fn result(&self) -> crate::ParcelResult {
        self.result
    }
    pub fn selected_prims(&self) -> i32 {
        self.selected_prims
    }
    pub fn sequence_id(&self) -> i32 {
        self.sequence_id
    }
    pub fn snap_selection(&self) -> bool {
        self.snap_selection
    }
}
simple_event!(SimParcelsDownloadedEventArgs { simulator: Simulator, sim_parcels: HashMap<i32, Parcel>, parcel_map: Vec<i32> });
impl SimParcelsDownloadedEventArgs {
    pub fn new(
        simulator: Simulator,
        sim_parcels: RwLock<HashMap<i32, Parcel>>,
        parcel_map: Vec<i32>,
    ) -> Result<Self, Error> {
        Ok(Self {
            simulator,
            sim_parcels: sim_parcels
                .into_inner()
                .unwrap_or_else(std::sync::PoisonError::into_inner),
            parcel_map,
        })
    }
    pub fn simulator(&self) -> Simulator {
        self.simulator.clone()
    }
    pub fn parcel_map(&self) -> Vec<i32> {
        self.parcel_map.clone()
    }
    pub fn parcels(&self) -> RwLock<HashMap<i32, Parcel>> {
        RwLock::new(self.sim_parcels.clone())
    }
}

#[derive(Default)]
struct ParcelEvents {
    force: EventRegistry<ForceSelectObjectsReplyEventArgs>,
    access: EventRegistry<ParcelAccessListReplyEventArgs>,
    dwell: EventRegistry<ParcelDwellReplyEventArgs>,
    info: EventRegistry<ParcelInfoReplyEventArgs>,
    media_command: EventRegistry<ParcelMediaCommandEventArgs>,
    media_update: EventRegistry<ParcelMediaUpdateReplyEventArgs>,
    owners: EventRegistry<ParcelObjectOwnersReplyEventArgs>,
    overlay: EventRegistry<ParcelOverlayEventArgs>,
    properties: EventRegistry<ParcelPropertiesEventArgs>,
    all: EventRegistry<SimParcelsDownloadedEventArgs>,
}
pub(crate) struct ParcelManagerInner {
    client: Arc<GridClient>,
    events: ParcelEvents,
    raw_subscription: Mutex<Option<Subscription>>,
    caps_subscriptions: Mutex<Vec<Subscription>>,
    sequence: AtomicI32,
    disposed: AtomicBool,
}
#[derive(Clone)]
pub struct ParcelManager {
    pub(crate) inner: Arc<ParcelManagerInner>,
}

impl ParcelManager {
    pub fn new(client: GridClient) -> Result<Self, Error> {
        Self::native_new(Arc::new(client))
    }
    pub(crate) fn native_new(client: Arc<GridClient>) -> Result<Self, Error> {
        if let Some(inner) = client.cached_parcel_manager_inner() {
            return Ok(Self { inner });
        }
        let inner = Arc::new(ParcelManagerInner {
            client: Arc::clone(&client),
            events: ParcelEvents::default(),
            raw_subscription: Mutex::new(None),
            caps_subscriptions: Mutex::new(Vec::new()),
            sequence: AtomicI32::new(1),
            disposed: AtomicBool::new(false),
        });
        let weak = Arc::downgrade(&inner);
        *mutex(&inner.raw_subscription) = Some(client.network().subscribe_raw_packet(Arc::new(
            move |event| {
                if let Some(inner) = weak.upgrade() {
                    inner.handle_packet(event);
                }
            },
        )));
        let weak = Arc::downgrade(&inner);
        mutex(&inner.caps_subscriptions).push(client.network().subscribe_caps(
            "ParcelProperties".into(),
            crate::CapsEventQueueCallback::from_handler(move |_, message, simulator| {
                if let Some(inner) = weak.upgrade() {
                    if let Ok(map) = message.serialize() {
                        if let Ok(mut value) =
                            crate::messages::linden::ParcelPropertiesMessage::new()
                        {
                            if value.deserialize(map).is_ok() {
                                inner.handle_properties(value, simulator);
                            }
                        }
                    }
                }
            }),
        ));
        let weak = Arc::downgrade(&inner);
        mutex(&inner.caps_subscriptions).push(client.network().subscribe_caps(
            "ParcelObjectOwnersReply".into(),
            crate::CapsEventQueueCallback::from_handler(move |_, message, simulator| {
                if let Some(inner) = weak.upgrade() {
                    if let Ok(map) = message.serialize() {
                        if let Ok(mut value) =
                            crate::messages::linden::ParcelObjectOwnersReplyMessage::new()
                        {
                            if value.deserialize(map).is_ok() {
                                inner.handle_owners(value, simulator);
                            }
                        }
                    }
                }
            }),
        ));
        Ok(Self {
            inner: client.install_parcel_manager_inner(inner),
        })
    }
    fn agent_ids(&self) -> (UUID, UUID) {
        let mut client = (*self.inner.client).clone();
        let agent = client.self_();
        (agent.agent_id(), agent.session_id())
    }
    fn send<T: GeneratedPacket>(
        &self,
        sim: &Simulator,
        packet: &T,
        kind: PacketType,
    ) -> Result<(), Error> {
        if self.inner.disposed.load(Ordering::Acquire) {
            return Err(Error::InvalidOperation);
        }
        let bytes = packet.encode_packet()?;
        let result = sim.native_send_packet_data(
            bytes.clone(),
            i32::try_from(bytes.len()).map_err(|_| Error::Argument)?,
            kind,
            false,
        );
        match result {
            // The reference manager accepts packet submission to an inert test
            // simulator. Connected transports still surface every send error.
            Err(Error::InvalidOperation) if !sim.connected() => Ok(()),
            result => result,
        }
    }
    pub fn dispose(&self) -> Result<(), Error> {
        self.inner.disposed.store(true, Ordering::Release);
        mutex(&self.inner.raw_subscription).take();
        mutex(&self.inner.caps_subscriptions).clear();
        Ok(())
    }
    pub fn current_parcel(&self) -> Option<Parcel> {
        let sim = self.inner.client.network().current_sim()?;
        let mut client = (*self.inner.client).clone();
        let id = self
            .get_parcel_local_id(sim.clone(), client.self_().sim_position())
            .ok()?;
        sim.native_parcel(id)
    }
    pub fn get_parcel_local_id(
        &self,
        simulator: Simulator,
        position: Vector3,
    ) -> Result<i32, Error> {
        if !position.x.is_finite() || !position.y.is_finite() {
            return Err(Error::Argument);
        }
        let x = (position.x / PARCEL_GRID_STEP).floor() as i32;
        let y = (position.y / PARCEL_GRID_STEP).floor() as i32;
        if !(0..64).contains(&x) || !(0..64).contains(&y) {
            return Ok(0);
        }
        Ok(simulator
            .native_parcel_map_at(x as usize, y as usize)
            .unwrap_or(0))
    }
    pub fn request_parcel_info(&self, parcel_id: UUID) -> Result<(), Error> {
        let sim = self
            .inner
            .client
            .network()
            .current_sim()
            .ok_or(Error::InvalidOperation)?;
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelInfoRequestPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.parcel_id = parcel_id;
        self.send(&sim, &p, PacketType::ParcelInfoRequest)
    }
    pub fn request_parcel_properties_with_simulator_int32_int32(
        &self,
        sim: Simulator,
        local_id: i32,
        sequence_id: i32,
    ) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelPropertiesRequestByIDPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = local_id;
        p.parcel_data.sequence_id = sequence_id;
        self.send(&sim, &p, PacketType::ParcelPropertiesRequestByID)
    }
    pub fn request_parcel_properties_with_simulator_single_single_single_single_int32_boolean(
        &self,
        sim: Simulator,
        north: f32,
        east: f32,
        south: f32,
        west: f32,
        sequence_id: i32,
        snap_selection: bool,
    ) -> Result<(), Error> {
        if ![north, east, south, west].iter().all(|v| v.is_finite()) || north < south || east < west
        {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelPropertiesRequestPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.north = north;
        p.parcel_data.east = east;
        p.parcel_data.south = south;
        p.parcel_data.west = west;
        p.parcel_data.sequence_id = sequence_id;
        p.parcel_data.snap_selection = snap_selection;
        self.send(&sim, &p, PacketType::ParcelPropertiesRequest)
    }
    pub fn request_parcel_access_list(
        &self,
        sim: Simulator,
        local_id: i32,
        flags: crate::AccessList,
        sequence_id: i32,
    ) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelAccessListRequestPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.local_id = local_id;
        p.data.flags = flags.0;
        p.data.sequence_id = sequence_id;
        self.send(&sim, &p, PacketType::ParcelAccessListRequest)
    }
    pub fn request_dwell(&self, sim: Simulator, local_id: i32) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelDwellRequestPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.local_id = local_id;
        p.data.parcel_id = UUID::zero();
        self.send(&sim, &p, PacketType::ParcelDwellRequest)
    }
    pub fn request_object_owners(&self, sim: Simulator, local_id: i32) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelObjectOwnersRequestPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = local_id;
        self.send(&sim, &p, PacketType::ParcelObjectOwnersRequest)
    }
    fn send_parcel_update(
        &self,
        parcel: &Parcel,
        sim: Simulator,
        want_reply: bool,
    ) -> Result<(), Error> {
        if parcel.desc.len() > 1024 * 1024
            || parcel.name.len() > 1024 * 1024
            || parcel.media.media_url.len() > 1024 * 1024
            || parcel.music_url.len() > 1024 * 1024
        {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelPropertiesUpdatePacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = parcel.local_id;
        p.parcel_data.auth_buyer_id = parcel.auth_buyer_id;
        p.parcel_data.category = parcel.category as u8;
        p.parcel_data.desc = parcel.desc.as_bytes().to_vec();
        p.parcel_data.group_id = parcel.group_id;
        p.parcel_data.landing_type = parcel.landing as u8;
        p.parcel_data.media_auto_scale = u8::from(parcel.media.media_auto_scale);
        p.parcel_data.media_id = parcel.media.media_id;
        p.parcel_data.media_url = parcel.media.media_url.as_bytes().to_vec();
        p.parcel_data.music_url = parcel.music_url.as_bytes().to_vec();
        p.parcel_data.name = parcel.name.as_bytes().to_vec();
        p.parcel_data.flags = u32::from(want_reply);
        p.parcel_data.parcel_flags = parcel.flags.0;
        p.parcel_data.pass_hours = parcel.pass_hours;
        p.parcel_data.pass_price = parcel.pass_price;
        p.parcel_data.sale_price = parcel.sale_price;
        p.parcel_data.snapshot_id = parcel.snapshot_id;
        p.parcel_data.user_location = parcel.user_location;
        p.parcel_data.user_look_at = parcel.user_look_at;
        self.send(&sim, &p, PacketType::ParcelPropertiesUpdate)?;
        parcel.update_other_clean_time(sim)
    }
    pub fn parcel_subdivide(
        &self,
        sim: Simulator,
        west: f32,
        south: f32,
        east: f32,
        north: f32,
    ) -> Result<(), Error> {
        self.send_bounds::<crate::packets::ParcelDividePacket>(
            sim,
            west,
            south,
            east,
            north,
            PacketType::ParcelDivide,
        )
    }
    pub fn parcel_join(
        &self,
        sim: Simulator,
        west: f32,
        south: f32,
        east: f32,
        north: f32,
    ) -> Result<(), Error> {
        self.send_bounds::<crate::packets::ParcelJoinPacket>(
            sim,
            west,
            south,
            east,
            north,
            PacketType::ParcelJoin,
        )
    }
    fn send_bounds<T: GeneratedPacket + ParcelBoundsPacket>(
        &self,
        sim: Simulator,
        west: f32,
        south: f32,
        east: f32,
        north: f32,
        kind: PacketType,
    ) -> Result<(), Error> {
        if ![west, south, east, north].iter().all(|v| v.is_finite())
            || west >= east
            || south >= north
        {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = T::new_generated();
        p.set_agent(a, s);
        p.set_bounds(west, south, east, north);
        self.send(&sim, &p, kind)
    }
    pub async fn request_all_sim_parcels_with_simulator_cancellation_token(
        &self,
        sim: Simulator,
        token: Option<CancellationToken>,
    ) -> Result<(), Error> {
        self.request_all_sim_parcels_with_simulator_boolean_time_span_cancellation_token(
            sim,
            false,
            Duration::from_millis(750),
            token,
        )
        .await
    }
    pub async fn request_all_sim_parcels_with_simulator_boolean_time_span_cancellation_token(
        &self,
        sim: Simulator,
        refresh: bool,
        delay: Duration,
        token: Option<CancellationToken>,
    ) -> Result<(), Error> {
        let token = token.unwrap_or_default();
        if refresh {
            sim.native_clear_parcels();
        }
        for y in 0..PARCEL_MAP_SIZE {
            for x in 0..PARCEL_MAP_SIZE {
                token.throw_if_cancellation_requested()?;
                if sim.native_parcel_map_at(x, y).unwrap_or(0) > 0 {
                    continue;
                }
                let sequence = self.inner.sequence.fetch_add(1, Ordering::Relaxed);
                self.request_parcel_properties_with_simulator_single_single_single_single_int32_boolean(sim.clone(),(y+1)as f32*4.0,(x+1)as f32*4.0,y as f32*4.0,x as f32*4.0,sequence,false)?;
                if !delay.is_zero() {
                    tokio::select! {
                        () = tokio::time::sleep(delay.min(Duration::from_secs(10))) => {}
                        () = token.cancelled() => return Err(Error::Cancelled),
                    }
                }
            }
        }
        Ok(())
    }
    pub fn buy(
        &self,
        sim: Simulator,
        local_id: i32,
        for_group: bool,
        group_id: UUID,
        remove_contribution: bool,
        parcel_area: i32,
        parcel_price: i32,
    ) -> Result<(), Error> {
        if parcel_area < 0 || parcel_price < 0 {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelBuyPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.final_ = true;
        p.data.group_id = group_id;
        p.data.local_id = local_id;
        p.data.is_group_owned = for_group;
        p.data.remove_contribution = remove_contribution;
        p.parcel_data.area = parcel_area;
        p.parcel_data.price = parcel_price;
        self.send(&sim, &p, PacketType::ParcelBuy)
    }
    pub fn reclaim(&self, sim: Simulator, local_id: i32) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelReclaimPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.local_id = local_id;
        self.send(&sim, &p, PacketType::ParcelReclaim)
    }
    pub fn deed_to_group(
        &self,
        sim: Simulator,
        local_id: i32,
        group_id: UUID,
    ) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelDeedToGroupPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.local_id = local_id;
        p.data.group_id = group_id;
        self.send(&sim, &p, PacketType::ParcelDeedToGroup)
    }
    pub fn release_parcel(&self, sim: Simulator, local_id: i32) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelReleasePacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.local_id = local_id;
        self.send(&sim, &p, PacketType::ParcelRelease)
    }
    pub fn start_auction(
        &self,
        sim: Simulator,
        local_id: i32,
        snapshot_id: UUID,
    ) -> Result<(), Error> {
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ViewerStartAuctionPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = local_id;
        p.parcel_data.snapshot_id = snapshot_id;
        self.send(&sim, &p, PacketType::ViewerStartAuction)
    }
    pub fn eject_user(&self, target_id: UUID, ban: bool) -> Result<(), Error> {
        let sim = self
            .inner
            .client
            .network()
            .current_sim()
            .ok_or(Error::InvalidOperation)?;
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::EjectUserPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.target_id = target_id;
        p.data.flags = u32::from(ban);
        self.send(&sim, &p, PacketType::EjectUser)
    }
    pub fn freeze_user(&self, target_id: UUID, freeze: bool) -> Result<(), Error> {
        let sim = self
            .inner
            .client
            .network()
            .current_sim()
            .ok_or(Error::InvalidOperation)?;
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::FreezeUserPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.data.target_id = target_id;
        p.data.flags = u32::from(!freeze);
        self.send(&sim, &p, PacketType::FreezeUser)
    }
    pub fn request_select_objects(
        &self,
        local_id: i32,
        select_type: crate::ObjectReturnType,
        owner_id: UUID,
    ) -> Result<(), Error> {
        let sim = self
            .inner
            .client
            .network()
            .current_sim()
            .ok_or(Error::InvalidOperation)?;
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelSelectObjectsPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = local_id;
        p.parcel_data.return_type = select_type as u32;
        let mut owner =
            crate::packets::ParcelSelectObjectsPacketReturnIDsBlock::new_with_constructor()?;
        owner.return_id = owner_id;
        p.return_i_ds = vec![owner];
        self.send(&sim, &p, PacketType::ParcelSelectObjects)
    }
    pub fn return_objects(
        &self,
        sim: Simulator,
        local_id: i32,
        type_: crate::ObjectReturnType,
        owner_i_ds: Vec<UUID>,
    ) -> Result<(), Error> {
        if owner_i_ds.len() > 65_535 {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ParcelReturnObjectsPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.parcel_data.local_id = local_id;
        p.parcel_data.return_type = type_ as u32;
        let mut task =
            crate::packets::ParcelReturnObjectsPacketTaskIDsBlock::new_with_constructor()?;
        task.task_id = UUID::zero();
        p.task_i_ds = vec![task];
        p.owner_i_ds = owner_i_ds
            .into_iter()
            .map(|owner_id| {
                let mut b =
                    crate::packets::ParcelReturnObjectsPacketOwnerIDsBlock::new_with_constructor()?;
                b.owner_id = owner_id;
                Ok(b)
            })
            .collect::<Result<Vec<_>, Error>>()?;
        self.send(&sim, &p, PacketType::ParcelReturnObjects)
    }

    pub fn terraform_with_simulator_int32_terraform_action_terraform_brush_size(
        &self,
        sim: Simulator,
        local_id: i32,
        action: crate::TerraformAction,
        brush_size: crate::TerraformBrushSize,
    ) -> Result<bool, Error> {
        let Some(parcel) = sim.native_parcel(local_id) else {
            return Ok(false);
        };
        self.terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32(sim,local_id,parcel.aabb_min.x,parcel.aabb_min.y,parcel.aabb_max.x,parcel.aabb_max.y,action,brush_size,None)
    }
    pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32(
        &self,
        sim: Simulator,
        local_id: i32,
        west: f32,
        south: f32,
        east: f32,
        north: f32,
        action: crate::TerraformAction,
        brush_size: crate::TerraformBrushSize,
        seconds: Option<i32>,
    ) -> Result<bool, Error> {
        let height = sim.native_parcel(local_id).map_or(0.0, |p| p.aabb_max.z);
        self.terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32_single(sim,local_id,west,south,east,north,action,brush_size,seconds.unwrap_or(1),height)?;
        Ok(true)
    }
    pub fn terraform_with_simulator_int32_single_single_single_single_terraform_action_terraform_brush_size_int32_single(
        &self,
        sim: Simulator,
        _local_id: i32,
        west: f32,
        south: f32,
        east: f32,
        north: f32,
        action: crate::TerraformAction,
        brush_size: crate::TerraformBrushSize,
        seconds: i32,
        height: f32,
    ) -> Result<(), Error> {
        if ![west, south, east, north, height]
            .iter()
            .all(|v| v.is_finite())
            || west >= east
            || south >= north
            || seconds < 0
        {
            return Err(Error::Argument);
        }
        let (a, s) = self.agent_ids();
        let mut p = crate::packets::ModifyLandPacket::new_with_constructor()?;
        p.agent_data.agent_id = a;
        p.agent_data.session_id = s;
        p.modify_block.action = action as u8;
        p.modify_block.brush_size = brush_size as u8;
        p.modify_block.seconds = seconds as f32;
        p.modify_block.height = height;
        let mut b = crate::packets::ModifyLandPacketParcelDataBlock::new_with_constructor()?;
        b.west = west;
        b.south = south;
        b.east = east;
        b.north = north;
        p.parcel_data = vec![b];
        self.send(&sim, &p, PacketType::ModifyLand)
    }

    pub async fn request_remote_parcel_id(
        &self,
        location: Vector3,
        region_handle: u64,
        region_id: UUID,
        cancellation_token: Option<CancellationToken>,
    ) -> Result<UUID, Error> {
        let token = cancellation_token.unwrap_or_default();
        token.throw_if_cancellation_requested()?;
        let Some(sim) = self.inner.client.network().current_sim() else {
            return Ok(UUID::zero());
        };
        let Some(caps) = sim.native_caps() else {
            return Ok(UUID::zero());
        };
        let Some(uri) = caps.capability_uri("RemoteParcelRequest".into())? else {
            return Ok(UUID::zero());
        };
        let mut msg = crate::messages::linden::RemoteParcelRequestRequest::new()?;
        msg.location = location;
        msg.region_handle = region_handle;
        msg.region_id = region_id;
        let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(msg.serialize()?.snapshot()))?;
        let (response, bytes) = self
            .inner
            .client
            .native_http_caps_client()
            .post_with_uri_string_bytes_cancellation_token_i_progress(
                uri,
                "application/llsd+xml".into(),
                body,
                token,
                None,
            )
            .await?;
        if !(200..300).contains(&response.status_code) || bytes.len() > MAX_PARCEL_BYTES {
            return Ok(UUID::zero());
        }
        let OSD::Map(values) = OSDParser::deserialize_with_bytes(bytes)? else {
            return Ok(UUID::zero());
        };
        Ok(values
            .get("parcel_id")
            .and_then(|v| v.as_uuid().ok())
            .unwrap_or_else(UUID::zero))
    }
    pub async fn get_parcel_resources(
        &self,
        parcel_id: UUID,
        get_details: bool,
        cancellation_token: Option<CancellationToken>,
    ) -> Result<(bool, crate::messages::linden::LandResourcesInfo), Error> {
        let token = cancellation_token.unwrap_or_default();
        token.throw_if_cancellation_requested()?;
        let mut info = crate::messages::linden::LandResourcesInfo::new()?;
        let Some(sim) = self.inner.client.network().current_sim() else {
            return Ok((false, info));
        };
        let Some(caps) = sim.native_caps() else {
            return Ok((false, info));
        };
        let Some(uri) = caps.capability_uri("LandResources".into())? else {
            return Ok((false, info));
        };
        let body = OSDParser::serialize_llsd_xml_bytes(OSD::Map(HashMap::from([(
            "parcel_id".into(),
            OSD::UUID(parcel_id),
        )])))?;
        let (response, bytes) = self
            .inner
            .client
            .native_http_caps_client()
            .post_with_uri_string_bytes_cancellation_token_i_progress(
                uri,
                "application/llsd+xml".into(),
                body,
                token.clone(),
                None,
            )
            .await?;
        if !(200..300).contains(&response.status_code) || bytes.len() > MAX_PARCEL_BYTES {
            return Ok((false, info));
        }
        let OSD::Map(links) = OSDParser::deserialize_with_bytes(bytes)? else {
            return Ok((false, info));
        };
        for key in if get_details {
            ["ScriptResourceSummary", "ScriptResourceDetails"].as_slice()
        } else {
            ["ScriptResourceSummary"].as_slice()
        } {
            if let Some(uri) = links.get(*key).and_then(|v| v.as_uri().ok().flatten()) {
                let (resp, data) = self
                    .inner
                    .client
                    .native_http_caps_client()
                    .get(uri, token.clone(), None)
                    .await?;
                if (200..300).contains(&resp.status_code) && data.len() <= MAX_PARCEL_BYTES {
                    if let OSD::Map(values) = OSDParser::deserialize_with_bytes(data)? {
                        info.deserialize(OSDMap::new_with_dictionary(values)?)?;
                    }
                }
            }
        }
        Ok((true, info))
    }

    pub fn subscribe_force_select_objects_reply(
        &self,
        h: EventHandler<ForceSelectObjectsReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.force.subscribe(h)
    }
    pub fn subscribe_parcel_access_list_reply(
        &self,
        h: EventHandler<ParcelAccessListReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.access.subscribe(h)
    }
    pub fn subscribe_parcel_dwell_reply(
        &self,
        h: EventHandler<ParcelDwellReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.dwell.subscribe(h)
    }
    pub fn subscribe_parcel_info_reply(
        &self,
        h: EventHandler<ParcelInfoReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.info.subscribe(h)
    }
    pub fn subscribe_parcel_media_command(
        &self,
        h: EventHandler<ParcelMediaCommandEventArgs>,
    ) -> Subscription {
        self.inner.events.media_command.subscribe(h)
    }
    pub fn subscribe_parcel_media_update_reply(
        &self,
        h: EventHandler<ParcelMediaUpdateReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.media_update.subscribe(h)
    }
    pub fn subscribe_parcel_object_owners_reply(
        &self,
        h: EventHandler<ParcelObjectOwnersReplyEventArgs>,
    ) -> Subscription {
        self.inner.events.owners.subscribe(h)
    }
    pub fn subscribe_parcel_overlays_downloaded(
        &self,
        h: EventHandler<ParcelOverlayEventArgs>,
    ) -> Subscription {
        self.inner.events.overlay.subscribe(h)
    }
    pub fn subscribe_parcel_properties(
        &self,
        h: EventHandler<ParcelPropertiesEventArgs>,
    ) -> Subscription {
        self.inner.events.properties.subscribe(h)
    }
    pub fn subscribe_sim_parcels_downloaded(
        &self,
        h: EventHandler<SimParcelsDownloadedEventArgs>,
    ) -> Subscription {
        self.inner.events.all.subscribe(h)
    }
}

trait ParcelBoundsPacket: GeneratedPacket {
    fn set_agent(&mut self, a: UUID, s: UUID);
    fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32);
}
impl ParcelBoundsPacket for crate::packets::ParcelDividePacket {
    fn set_agent(&mut self, a: UUID, s: UUID) {
        self.agent_data.agent_id = a;
        self.agent_data.session_id = s;
    }
    fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32) {
        self.parcel_data.west = w;
        self.parcel_data.south = s;
        self.parcel_data.east = e;
        self.parcel_data.north = n;
    }
}
impl ParcelBoundsPacket for crate::packets::ParcelJoinPacket {
    fn set_agent(&mut self, a: UUID, s: UUID) {
        self.agent_data.agent_id = a;
        self.agent_data.session_id = s;
    }
    fn set_bounds(&mut self, w: f32, s: f32, e: f32, n: f32) {
        self.parcel_data.west = w;
        self.parcel_data.south = s;
        self.parcel_data.east = e;
        self.parcel_data.north = n;
    }
}

impl ParcelManagerInner {
    fn handle_packet(&self, event: crate::network_manager::RawPacketReceivedEventArgs) {
        if self.disposed.load(Ordering::Acquire) || event.data.len() > MAX_PARCEL_BYTES {
            return;
        }
        let mut pos = 0;
        match event.packet_type {
            PacketType::ParcelOverlay => {
                if let Ok(p) =
                    crate::packets::ParcelOverlayPacket::new_from_bytes(&event.data, &mut pos)
                {
                    if event
                        .simulator
                        .native_apply_parcel_overlay(p.parcel_data.sequence_id, p.parcel_data.data)
                        .unwrap_or(false)
                    {
                        self.events.overlay.emit(ParcelOverlayEventArgs {
                            simulator: event.simulator,
                        });
                    }
                }
            }
            PacketType::ParcelDwellReply => {
                if let Ok(p) =
                    crate::packets::ParcelDwellReplyPacket::new_from_bytes(&event.data, &mut pos)
                {
                    event
                        .simulator
                        .native_update_parcel_dwell(p.data.local_id, p.data.dwell);
                    self.events.dwell.emit(ParcelDwellReplyEventArgs {
                        parcel_id: p.data.parcel_id,
                        local_id: p.data.local_id,
                        dwell: p.data.dwell,
                    });
                }
            }
            PacketType::ParcelInfoReply => {
                if let Ok(p) =
                    crate::packets::ParcelInfoReplyPacket::new_from_bytes(&event.data, &mut pos)
                {
                    let d = p.data;
                    self.events.info.emit(ParcelInfoReplyEventArgs {
                        parcel: ParcelInfo {
                            actual_area: d.actual_area,
                            auction_id: d.auction_id,
                            billable_area: d.billable_area,
                            description: wire_string(&d.desc),
                            dwell: d.dwell,
                            global_x: d.global_x,
                            global_y: d.global_y,
                            global_z: d.global_z,
                            id: d.parcel_id,
                            mature: d.flags & 1 != 0,
                            name: wire_string(&d.name),
                            owner_id: d.owner_id,
                            sale_price: d.sale_price,
                            sim_name: wire_string(&d.sim_name),
                            snapshot_id: d.snapshot_id,
                        },
                    });
                }
            }
            PacketType::ParcelAccessListReply => {
                if let Ok(p) = crate::packets::ParcelAccessListReplyPacket::new_from_bytes(
                    &event.data,
                    &mut pos,
                ) {
                    let entries: Vec<_> = p
                        .list
                        .into_iter()
                        .take(65_535)
                        .map(|v| ParcelManagerParcelAccessEntry {
                            agent_id: v.id,
                            flags: crate::AccessList(v.flags),
                            time: UNIX_EPOCH + Duration::from_secs(v.time.max(0) as u64),
                        })
                        .collect();
                    event.simulator.native_update_parcel_access(
                        p.data.local_id,
                        p.data.flags,
                        entries.clone(),
                    );
                    self.events.access.emit(ParcelAccessListReplyEventArgs {
                        simulator: event.simulator,
                        sequence_id: p.data.sequence_id,
                        local_id: p.data.local_id,
                        flags: p.data.flags,
                        access_list: entries,
                    });
                }
            }
            PacketType::ForceObjectSelect => {
                if let Ok(p) =
                    crate::packets::ForceObjectSelectPacket::new_from_bytes(&event.data, &mut pos)
                {
                    self.events.force.emit(ForceSelectObjectsReplyEventArgs {
                        simulator: event.simulator,
                        object_i_ds: p.data.into_iter().map(|block| block.local_id).collect(),
                        reset_list: p.header.reset_list,
                    });
                }
            }
            PacketType::ParcelMediaUpdate => {
                if let Ok(p) =
                    crate::packets::ParcelMediaUpdatePacket::new_from_bytes(&event.data, &mut pos)
                {
                    self.events
                        .media_update
                        .emit(ParcelMediaUpdateReplyEventArgs {
                            simulator: event.simulator,
                            media: ParcelMedia {
                                media_auto_scale: p.data_block.media_auto_scale == 1,
                                media_desc: wire_string(&p.data_block_extended.media_desc),
                                media_height: p.data_block_extended.media_height,
                                media_id: p.data_block.media_id,
                                media_loop: p.data_block_extended.media_loop & 1 != 0,
                                media_type: wire_string(&p.data_block_extended.media_type),
                                media_url: wire_string(&p.data_block.media_url),
                                media_width: p.data_block_extended.media_width,
                            },
                        });
                }
            }
            PacketType::ParcelMediaCommandMessage => {
                if let Ok(p) = crate::packets::ParcelMediaCommandMessagePacket::new_from_bytes(
                    &event.data,
                    &mut pos,
                ) {
                    if let Some(command) = parcel_media_command(p.command_block.command) {
                        let mut header_pos = 0;
                        let mut packet_end = i32::try_from(event.data.len())
                            .ok()
                            .and_then(|length| length.checked_sub(1))
                            .unwrap_or_default();
                        let sequence = crate::packet_wire::decode_header(
                            &event.data,
                            &mut header_pos,
                            &mut packet_end,
                        )
                        .map_or(0, |header| header.sequence);
                        self.events.media_command.emit(ParcelMediaCommandEventArgs {
                            simulator: event.simulator,
                            sequence,
                            parcel_flags: crate::ParcelFlags(p.command_block.flags),
                            media_command: command,
                            time: p.command_block.time,
                        });
                    }
                }
            }
            _ => {}
        }
    }

    fn handle_owners(
        &self,
        msg: crate::messages::linden::ParcelObjectOwnersReplyMessage,
        simulator: Simulator,
    ) {
        if msg.prim_owners_block.len() > 65_535 {
            return;
        }
        let prim_owners = msg
            .prim_owners_block
            .into_iter()
            .map(|owner| ParcelManagerParcelPrimOwners {
                count: owner.count,
                is_group_owned: owner.is_group_owned,
                newest_prim: owner.time_stamp,
                online_status: owner.online_status,
                owner_id: owner.owner_id,
            })
            .collect();
        self.events.owners.emit(ParcelObjectOwnersReplyEventArgs {
            simulator,
            prim_owners,
        });
    }
    fn handle_properties(
        &self,
        msg: crate::messages::linden::ParcelPropertiesMessage,
        simulator: Simulator,
    ) {
        if let Ok(parcel) = parcel_from_message(&msg) {
            simulator.native_store_parcel(parcel.clone());
            self.events.properties.emit(ParcelPropertiesEventArgs {
                simulator: simulator.clone(),
                parcel: parcel.clone(),
                result: msg.request_result,
                selected_prims: msg.selected_prims,
                sequence_id: msg.sequence_id,
                snap_selection: msg.snap_selection,
            });
            if simulator.native_is_parcel_map_full() {
                self.events.all.emit(SimParcelsDownloadedEventArgs {
                    simulator: simulator.clone(),
                    sim_parcels: simulator.native_parcels_snapshot(),
                    parcel_map: simulator.native_parcel_map_snapshot(),
                });
            }
        }
    }
}

fn parcel_media_command(value: u32) -> Option<crate::ParcelMediaCommand> {
    Some(match value {
        0 => crate::ParcelMediaCommand::Stop,
        1 => crate::ParcelMediaCommand::Pause,
        2 => crate::ParcelMediaCommand::Play,
        3 => crate::ParcelMediaCommand::Loop,
        4 => crate::ParcelMediaCommand::Texture,
        5 => crate::ParcelMediaCommand::URL,
        6 => crate::ParcelMediaCommand::Time,
        7 => crate::ParcelMediaCommand::Agent,
        8 => crate::ParcelMediaCommand::Unload,
        9 => crate::ParcelMediaCommand::AutoAlign,
        10 => crate::ParcelMediaCommand::Type,
        11 => crate::ParcelMediaCommand::Size,
        12 => crate::ParcelMediaCommand::Desc,
        _ => return None,
    })
}

fn parcel_from_message(
    msg: &crate::messages::linden::ParcelPropertiesMessage,
) -> Result<Parcel, Error> {
    let mut p = Parcel::new(msg.local_id)?;
    p.aabb_max = msg.aabb_max;
    p.aabb_min = msg.aabb_min;
    p.area = msg.area;
    p.auction_id = msg.auction_id;
    p.auth_buyer_id = msg.auth_buyer_id;
    p.bitmap = msg.bitmap.clone();
    if p.bitmap.len() > 512 {
        return Err(Error::Argument);
    }
    p.category = msg.category;
    p.claim_date = msg.claim_date;
    p.claim_price = msg.claim_price;
    p.desc = msg.desc.clone();
    p.flags = msg.parcel_flags;
    p.group_id = msg.group_id;
    p.group_prims = msg.group_prims;
    p.is_group_owned = msg.is_group_owned;
    p.landing = msg.landing_type;
    p.max_prims = msg.max_prims;
    p.media.media_auto_scale = msg.media_auto_scale;
    p.media.media_id = msg.media_id;
    p.media.media_url = msg.media_url.clone();
    p.media.media_desc = msg.media_desc.clone();
    p.media.media_height = msg.media_height;
    p.media.media_width = msg.media_width;
    p.media.media_loop = msg.media_loop;
    p.media.media_type = msg.media_type.clone();
    p.music_url = msg.music_url.clone();
    p.name = msg.name.clone();
    p.other_clean_time = msg.other_clean_time;
    p.other_count = msg.other_count;
    p.other_prims = msg.other_prims;
    p.owner_id = msg.owner_id;
    p.owner_prims = msg.owner_prims;
    p.parcel_prim_bonus = msg.parcel_prim_bonus;
    p.pass_hours = msg.pass_hours;
    p.pass_price = msg.pass_price;
    p.public_count = msg.public_count;
    p.region_deny_age_unverified = msg.region_deny_age_unverified;
    p.region_deny_anonymous = msg.region_deny_anonymous;
    p.region_push_override = msg.region_push_override;
    p.rent_price = msg.rent_price;
    p.sale_price = msg.sale_price;
    p.see_a_vs = msg.see_a_vs;
    p.any_av_sounds = msg.any_av_sounds;
    p.group_av_sounds = msg.group_av_sounds;
    p.self_count = msg.self_count;
    p.sim_wide_max_prims = msg.sim_wide_max_prims;
    p.sim_wide_total_prims = msg.sim_wide_total_prims;
    p.snapshot_id = msg.snapshot_id;
    p.status = msg.status;
    p.total_prims = msg.total_prims;
    p.user_location = msg.user_location;
    p.user_look_at = msg.user_look_at;
    p.obscure_media = msg.obscure_media;
    p.obscure_music = msg.obscure_music;
    Ok(p)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::Ordering;

    #[test]
    fn parcel_coordinates_are_bounded() {
        let client = GridClient::new().unwrap();
        let sim = Simulator::new(
            client.clone(),
            "127.0.0.1:13".parse().unwrap(),
            0,
            None,
            None,
        )
        .unwrap();
        let manager = ParcelManager::new(client).unwrap();
        assert_eq!(
            manager
                .get_parcel_local_id(
                    sim.clone(),
                    Vector3 {
                        x: -1.0,
                        y: 0.0,
                        z: 0.0
                    }
                )
                .unwrap(),
            0
        );
        assert_eq!(
            manager
                .get_parcel_local_id(
                    sim,
                    Vector3 {
                        x: 256.0,
                        y: 0.0,
                        z: 0.0
                    }
                )
                .unwrap(),
            0
        );
    }
    #[test]
    fn bitmap_populates_four_meter_map() {
        let client = GridClient::new().unwrap();
        let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap();
        let mut parcel = Parcel::new(7).unwrap();
        parcel.bitmap = vec![1; 512];
        sim.native_store_parcel(parcel);
        assert_eq!(sim.native_parcel_map_at(0, 0), Some(7));
    }

    #[test]
    fn overlay_segments_are_correlated_by_sequence_before_completion() {
        let client = GridClient::new().unwrap();
        let sim = Simulator::new(client, "127.0.0.1:13".parse().unwrap(), 0, None, None).unwrap();

        assert!(!sim.native_apply_parcel_overlay(2, vec![3; 1024]).unwrap());
        assert!(!sim.native_apply_parcel_overlay(2, vec![7; 1024]).unwrap());
        assert_eq!(sim.parcel_overlays_received.load(Ordering::Acquire), 1);
        assert!(!sim.native_apply_parcel_overlay(0, vec![1; 1024]).unwrap());
        assert!(!sim.native_apply_parcel_overlay(3, vec![4; 1024]).unwrap());
        assert!(sim.native_apply_parcel_overlay(1, vec![2; 1024]).unwrap());
        assert_eq!(sim.parcel_overlays_received.load(Ordering::Acquire), 0);

        {
            let overlay = sim
                .parcel_overlay
                .read()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            assert_eq!(&overlay[0..1024], &[1; 1024]);
            assert_eq!(&overlay[1024..2048], &[2; 1024]);
            assert_eq!(&overlay[2048..3072], &[7; 1024]);
            assert_eq!(&overlay[3072..4096], &[4; 1024]);
        }
        assert!(sim.native_apply_parcel_overlay(0, vec![0; 1023]).is_err());
    }

    #[test]
    fn parcel_properties_caps_message_updates_state_before_emitting() {
        let client = GridClient::new().unwrap();
        let sim = Simulator::new(
            client.clone(),
            "127.0.0.1:13".parse().unwrap(),
            0,
            None,
            None,
        )
        .unwrap();
        let manager = ParcelManager::new(client).unwrap();
        let observed = Arc::new(Mutex::new(None));
        let captured = Arc::clone(&observed);
        let _subscription = manager.subscribe_parcel_properties(Arc::new(move |event| {
            *mutex(&captured) = Some((
                event.sequence_id(),
                event.parcel().local_id,
                event.simulator().native_parcel(event.parcel().local_id),
            ));
        }));

        let mut message = crate::messages::linden::ParcelPropertiesMessage::new().unwrap();
        message.local_id = 23;
        message.sequence_id = 91;
        message.name = "Packet-path parcel".into();
        message.bitmap = vec![0; 512];
        message.bitmap[0] = 1;
        manager.inner.handle_properties(message, sim.clone());

        let observed = mutex(&observed).clone().expect("parcel event");
        assert_eq!((observed.0, observed.1), (91, 23));
        assert_eq!(
            observed.2.expect("state visible to callback").name,
            "Packet-path parcel"
        );
        assert_eq!(sim.native_parcel_map_at(0, 0), Some(23));
    }
}
