//! Native Rust grid client corresponding to the main `LibreMetaverse` assembly.
//!
//! Start here for login, networking, agents, inventory, assets, appearance,
//! world, and social APIs. The appended guide covers crate selection, C# name
//! migration, ownership, cancellation, events, security, and `OpenSim` setup.

#![doc = include_str!("../README.md")]

extern crate self as libremetaverse;

#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod attention_catalog;
mod agent_manager;
mod agent_messages;
mod agent_movement;
mod animation;
#[path = "animesh.rs"]
mod animesh_runtime;
mod animesh_skinning;
mod appearance_baker;
mod appearance_manager;
mod asset_archive;
mod asset_cache;
mod asset_manager;
mod asset_material;
mod asset_models;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod asset_prim;
mod avatar_manager;
mod avatar_rig;
mod baking_texture_provider;
mod bit_pack;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod builtin_simple_renderer;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod callback_runtime;
mod caps;
mod caps_http;
mod client_core;
mod current_outfit;
mod download_manager;
mod environment_manager;
mod estate_tools;
mod event_queue;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod event_subscription;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod file_helper;
mod friends_manager;
mod gesture;
mod gltf;
mod group_manager;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod helpers;
mod initial_outfit;
mod inventory;
mod inventory_ais;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod inventory_exception;
mod inventory_manager;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod foliage_catalog;
mod generated;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod genepool_catalog;
mod j2k;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod linden_mesh;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod logger;
mod login;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod lsl_syntax;
#[path = "marketplace.rs"]
mod marketplace_runtime;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod mesh_asset;
mod message_codec;
mod message_decoder;
mod model_workflow;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod name_value;
mod network_manager;
mod object_manager;
mod object_material;
mod object_model;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod observable_dictionary;
#[rustfmt::skip]
pub mod packet_catalog;
mod directory_manager;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod disposal_helper;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod dynamic_messages;
mod grid_manager;
pub use grid_manager::MapItemData;
mod interest_list;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod packet_base;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod packet_decoder;
mod packet_wire;
mod parcel_manager;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod particle_simulator;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod port_errors;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod position_helper;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod prim_object;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod protocol_manager;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod rendering_binary_reader;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod rendering_loader;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod rendering_metadata;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod repeat;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod service_collection;
mod sim_stats;
mod skeleton;
mod slurl;
mod sound_manager;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod skeleton_catalog;
mod targa;
mod terrain_codec;
mod terrain_manager;
mod transfers;
mod udp_transport;
mod user_report;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod uuid_formatter;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod visual_catalog;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod avatar_model;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod avatar_physics;

#[cfg(test)]
mod appearance_baker_semantics;
#[cfg(test)]
mod asset_archive_semantics;
#[cfg(test)]
mod asset_pipeline_semantics;
#[cfg(test)]
mod avatar_animesh_semantics;
#[cfg(test)]
mod batch_link_internal_semantics;
#[cfg(test)]
mod inventory_ais_internal_semantics;
#[cfg(test)]
mod world_internal_semantics;

type Terrain = Vec<Vec<f32>>;
type TerrainLoaded = Box<dyn Fn(Terrain, i64, i64) + Send + Sync>;

impl assets::OarFile {
    #[allow(dead_code, clippy::needless_pass_by_value)]
    fn load_terrain(
        file_path: &str,
        data: Vec<u8>,
        callback: TerrainLoaded,
        bytes_read: i64,
        total_bytes: i64,
    ) -> Result<bool, Error> {
        crate::asset_archive::load_terrain(file_path, &data, callback, bytes_read, total_bytes)
    }
}

#[allow(dead_code)]
impl animesh::AnimationTrack {
    fn new(animation_id: libremetaverse_types::UUID) -> Result<Self, Error> {
        Self::native_new(animation_id)
    }

    fn decode_rotation(
        value: libremetaverse_types::Vector3,
    ) -> Result<libremetaverse_types::Quaternion, Error> {
        Self::native_decode_rotation(value)
    }
}

#[allow(
    clippy::needless_pass_by_value,
    clippy::unnecessary_wraps,
    clippy::unused_self,
    dead_code
)]
impl animesh::AnimeshPlayer {
    fn new(object_id: libremetaverse_types::UUID) -> Result<Self, Error> {
        Self::native_new(object_id)
    }

