use std::{ops::Deref, path::Path};

use block2::{Block, DynBlock, RcBlock};
use objc2::{
    Message, extern_class, extern_conformance, extern_methods, extern_protocol, msg_send,
    rc::{Allocated, Retained},
    runtime::ProtocolObject,
};
use objc2_foundation::{CopyingHelper, NSArray, NSCopying, NSError, NSObject, NSObjectProtocol, NSString, NSURL};

use crate::*;

pub struct MTLNewLibraryCompletionHandler(RcBlock<dyn Fn(*mut ProtocolObject<dyn MTLLibrary>, *mut NSError)>);

impl MTLNewLibraryCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTLLibrary>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(move |library_ptr: *mut ProtocolObject<dyn MTLLibrary>, error: *mut NSError| {
            let library = unsafe { Retained::from_raw(library_ptr) };
            handler(library, error);
        }))
    }

    pub fn copy(ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTLLibrary>, *mut NSError)>) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTLNewLibraryCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTLLibrary>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct MTLNewDynamicLibraryCompletionHandler(
    RcBlock<dyn Fn(*mut ProtocolObject<dyn MTLDynamicLibrary>, *mut NSError)>,
);

impl MTLNewDynamicLibraryCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTLDynamicLibrary>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(move |library_ptr: *mut ProtocolObject<dyn MTLDynamicLibrary>, error: *mut NSError| {
            let library = unsafe { Retained::from_raw(library_ptr) };
            handler(library, error);
        }))
    }

    pub fn copy(ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTLDynamicLibrary>, *mut NSError)>) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTLNewDynamicLibraryCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTLDynamicLibrary>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct MTLNewComputePipelineStateCompletionHandler(
    RcBlock<dyn Fn(*mut ProtocolObject<dyn MTLComputePipelineState>, *mut NSError)>,
);

impl MTLNewComputePipelineStateCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTLComputePipelineState>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(
            move |pipeline_ptr: *mut ProtocolObject<dyn MTLComputePipelineState>, error: *mut NSError| {
                let pipeline = unsafe { Retained::from_raw(pipeline_ptr) };
                handler(pipeline, error);
            },
        ))
    }

    pub fn copy(ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTLComputePipelineState>, *mut NSError)>) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTLNewComputePipelineStateCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTLComputePipelineState>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct MTLNewRenderPipelineStateCompletionHandler(
    RcBlock<dyn Fn(*mut ProtocolObject<dyn MTLRenderPipelineState>, *mut NSError)>,
);

impl MTLNewRenderPipelineStateCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTLRenderPipelineState>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(move |pipeline_ptr: *mut ProtocolObject<dyn MTLRenderPipelineState>, error: *mut NSError| {
            let pipeline = unsafe { Retained::from_raw(pipeline_ptr) };
            handler(pipeline, error);
        }))
    }

    pub fn copy(ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTLRenderPipelineState>, *mut NSError)>) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTLNewRenderPipelineStateCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTLRenderPipelineState>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

extern_class!(
    /// Groups together properties for creating a compiler context.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/metal/mtl4compilerdescriptor?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct MTL4CompilerDescriptor;
);

extern_conformance!(
    unsafe impl NSCopying for MTL4CompilerDescriptor {}
);

unsafe impl CopyingHelper for MTL4CompilerDescriptor {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for MTL4CompilerDescriptor {}
);

impl MTL4CompilerDescriptor {
    extern_methods!(
        /// Assigns a pipeline data set serializer into which this compiler stores data for all pipelines it creates.
        #[unsafe(method(pipelineDataSetSerializer))]
        #[unsafe(method_family = none)]
        pub fn pipeline_data_set_serializer(
            &self
        ) -> Option<Retained<ProtocolObject<dyn MTL4PipelineDataSetSerializer>>>;

        /// Setter for [`pipelineDataSetSerializer`][Self::pipelineDataSetSerializer].
        #[unsafe(method(setPipelineDataSetSerializer:))]
        #[unsafe(method_family = none)]
        pub fn set_pipeline_data_set_serializer(
            &self,
            pipeline_data_set_serializer: Option<&ProtocolObject<dyn MTL4PipelineDataSetSerializer>>,
        );
    );

    pub fn label(&self) -> Option<String> {
        let label: Option<Retained<NSString>> = unsafe { msg_send![self, label] };
        label.map(|label| label.to_string())
    }

