use std::ffi::CStr;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::ptr::NonNull;
use std::rc::Rc;
use pipewire_wrapper_proc_macro::RawWrapper;
use crate::core_api::core::Core;
use crate::core_api::proxy::events::ProxyEvents;
use crate::core_api::type_info::TypeInfo;
use crate::error::Error;
use crate::i32_as_void_result;
use crate::impl_api::protocol::ProtocolRef;
use crate::listeners::AddListener;
use crate::spa::SPA_ID_INVALID;
use crate::wrapper::{RawWrapper, Wrapper};
pub mod events;
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct ProxyRef {
    #[raw]
    raw: pw_sys::pw_proxy,
}
#[derive(Debug)]
pub struct Proxy<'c> {
    inner: Rc<InnerProxy>,
    core: &'c Core,
}
#[derive(Debug)]
struct InnerProxy {
    ref_: NonNull<ProxyRef>,
}
pub trait Proxied: RawWrapper {
    fn type_info() -> TypeInfo<'static>;
    fn as_proxy(&self) -> &ProxyRef {
        unsafe { ProxyRef::from_raw_ptr(self.as_raw_ptr() as *mut _) }
    }
}
impl<'c> Proxy<'c> {
    pub(crate) fn from_ref(core: &'c Core, ref_: &ProxyRef) -> Self {
        Self {
            inner: Rc::new(InnerProxy {
                ref_: NonNull::new(ref_.as_ptr()).unwrap(),
            }),
            core,
        }
    }
    pub fn core(&self) -> &'c Core {
        self.core
    }
}
impl<'c> Wrapper for Proxy<'c> {
    type RawWrapperType = ProxyRef;
}
impl<'c> AsMut<ProxyRef> for Proxy<'c> {
    fn as_mut(&mut self) -> &'c mut ProxyRef {
        unsafe { &mut *self.inner.ref_.as_ptr() }
    }
}
impl<'c> AsRef<ProxyRef> for Proxy<'c> {
    fn as_ref(&self) -> &'c ProxyRef {
        unsafe { self.inner.ref_.as_ref() }
    }
}
impl<'c> Deref for Proxy<'c> {
    type Target = <Self as crate::wrapper::Wrapper>::RawWrapperType;
    fn deref(&self) -> &'c Self::Target {
        unsafe { self.inner.ref_.as_ref() }
    }
}
impl<'c> DerefMut for Proxy<'c> {
    fn deref_mut(&mut self) -> &'c mut Self::Target {
        unsafe { &mut *self.inner.ref_.as_ptr() }
    }
}
impl Drop for InnerProxy {
    fn drop(&mut self) {
        unsafe { pw_sys::pw_proxy_destroy(self.ref_.as_ref().as_raw_ptr()) }
    }
}
impl<'c> Clone for Proxy<'c> {
    fn clone(&self) -> Self {
        let cloned = Self {
            inner: self.inner.clone(),
            core: self.core,
        };
        unsafe { pw_sys::pw_proxy_ref(self.as_raw_ptr()) };
        cloned
    }
}
impl<'c> Drop for Proxy<'c> {
    fn drop(&mut self) {
        if Rc::strong_count(&self.inner) > 1 {
            unsafe { pw_sys::pw_proxy_unref(self.as_raw_ptr()) }
        }
    }
}
impl ProxyRef {
    pub fn get_id(&self) -> u32 {
        unsafe { pw_sys::pw_proxy_get_id(self.as_raw_ptr()) }
    }
    pub fn is_bound(&self) -> bool {
        self.get_id() != SPA_ID_INVALID
    }
    pub fn get_type_and_version(&self) -> (TypeInfo, u32) {
        let mut version = 0u32;
        let type_str = unsafe { pw_sys::pw_proxy_get_type(self.as_raw_ptr(), &mut version) };
        unsafe { (TypeInfo::from_c_str(CStr::from_ptr(type_str)), version) }
    }
    pub fn get_type(&self) -> TypeInfo {
        self.get_type_and_version().0
    }
    pub fn get_protocol(&self) -> &ProtocolRef {
        unsafe { ProtocolRef::from_raw_ptr(pw_sys::pw_proxy_get_protocol(self.as_raw_ptr())) }
    }
    pub fn sync(&self, seq: i32) -> crate::Result<()> {
        let result = unsafe { pw_sys::pw_proxy_sync(self.as_raw_ptr(), seq) };
        i32_as_void_result(result)
    }
    pub fn get_bound_id(&self) -> Option<u32> {
        let id = unsafe { pw_sys::pw_proxy_get_bound_id(self.as_raw_ptr()) };
        if id == SPA_ID_INVALID {
            None
        } else {
            Some(id)
        }
    }
    pub fn error(&self, res: i32, error: &CStr) -> crate::Result<()> {
        let result = unsafe { pw_sys::pw_proxy_error(self.as_raw_ptr(), res, error.as_ptr()) };
        i32_as_void_result(result)
    }
    pub fn as_object<T>(&self) -> crate::Result<&T>
    where
        T: Proxied,
    {
        let proxy_type = self.get_type();
        let target_type = T::type_info();
        if proxy_type == target_type {
            unsafe { Ok(self.as_object_unchecked()) }
        } else {
            Err(Error::TypeMismatch)
        }
    }
    pub(crate) unsafe fn as_object_unchecked<T>(&self) -> &'_ T
    where
        T: Proxied,
    {
        T::from_raw_ptr(self.as_raw_ptr().cast())
    }
}
impl<'a> AddListener<'a> for ProxyRef {
    type Events = ProxyEvents<'a>;
    fn add_listener(&self, events: Pin<Box<Self::Events>>) -> Pin<Box<Self::Events>> {
        unsafe {
            pw_sys::pw_proxy_add_listener(
                self.as_raw_ptr(),
                events.hook().as_raw_ptr(),
                events.as_raw_ptr(),
                &*events as *const _ as *mut _,
            )
        }
        events
    }
}