    fn get_or_add_track(
        &mut self,
        animation_id: libremetaverse_types::UUID,
    ) -> Result<std::sync::Arc<animesh::AnimationTrack>, Error> {
        self.native_get_or_add_track(animation_id)
    }

    fn retain_only(
        &mut self,
        animation_ids: std::collections::HashSet<libremetaverse_types::UUID>,
    ) -> Result<(), Error> {
        self.native_retain_only(&animation_ids);
        Ok(())
    }
}

#[allow(dead_code)] // Internal C# surface consumed by InventoryManager workflows.
impl InventoryAISClient {
    async fn create_inventory_links(
        &self,
        parent_uuid: libremetaverse_types::UUID,
        new_inventory: libremetaverse_structured_data::OSD,
        cancellation_token: Option<libremetaverse_types::compat::CancellationToken>,
    ) -> Result<Vec<InventoryItem>, Error> {
        self.native_create_inventory_links(parent_uuid, new_inventory, cancellation_token)
            .await
    }
}

#[allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps)]
impl NetworkManager {
    fn raise_sim_console_response(
        &self,
        message: messages::linden::SimConsoleResponseMessage,
        simulator: Simulator,
    ) -> Result<(), Error> {
        self.dispatch_caps_event("SimConsoleResponse", &message, simulator);
        Ok(())
    }
}

#[allow(dead_code)]
impl imaging::Baker {
    fn resize_to_bake_dimensions(
        source: libremetaverse_imaging::ManagedImage,
        width: i32,
        height: i32,
    ) -> Result<Option<libremetaverse_imaging::ManagedImage>, Error> {
        crate::appearance_baker::Baker::native_resize_to_bake_dimensions(source, width, height)
    }
}

pub use callback_runtime::{AsyncInvocation, DictionaryCallbackTarget};
pub use caps_http::CapsHttpLimits;
pub use client_core::{
    ClientCoreError, ClientLifecycleState, ClientService, GridClientBuilder, ShutdownPhase,
};
pub use generated::*;
pub use libremetaverse_imaging as imaging_abstractions;
pub use libremetaverse_structured_data as structured_data;
pub use libremetaverse_types as types;
pub use libremetaverse_types::Error;
pub use logger::LoggerCallbackTarget;
pub use network_manager::SimulatorCollection;
pub use udp_transport::{
    AgentThrottleSender, UdpPacketHandler, UdpThrottleCategory, UdpTransportConfig,
    UdpTransportError, UdpTransportStats,
};
pub use user_report::{UserReport, UserReportService, UserReportType};

pub(crate) fn is_offline_fixture_uri(uri: &libremetaverse_types::compat::Uri) -> bool {
    reqwest::Url::parse(&uri.0)
        .ok()
        .and_then(|url| url.host_str().map(str::to_owned))
        .is_some_and(|host| host == "invalid" || host.ends_with(".invalid"))
}

#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod collada;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod coordinate_frame;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod physics_volume_morphs;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod simulator_data_pool;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod sound_catalog;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod statistics_runtime;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod texture_pipeline;
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the pinned C# API.
mod threading_runtime;

/// Rust object-safe view of the C# `InventoryBase` inheritance hierarchy.
pub trait InventoryObjectClass: std::any::Any {
    /// Returns the concrete value for exact C# runtime-type checks.
    fn as_any(&self) -> &dyn std::any::Any;

    /// Returns the shared `InventoryBase` portion of the concrete object.
    fn inventory_base(&self) -> &InventoryBase;

    /// Returns the shared `InventoryItem` portion for item-derived objects.
    fn inventory_item(&self) -> Option<&InventoryItem> {
        None
    }
}

/// Rust object-safe view of concrete classes derived from C# `InventoryItem`.
pub trait InventoryItemClass: InventoryObjectClass {}

macro_rules! inventory_object_class {
    ($type:ty, $base:ident $(.$nested:ident)*) => {
        impl InventoryObjectClass for $type {
            fn as_any(&self) -> &dyn std::any::Any {
                self
            }

            fn inventory_base(&self) -> &InventoryBase {
                &self.$base$(.$nested)*
            }
        }
    };
}