    pub fn set_label(
        &self,
        label: Option<&str>,
    ) {
        unsafe {
            let _: () = msg_send![self, setLabel: label.map(NSString::from_str).as_deref()];
        }
    }
}

/// Methods declared on superclass `NSObject`.
impl MTL4CompilerDescriptor {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub fn new() -> Retained<Self>;
    );
}

extern_class!(
    /// [Apple's documentation](https://developer.apple.com/documentation/metal/mtl4compilertaskoptions?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct MTL4CompilerTaskOptions;
);

extern_conformance!(
    unsafe impl NSCopying for MTL4CompilerTaskOptions {}
);

unsafe impl CopyingHelper for MTL4CompilerTaskOptions {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for MTL4CompilerTaskOptions {}
);

impl MTL4CompilerTaskOptions {
    extern_methods!(
        /// Specifies a set of archive instances this compilation process uses for accelerating the build process.
        ///
        /// In case of a match in the archive, the compiler can skip one or more compilation tasks, speeding up the build process.
        #[unsafe(method(lookupArchives))]
        #[unsafe(method_family = none)]
        pub fn lookup_archives(&self) -> Option<Retained<NSArray<ProtocolObject<dyn MTL4Archive>>>>;

        /// Setter for [`lookupArchives`][Self::lookupArchives].
        ///
        /// This is [copied][objc2_foundation::NSCopying::copy] when set.
        #[unsafe(method(setLookupArchives:))]
        #[unsafe(method_family = none)]
        pub fn set_lookup_archives(
            &self,
            lookup_archives: Option<&NSArray<ProtocolObject<dyn MTL4Archive>>>,
        );
    );
}

/// Methods declared on superclass `NSObject`.
impl MTL4CompilerTaskOptions {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub fn new() -> Retained<Self>;
    );
}

/// Callback invoked when compilation of a binary function completes.
pub struct MTL4NewBinaryFunctionCompletionHandler(
    RcBlock<dyn Fn(*mut ProtocolObject<dyn MTL4BinaryFunction>, *mut NSError)>,
);

impl MTL4NewBinaryFunctionCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTL4BinaryFunction>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(move |binary_fn_ptr: *mut ProtocolObject<dyn MTL4BinaryFunction>, error: *mut NSError| {
            let binary_fn = unsafe { Retained::from_raw(binary_fn_ptr) };
            handler(binary_fn, error);
        }))
    }

    pub fn copy(ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTL4BinaryFunction>, *mut NSError)>) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTL4NewBinaryFunctionCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTL4BinaryFunction>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// Callback invoked when compilation of a machine-learning pipeline state completes.
pub struct MTL4NewMachineLearningPipelineStateCompletionHandler(
    RcBlock<dyn Fn(*mut ProtocolObject<dyn MTL4MachineLearningPipelineState>, *mut NSError)>,
);

impl MTL4NewMachineLearningPipelineStateCompletionHandler {
    pub fn new<F>(handler: F) -> Self
    where
        F: Fn(Option<Retained<ProtocolObject<dyn MTL4MachineLearningPipelineState>>>, *mut NSError) + 'static,
    {
        Self(RcBlock::new(
            move |pipeline_ptr: *mut ProtocolObject<dyn MTL4MachineLearningPipelineState>, error: *mut NSError| {
                let pipeline = unsafe { Retained::from_raw(pipeline_ptr) };
                handler(pipeline, error);
            },
        ))
    }

    pub fn copy(
        ptr: *mut DynBlock<dyn Fn(*mut ProtocolObject<dyn MTL4MachineLearningPipelineState>, *mut NSError)>
    ) -> Self {
        Self(unsafe { RcBlock::copy(ptr) }.unwrap())
    }
}

