//! Public rewrite shell corresponding to the main `LibreMetaverse` assembly.

extern crate self as libremetaverse;

mod generated;

#[cfg(test)]
mod appearance_baker_semantics;
#[cfg(test)]
mod asset_archive_semantics;
#[cfg(test)]
mod avatar_animesh_semantics;
#[cfg(test)]
mod batch_link_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)]
    fn load_terrain(
        _file_path: &str,
        _data: Vec<u8>,
        _callback: TerrainLoaded,
        _bytes_read: i64,
        _total_bytes: i64,
    ) -> Result<bool, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Assets.OarFile.LoadTerrain(System.String,System.Byte[],LibreMetaverse.Assets.OarFile.TerrainLoadedCallback,System.Int64,System.Int64)",
        )
    }
}

#[allow(dead_code)]
impl animesh::AnimationTrack {
    fn new(_animation_id: libremetaverse_types::UUID) -> Result<Self, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Animesh.AnimationTrack.#ctor(LibreMetaverse.UUID)",
        )
    }

    fn decode_rotation(
        _value: libremetaverse_types::Vector3,
    ) -> Result<libremetaverse_types::Quaternion, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Animesh.AnimationTrack.DecodeRotation(LibreMetaverse.Vector3)",
        )
    }
}

#[allow(clippy::unused_self, dead_code)]
impl animesh::AnimeshPlayer {
    fn new(_object_id: libremetaverse_types::UUID) -> Result<Self, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Animesh.AnimeshPlayer.#ctor(LibreMetaverse.UUID)",
        )
    }

    fn get_or_add_track(
        &mut self,
        _animation_id: libremetaverse_types::UUID,
    ) -> Result<std::sync::Arc<animesh::AnimationTrack>, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Animesh.AnimeshPlayer.GetOrAddTrack(LibreMetaverse.UUID)",
        )
    }

    fn retain_only(
        &mut self,
        _animation_ids: std::collections::HashSet<libremetaverse_types::UUID>,
    ) -> Result<(), Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Animesh.AnimeshPlayer.RetainOnly(System.Collections.Generic.ISet{LibreMetaverse.UUID})",
        )
    }
}

#[allow(dead_code)]
trait CofLinkItem {
    fn inventory_item(&self) -> &InventoryItem;
    fn wearable_type(&self) -> Option<libremetaverse_types::WearableType>;
}

impl CofLinkItem for InventoryWearable {
    fn inventory_item(&self) -> &InventoryItem {
        &self.base
    }

    fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
        Some(self.wearable_type())
    }
}

impl CofLinkItem for InventoryObject {
    fn inventory_item(&self) -> &InventoryItem {
        &self.base
    }

    fn wearable_type(&self) -> Option<libremetaverse_types::WearableType> {
        None
    }
}

#[allow(clippy::unused_self, dead_code)]
impl appearance::CurrentOutfitFolder {
    fn get_cof_link_description(&self, _item: &dyn CofLinkItem) -> Result<String, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Appearance.CurrentOutfitFolder.GetCofLinkDescription(LibreMetaverse.InventoryItem)",
        )
    }
}

#[allow(clippy::unused_async, dead_code)]
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> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.InventoryAISClient.CreateInventoryLinksAsync(LibreMetaverse.UUID,LibreMetaverse.StructuredData.OSD,System.Threading.CancellationToken)",
        )
    }
}

#[allow(dead_code)]
impl AgentManager {
    fn extract_experience_permission(
        _map: libremetaverse_structured_data::OSDMap,
        _experience_id: libremetaverse_types::UUID,
    ) -> Result<String, Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.AgentManager.ExtractExperiencePermission(LibreMetaverse.StructuredData.OSDMap,LibreMetaverse.UUID)",
        )
    }
}

#[allow(dead_code, clippy::unused_self)]
impl EstateTools {
    fn estate_owner_message_handler(
        &self,
        _packet: packets::EstateOwnerMessagePacket,
        _simulator: Simulator,
    ) -> Result<(), Error> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.EstateTools.EstateOwnerMessageHandler(System.Object,LibreMetaverse.PacketReceivedEventArgs)",
        )
    }
}

#[allow(dead_code, clippy::unused_self)]
impl NetworkManager {
    fn raise_sim_console_response(
        &self,
        _message: messages::linden::SimConsoleResponseMessage,
        _simulator: Simulator,
    ) -> Result<(), Error> {
        libremetaverse_types::not_implemented(
            "F:LibreMetaverse.NetworkManager.CapsEvents.SimConsoleResponse",
        )
    }
}

#[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> {
        libremetaverse_types::not_implemented(
            "M:LibreMetaverse.Imaging.Baker.ResizeToBakeDimensions(LibreMetaverse.Imaging.ManagedImage,System.Int32,System.Int32)",
        )
    }
}

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;

/// 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;
}

/// 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)*
            }
        }
    };
}

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

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

inventory_object_class!(InventoryFolder, base);
inventory_object_class!(InventoryItem, base);
inventory_object_class!(InventoryAnimation, base.base);
inventory_object_class!(InventoryAttachment, base.base);
inventory_object_class!(InventoryCallingCard, base.base);
inventory_object_class!(InventoryCategory, base.base);
inventory_object_class!(InventoryGesture, base.base);
inventory_object_class!(InventoryLSL, base.base);
inventory_object_class!(InventoryLandmark, base.base);
inventory_object_class!(InventoryMaterial, base.base);
inventory_object_class!(InventoryNotecard, base.base);
inventory_object_class!(InventoryObject, base.base);
inventory_object_class!(InventorySettings, base.base);
inventory_object_class!(InventorySnapshot, base.base);
inventory_object_class!(InventorySound, base.base);
inventory_object_class!(InventoryTexture, base.base);
inventory_object_class!(InventoryWearable, base.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.
pub mod message_pack {
    pub trait IMessagePackFormatter<T = ()> {}
    pub struct MessagePackReader;
    pub struct MessagePackSerializerOptions;
    pub struct MessagePackWriter;
}

/// Project-owned boundary types for external logging signatures.
pub mod logging {
    pub trait ILoggerFactory {}

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

/// 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 trait IServiceCollection {}
}

/// 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
    }
}