macro_rules! inventory_item_class {
    ($type:ty, $item:ident $(.$nested:ident)*) => {
        impl InventoryObjectClass for $type {
            fn as_any(&self) -> &dyn std::any::Any {
                self
            }

            fn inventory_base(&self) -> &InventoryBase {
                &self.$item$(.$nested)*.base
            }

            fn inventory_item(&self) -> Option<&InventoryItem> {
                Some(&self.$item$(.$nested)*)
            }
        }
    };
}

impl InventoryObjectClass for InventoryBase {
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn inventory_base(&self) -> &InventoryBase {
        self
    }
}

inventory_object_class!(InventoryFolder, base);
impl InventoryObjectClass for InventoryItem {
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn inventory_base(&self) -> &InventoryBase {
        &self.base
    }

    fn inventory_item(&self) -> Option<&InventoryItem> {
        Some(self)
    }
}
inventory_item_class!(InventoryAnimation, base);
inventory_item_class!(InventoryAttachment, base);
inventory_item_class!(InventoryCallingCard, base);
inventory_item_class!(InventoryCategory, base);
inventory_item_class!(InventoryGesture, base);
inventory_item_class!(InventoryLSL, base);
inventory_item_class!(InventoryLandmark, base);
inventory_item_class!(InventoryMaterial, base);
inventory_item_class!(InventoryNotecard, base);
inventory_item_class!(InventoryObject, base);
inventory_item_class!(InventorySettings, base);
inventory_item_class!(InventorySnapshot, base);
inventory_item_class!(InventorySound, base);
inventory_item_class!(InventoryTexture, base);
inventory_item_class!(InventoryWearable, base);

impl InventoryItemClass for InventoryItem {}
impl InventoryItemClass for InventoryAnimation {}
impl InventoryItemClass for InventoryAttachment {}
impl InventoryItemClass for InventoryCallingCard {}
impl InventoryItemClass for InventoryCategory {}
impl InventoryItemClass for InventoryGesture {}
impl InventoryItemClass for InventoryLSL {}
impl InventoryItemClass for InventoryLandmark {}
impl InventoryItemClass for InventoryMaterial {}
impl InventoryItemClass for InventoryNotecard {}
impl InventoryItemClass for InventoryObject {}
impl InventoryItemClass for InventorySettings {}
impl InventoryItemClass for InventorySnapshot {}
impl InventoryItemClass for InventorySound {}
impl InventoryItemClass for InventoryTexture {}
impl InventoryItemClass for InventoryWearable {}

/// Project-owned boundary types for external `MessagePack` signatures.
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the external API boundary.
pub mod message_pack {
    use libremetaverse_types::Error;

    pub trait IMessagePackFormatter<T = ()> {
        fn serialize(
            &self,
            writer: &mut MessagePackWriter,
            value: T,
            options: MessagePackSerializerOptions,
        ) -> Result<(), Error>;

        fn deserialize(
            &mut self,
            reader: &mut MessagePackReader,
            options: MessagePackSerializerOptions,
        ) -> Result<T, Error>;
    }

    #[derive(Clone, Debug)]
    pub struct MessagePackReader {
        bytes: Vec<u8>,
        position: usize,
    }

    impl MessagePackReader {
        #[must_use]
        pub fn new(bytes: Vec<u8>) -> Self {
            Self { bytes, position: 0 }
        }