impl Deref for MTL4NewMachineLearningPipelineStateCompletionHandler {
    type Target = Block<dyn Fn(*mut ProtocolObject<dyn MTL4MachineLearningPipelineState>, *mut NSError)>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

extern_protocol!(
    /// A abstraction for a pipeline state and shader function compiler.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/metal/mtl4compiler?language=objc)
    pub unsafe trait MTL4Compiler: NSObjectProtocol + Send + Sync {
        /// Returns the device that this compiler belongs to.
        #[unsafe(method(device))]
        #[unsafe(method_family = none)]
        fn device(&self) -> Retained<ProtocolObject<dyn MTLDevice>>;

        /// Returns the optional label you specify at creation time.
        #[unsafe(method(label))]
        #[unsafe(method_family = none)]
        fn label(&self) -> Option<Retained<NSString>>;

        /// Returns the pipeline data set serializer into which this compiler stores data for all pipelines it creates.
        #[unsafe(method(pipelineDataSetSerializer))]
        #[unsafe(method_family = none)]
        fn pipeline_data_set_serializer(&self) -> Option<Retained<ProtocolObject<dyn MTL4PipelineDataSetSerializer>>>;

        /// Creates a new Metal library synchronously.
        ///
        /// - Parameters:
        /// - descriptor: A description of the library to create.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: a Metal library instance upon success, `nil` otherwise.
        #[unsafe(method(newLibraryWithDescriptor:error:_))]
        #[unsafe(method_family = new)]
        fn new_library_with_descriptor_error(
            &self,
            descriptor: &MTL4LibraryDescriptor,
        ) -> Result<Retained<ProtocolObject<dyn MTLLibrary>>, Retained<NSError>>;

        /// Creates a new dynamic library from a library containing Metal IR code synchronously.
        ///
        /// - Parameters:
        /// - library: A library from which this compiler creates the new a dynamic library
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A new dynamic Metal library upon success, `nil` otherwise.
        #[unsafe(method(newDynamicLibrary:error:_))]
        #[unsafe(method_family = new)]
        fn new_dynamic_library_error(
            &self,
            library: &ProtocolObject<dyn MTLLibrary>,
        ) -> Result<Retained<ProtocolObject<dyn MTLDynamicLibrary>>, Retained<NSError>>;

        /// Creates a new compute pipeline state object synchronously.
        ///
        /// - Parameters:
        /// - descriptor: A compute pipeline state descriptor describing the pipeline this compiler creates.
        /// - compilerTaskOptions: A description of the compilation process itself, providing parameters that
        /// influence execution of the compilation process.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A new compute pipeline state object upon success, `nil` otherwise.
        #[unsafe(method(newComputePipelineStateWithDescriptor:compilerTaskOptions:error:_))]
        #[unsafe(method_family = new)]
        fn new_compute_pipeline_state_with_descriptor_compiler_task_options_error(
            &self,
            descriptor: &MTL4ComputePipelineDescriptor,
            compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        ) -> Result<Retained<ProtocolObject<dyn MTLComputePipelineState>>, Retained<NSError>>;

        /// Creates a new compute pipeline state synchronously.
        ///
        /// - Parameters:
        /// - descriptor: A compute pipeline state descriptor describing the pipeline this compiler creates.
        /// - dynamicLinkingDescriptor: An optional parameter that provides additional configuration for linking the pipeline state object.
        /// - compilerTaskOptions: A description of the compilation process itself, providing parameters that
        /// influence execution of the compilation process.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A new compute pipeline state object upon success, `nil` otherwise.
        #[unsafe(method(newComputePipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:error:_))]
        #[unsafe(method_family = new)]
        fn new_compute_pipeline_state_with_descriptor_dynamic_linking_descriptor_compiler_task_options_error(
            &self,
            descriptor: &MTL4ComputePipelineDescriptor,
            dynamic_linking_descriptor: Option<&MTL4PipelineStageDynamicLinkingDescriptor>,
            compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        ) -> Result<Retained<ProtocolObject<dyn MTLComputePipelineState>>, Retained<NSError>>;

        /// Creates a new render pipeline state synchronously.
        ///
        /// Use this method to build any render pipeline type, including render, tile, and mesh render pipeline states.
        /// The type of the descriptor you pass indicates the pipeline type this method builds.
        ///
        /// Passing in a compute pipeline descriptor to the `descriptor` parameter produces an error.
        ///
        /// - Parameters:
        /// - descriptor: A render, tile, or mesh pipeline state descriptor that describes the pipeline to create.
        /// - compilerTaskOptions: A description of the compilation process itself, providing parameters that
        /// influence execution of the compilation process.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A new render pipeline state object upon success, `nil` otherwise.
        #[unsafe(method(newRenderPipelineStateWithDescriptor:compilerTaskOptions:error:_))]
        #[unsafe(method_family = new)]
        fn new_render_pipeline_state_with_descriptor_compiler_task_options_error(
            &self,
            descriptor: &MTL4PipelineDescriptor,
            compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        ) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, Retained<NSError>>;

        /// Creates a new render pipeline state synchronously.
        ///
        /// Use this method to build any render pipeline type, including render, tile, and mesh render pipeline states.
        /// The type of the descriptor you pass indicates the pipeline type this method builds.
        ///
        /// Passing in a compute pipeline descriptor to the `descriptor` parameter produces an error.
        ///
        /// - Parameters:
        /// - descriptor: A render, tile, or mesh pipeline state descriptor that describes the pipeline to create.
        /// - dynamicLinkingDescriptor: An optional parameter that provides additional configuration for linking the pipeline state object.
        /// - compilerTaskOptions: A description of the compilation process itself, providing parameters that
        /// influence execution of the compilation process.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A new render pipeline state object upon success, `nil` otherwise.
        #[unsafe(method(newRenderPipelineStateWithDescriptor:dynamicLinkingDescriptor:compilerTaskOptions:error:_))]
        #[unsafe(method_family = new)]
        fn new_render_pipeline_state_with_descriptor_dynamic_linking_descriptor_compiler_task_options_error(
            &self,
            descriptor: &MTL4PipelineDescriptor,
            dynamic_linking_descriptor: Option<&MTL4RenderPipelineDynamicLinkingDescriptor>,
            compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        ) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, Retained<NSError>>;

        /// Creates a new render pipeline state from another, previously unspecialized, pipeline state.
        ///
        /// Metal specializes the pipeline state with new state values the descriptor provides, observing the following rules:
        /// * The compiler only updates properties that were originally specified as *unspecialized*. It doesn't modify other
        /// already-specialized properties
        /// * The compiler sets to their default behavior any unspecialized properties that your passed-in descriptor doesn't specialize
        ///
        /// Additionally, there are some cases where the Metal can't specialize a pipeline:
        /// * If the original pipeline state object doesn't have any unspecialized properties
        /// * You can't re-specialize a previously specialized pipeline state object
        ///
        /// - Parameters:
        /// - descriptor: A render pipeline state descriptor or any type: default, tile, or mesh render pipeline descriptor.
        /// - pipeline: A render pipeline state containing unspecialized substate.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: a fully-specialized pipeline state object.
        #[unsafe(method(newRenderPipelineStateBySpecializationWithDescriptor:pipeline:error:_))]
        #[unsafe(method_family = new)]
        fn new_render_pipeline_state_by_specialization_with_descriptor_pipeline_error(
            &self,
            descriptor: &MTL4PipelineDescriptor,
            pipeline: &ProtocolObject<dyn MTLRenderPipelineState>,
        ) -> Result<Retained<ProtocolObject<dyn MTLRenderPipelineState>>, Retained<NSError>>;

        /// Creates a new binary visible or intersection function synchronously.
        ///
        /// - Parameters:
        /// - descriptor: A binary function descriptor to use for creating the binary function.
        /// - compilerTaskOptions: A descriptor of the compilation itself, providing parameters that
        /// influence execution of the compilation process.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: a new binary function upon success, `nil` otherwise.
        #[unsafe(method(newBinaryFunctionWithDescriptor:compilerTaskOptions:error:_))]
        #[unsafe(method_family = new)]
        fn new_binary_function_with_descriptor_compiler_task_options_error(
            &self,
            descriptor: &MTL4BinaryFunctionDescriptor,
            compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        ) -> Result<Retained<ProtocolObject<dyn MTL4BinaryFunction>>, Retained<NSError>>;

        /// Creates a new ML pipeline state with descriptor.
        ///
        /// - Parameters:
        /// - descriptor: A machine learning pipeline state descriptor to use for creating the new pipeline state.
        /// - error: An optional parameter into which Metal stores information in case of an error.
        ///
        /// - Returns: A machine learning pipeline state if operation is successful, otherwise `nil`.
        #[unsafe(method(newMachineLearningPipelineStateWithDescriptor:error:_))]
        #[unsafe(method_family = new)]
        fn new_machine_learning_pipeline_state_with_descriptor_error(
            &self,
            descriptor: &MTL4MachineLearningPipelineDescriptor,
        ) -> Result<Retained<ProtocolObject<dyn MTL4MachineLearningPipelineState>>, Retained<NSError>>;
    }
);

pub trait MTL4CompilerExt: MTL4Compiler + Message {
    fn new_dynamic_library_with_path(
        &self,
        path: &Path,
    ) -> Result<Retained<ProtocolObject<dyn MTLDynamicLibrary>>, Retained<NSError>>
    where
        Self: Sized,
    {
        let url = NSURL::from_file_path(path).expect("path must be a valid file URL path");
        let mut error: *mut NSError = std::ptr::null_mut();
        let library: Option<Retained<ProtocolObject<dyn MTLDynamicLibrary>>> =
            unsafe { msg_send![self, newDynamicLibraryWithURL: &*url, error: &mut error] };
        match (library, unsafe { Retained::retain(error) }) {
            (Some(library), None) => Ok(library),
            (None, Some(error)) => Err(error),
            (library, error) => unreachable!("Metal API contract violated: {library:?} {error:?}"),
        }
    }

