use std::ffi::CStr;
use std::fmt::{Debug, Formatter};
use std::pin::Pin;
use std::ptr::NonNull;
use derive_builder::Builder;
use pw_sys::{pw_buffer, pw_filter_events, pw_filter_state};
use spa_sys::{spa_command, spa_pod};
use pipewire_wrapper_proc_macro::{RawWrapper, Wrapper};
use crate::events_builder_build;
use crate::filter::FilterState;
use crate::spa::interface::Hook;
use crate::spa::io::IOPositionRef;
use crate::spa::pod::PodRef;
use crate::spa::type_::CommandRef;
use crate::stream::buffer::BufferRef;
use crate::wrapper::RawWrapper;
#[derive(RawWrapper, Debug)]
#[repr(transparent)]
pub struct FilterEventsRef {
    #[raw]
    raw: pw_sys::pw_filter_events,
}
pub type DestroyCallback<'p> = Box<dyn FnMut() + 'p>;
pub type StateChangedCallback<'p> =
    Box<dyn for<'a> FnMut(FilterState, FilterState, Option<&'a CStr>) + 'p>;
pub type IOChangedCallback<'p, T> =
    Box<dyn for<'a> FnMut(Option<&mut T>, u32, *mut ::std::os::raw::c_void, u32) + 'p>;
pub type ParamChangedCallback<'p, T> = Box<dyn for<'a> FnMut(Option<&mut T>, u32, &'a PodRef) + 'p>;
pub type AddBufferCallback<'p, T> = Box<dyn for<'a> FnMut(&mut T, &'a BufferRef) + 'p>;
pub type RemoveBufferCallback<'p, T> = Box<dyn for<'a> FnMut(&mut T, &'a BufferRef) + 'p>;
pub type ProcessCallback<'p> = Box<dyn for<'a> FnMut(&'a IOPositionRef) + 'p>;
pub type DrainedCallback<'p> = Box<dyn FnMut() + 'p>;
pub type CommandCallback<'p> = Box<dyn for<'a> FnMut(&'a CommandRef) + 'p>;
#[derive(Wrapper, Builder)]
#[builder(setter(skip, strip_option), build_fn(skip), pattern = "owned")]
pub struct FilterEvents<'p, T> {
    #[raw_wrapper]
    ref_: NonNull<FilterEventsRef>,
    raw: Pin<Box<FilterEventsRef>>,
    hook: Pin<Box<Hook>>,
    #[builder(setter)]
    destroy: Option<DestroyCallback<'p>>,
    #[builder(setter)]
    state_changed: Option<StateChangedCallback<'p>>,
    #[builder(setter)]
    io_changed: Option<IOChangedCallback<'p, T>>,
    #[builder(setter)]
    param_changed: Option<ParamChangedCallback<'p, T>>,
    #[builder(setter)]
    add_buffer: Option<AddBufferCallback<'p, T>>,
    #[builder(setter)]
    remove_buffer: Option<RemoveBufferCallback<'p, T>>,
    #[builder(setter)]
    process: Option<ProcessCallback<'p>>,
    #[builder(setter)]
    drained: Option<DrainedCallback<'p>>,
    #[builder(setter)]
    command: Option<CommandCallback<'p>>,
}
impl<'p, T> FilterEvents<'p, T> {
    unsafe extern "C" fn destroy_call(data: *mut ::std::os::raw::c_void) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.destroy {
                callback();
            }
        }
    }
    unsafe extern "C" fn state_changed_call(
        data: *mut ::std::os::raw::c_void,
        old: pw_filter_state,
        state: pw_filter_state,
        error: *const ::std::os::raw::c_char,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.state_changed {
                callback(
                    FilterState::from_raw(old),
                    FilterState::from_raw(state),
                    error.as_ref().map(|e| CStr::from_ptr(e)),
                );
            }
        }
    }
    unsafe extern "C" fn io_changed_call(
        port_data: *mut ::std::os::raw::c_void,
        data: *mut ::std::os::raw::c_void,
        id: u32,
        area: *mut ::std::os::raw::c_void,
        size: u32,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.io_changed {
                callback((port_data as *mut T).as_mut(), id, area, size);
            }
        }
    }
    unsafe extern "C" fn param_changed_call(
        port_data: *mut ::std::os::raw::c_void,
        data: *mut ::std::os::raw::c_void,
        id: u32,
        param: *const spa_pod,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.param_changed {
                callback(
                    (port_data as *mut T).as_mut(),
                    id,
                    PodRef::from_raw_ptr(param),
                );
            }
        }
    }
    unsafe extern "C" fn add_buffer_call(
        port_data: *mut ::std::os::raw::c_void,
        data: *mut ::std::os::raw::c_void,
        buffer: *mut pw_buffer,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.add_buffer {
                callback(
                    (port_data as *mut T).as_mut().unwrap(),
                    BufferRef::from_raw_ptr(buffer),
                );
            }
        }
    }
    unsafe extern "C" fn remove_buffer_call(
        port_data: *mut ::std::os::raw::c_void,
        data: *mut ::std::os::raw::c_void,
        buffer: *mut pw_buffer,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.remove_buffer {
                callback(
                    (port_data as *mut T).as_mut().unwrap(),
                    BufferRef::from_raw_ptr(buffer),
                );
            }
        }
    }
    unsafe extern "C" fn process_call(
        data: *mut ::std::os::raw::c_void,
        position: *mut spa_sys::spa_io_position,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.process {
                callback(IOPositionRef::from_raw_ptr(position));
            }
        }
    }
    unsafe extern "C" fn drained_call(data: *mut ::std::os::raw::c_void) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.drained {
                callback();
            }
        }
    }
    unsafe extern "C" fn command_call(
        data: *mut ::std::os::raw::c_void,
        command: *const spa_command,
    ) {
        if let Some(events) = (data as *mut FilterEvents<'p, T>).as_mut() {
            if let Some(callback) = &mut events.command {
                callback(CommandRef::from_raw_ptr(command));
            }
        }
    }
    pub fn hook(&self) -> &Pin<Box<Hook>> {
        &self.hook
    }
}
impl<'p, T> FilterEventsBuilder<'p, T> {
    events_builder_build! {
        FilterEvents<'p, T>,
        pw_filter_events,
        destroy => destroy_call,
        state_changed => state_changed_call,
        io_changed => io_changed_call,
        param_changed => param_changed_call,
        add_buffer => add_buffer_call,
        remove_buffer => remove_buffer_call,
        process => process_call,
        drained => drained_call,
        command => command_call,
    }
}
impl<T> Debug for FilterEvents<'_, T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("FilterEvents")
            .field("raw", &self.raw)
            .finish()
    }
}