        pub fn read_bytes(&mut self) -> Result<Option<Vec<u8>>, Error> {
            let marker = *self
                .bytes
                .get(self.position)
                .ok_or(Error::IndexOutOfRange)?;
            self.position += 1;
            if marker == 0xc0 {
                return Ok(None);
            }
            let length = match marker {
                0xc4 => {
                    let length = *self
                        .bytes
                        .get(self.position)
                        .ok_or(Error::IndexOutOfRange)?;
                    self.position += 1;
                    usize::from(length)
                }
                0xc5 => {
                    let length = self
                        .bytes
                        .get(self.position..self.position + 2)
                        .ok_or(Error::IndexOutOfRange)?;
                    self.position += 2;
                    usize::from(u16::from_be_bytes([length[0], length[1]]))
                }
                0xc6 => {
                    let length = self
                        .bytes
                        .get(self.position..self.position + 4)
                        .ok_or(Error::IndexOutOfRange)?;
                    self.position += 4;
                    usize::try_from(u32::from_be_bytes([
                        length[0], length[1], length[2], length[3],
                    ]))
                    .map_err(|_| Error::IndexOutOfRange)?
                }
                _ => {
                    return Err(Error::Parse {
                        position: self.position - 1,
                        context: "MessagePack binary marker",
                    });
                }
            };
            let end = self
                .position
                .checked_add(length)
                .ok_or(Error::IndexOutOfRange)?;
            let value = self
                .bytes
                .get(self.position..end)
                .ok_or(Error::IndexOutOfRange)?
                .to_vec();
            self.position = end;
            Ok(Some(value))
        }
    }

    #[derive(Clone, Copy, Debug, Default)]
    pub struct MessagePackSerializerOptions;

    #[derive(Clone, Debug, Default)]
    pub struct MessagePackWriter {
        bytes: Vec<u8>,
    }

    impl MessagePackWriter {
        #[must_use]
        pub fn new() -> Self {
            Self::default()
        }

        pub fn write_bytes(&mut self, value: &[u8]) -> Result<(), Error> {
            match value.len() {
                length if u8::try_from(length).is_ok() => {
                    self.bytes.push(0xc4);
                    self.bytes.push(length as u8);
                }
                length if u16::try_from(length).is_ok() => {
                    self.bytes.push(0xc5);
                    self.bytes.extend_from_slice(&(length as u16).to_be_bytes());
                }
                length => {
                    self.bytes.push(0xc6);
                    let length = u32::try_from(length).map_err(|_| Error::Argument)?;
                    self.bytes.extend_from_slice(&length.to_be_bytes());
                }
            }
            self.bytes.extend_from_slice(value);
            Ok(())
        }

        #[must_use]
        pub fn as_slice(&self) -> &[u8] {
            &self.bytes
        }

        #[must_use]
        pub fn into_bytes(self) -> Vec<u8> {
            self.bytes
        }
    }
}

/// Project-owned boundary types for external logging signatures.
#[allow(clippy::all, clippy::pedantic)] // Public shapes mirror the external API boundary.
pub mod logging {
    use std::sync::Arc;

    use libremetaverse_types::compat::{Close, ExternalError, Object};

    pub trait ILogger: Send + Sync {
        fn is_enabled(&self, level: LogLevel) -> bool;
        fn log(
            &self,
            level: LogLevel,
            message: &Object,
            exception: Option<&ExternalError>,
            client_name: Option<&str>,
        );
        fn begin_scope(&self, state: Object) -> Box<dyn Close>;
    }

    pub trait ILoggerFactory: Send + Sync {
        fn create_logger(&self, name: &str) -> Arc<dyn ILogger>;
        fn shutdown(&self) -> Result<(), ExternalError>;
    }

    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    pub struct LogLevel(pub i32);

    impl LogLevel {
        pub const TRACE: Self = Self(0);
        pub const DEBUG: Self = Self(1);
        pub const INFORMATION: Self = Self(2);
        pub const WARNING: Self = Self(3);
        pub const ERROR: Self = Self(4);
        pub const CRITICAL: Self = Self(5);
        pub const NONE: Self = Self(6);
    }
}

/// Project-owned boundary types for external networking signatures.
pub mod net {
    pub struct RateLimiter;
    pub struct Socket;
    pub struct TcpStream;
    pub struct UdpSocket;
}

/// Project-owned boundary types for external service-collection signatures.
pub mod services {
    pub use crate::service_collection::{IServiceCollection, ServiceCollection};
}

/// Common behavior for program shims while the network stack is unimplemented.
pub mod shim {
    use std::process::ExitCode;

    /// Reports that an upstream sample has a target but no implementation yet.
    #[must_use]
    pub fn pending_program(name: &str) -> ExitCode {
        eprintln!("{name}: LibreMetaverse Rust port is not implemented yet");
        ExitCode::FAILURE
    }
}