    fn new_library_with_descriptor_completion_handler(
        &self,
        descriptor: &MTL4LibraryDescriptor,
        completion_handler: MTLNewLibraryCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newLibraryWithDescriptor: descriptor,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_dynamic_library_completion_handler(
        &self,
        library: &ProtocolObject<dyn MTLLibrary>,
        completion_handler: MTLNewDynamicLibraryCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newDynamicLibrary: library,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_dynamic_library_with_path_completion_handler(
        &self,
        path: &Path,
        completion_handler: MTLNewDynamicLibraryCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        let url = NSURL::from_file_path(path).expect("path must be a valid file URL path");
        unsafe {
            msg_send![
                self,
                newDynamicLibraryWithURL: &*url,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_compute_pipeline_state_with_descriptor_compiler_task_options_completion_handler(
        &self,
        descriptor: &MTL4ComputePipelineDescriptor,
        compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        completion_handler: MTLNewComputePipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newComputePipelineStateWithDescriptor: descriptor,
                compilerTaskOptions: compiler_task_options,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_compute_pipeline_state_with_descriptor_dynamic_linking_descriptor_compiler_task_options_completion_handler(
        &self,
        descriptor: &MTL4ComputePipelineDescriptor,
        dynamic_linking_descriptor: Option<&MTL4PipelineStageDynamicLinkingDescriptor>,
        compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        completion_handler: MTLNewComputePipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newComputePipelineStateWithDescriptor: descriptor,
                dynamicLinkingDescriptor: dynamic_linking_descriptor,
                compilerTaskOptions: compiler_task_options,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_render_pipeline_state_with_descriptor_compiler_task_options_completion_handler(
        &self,
        descriptor: &MTL4PipelineDescriptor,
        compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        completion_handler: MTLNewRenderPipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newRenderPipelineStateWithDescriptor: descriptor,
                compilerTaskOptions: compiler_task_options,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_render_pipeline_state_with_descriptor_dynamic_linking_descriptor_compiler_task_options_completion_handler(
        &self,
        descriptor: &MTL4PipelineDescriptor,
        dynamic_linking_descriptor: Option<&MTL4RenderPipelineDynamicLinkingDescriptor>,
        compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        completion_handler: MTLNewRenderPipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newRenderPipelineStateWithDescriptor: descriptor,
                dynamicLinkingDescriptor: dynamic_linking_descriptor,
                compilerTaskOptions: compiler_task_options,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_render_pipeline_state_by_specialization_with_descriptor_pipeline_completion_handler(
        &self,
        descriptor: &MTL4PipelineDescriptor,
        pipeline: &ProtocolObject<dyn MTLRenderPipelineState>,
        completion_handler: MTLNewRenderPipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newRenderPipelineStateBySpecializationWithDescriptor: descriptor,
                pipeline: pipeline,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_binary_function_with_descriptor_compiler_task_options_completion_handler(
        &self,
        descriptor: &MTL4BinaryFunctionDescriptor,
        compiler_task_options: Option<&MTL4CompilerTaskOptions>,
        completion_handler: MTL4NewBinaryFunctionCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newBinaryFunctionWithDescriptor: descriptor,
                compilerTaskOptions: compiler_task_options,
                completionHandler: &*completion_handler
            ]
        }
    }

    fn new_machine_learning_pipeline_state_with_descriptor_completion_handler(
        &self,
        descriptor: &MTL4MachineLearningPipelineDescriptor,
        completion_handler: MTL4NewMachineLearningPipelineStateCompletionHandler,
    ) -> Retained<ProtocolObject<dyn MTL4CompilerTask>>
    where
        Self: Sized,
    {
        unsafe {
            msg_send![
                self,
                newMachineLearningPipelineStateWithDescriptor: descriptor,
                completionHandler: &*completion_handler
            ]
        }
    }
}

impl<T: MTL4Compiler + Message> MTL4CompilerExt for T {}
