{{ self::swift_doc_block(callback.doc, "") }}/// > Warning: Callback methods may be invoked from any thread and the thread is not
/// > guaranteed to be the same between calls. Your implementation must be thread-safe.
/// > Dispatch to the main thread if updating UI.
public protocol {{ callback.protocol_name }}: AnyObject {
{%- for method in callback.methods %}
{{ self::swift_doc_block(method.doc, "    ") }}    func {{ method.swift_name }}({% for param in method.params %}{{ param.label }}: {{ param.swift_type }}{% if !loop.last %}, {% endif %}{% endfor %}){% if method.is_async %} async{% endif %}{% if method.throws() %} throws{% endif %}{% if let Some(ret) = method.return_type() %} -> {{ ret }}{% endif %}
{%- endfor %}
}

private class {{ callback.wrapper_class }} {
    let impl_: {{ callback.protocol_name }}
    init(_ impl_: {{ callback.protocol_name }}) { self.impl_ = impl_ }
}
{% for method in callback.methods %}
{%- if method.is_async %}

private func _{{ callback.vtable_var }}_{{ method.swift_name }}(_ wrapper: {{ callback.wrapper_class }}, {% for param in method.params %}_ {{ param.label }}: {{ param.swift_type }}, {% endfor %}_ callback: {{ method.async_callback_c_type() }}, _ callbackData: UInt64) {
    Task {
{%- if method.throws() %}
        do {
            {% if method.has_return() %}let result = {% endif %}try await wrapper.impl_.{{ method.swift_name }}({% for param in method.params %}{{ param.label }}: {{ param.label }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- if method.has_return() %}
{%- if method.wire_encoded_return() %}
{%- if let Some(encoded) = method.wire_return_encode() %}
            {{ encoded }}
{%- endif %}
            boltffiInvokeWireCallback(callback, callbackData, encoded, FfiStatus(code: 0))
{%- else %}
            callback?(callbackData, result, FfiStatus(code: 0))
{%- endif %}
{%- else %}
            callback?(callbackData, FfiStatus(code: 0))
{%- endif %}
        } catch {
{%- if let Some(err_type) = method.err_type() %}
{%- if let Some(err_encoded) = method.wire_err_encode() %}
            guard let error = error as? {{ err_type }} else {
                var errWriter = WireWriter()
                errWriter.writeString(String(describing: error))
                boltffiInvokeWireCallback(callback, callbackData, errWriter.finalize(), FfiStatus(code: 1))
                return
            }
            {{ err_encoded }}
            boltffiInvokeWireCallback(callback, callbackData, encoded, FfiStatus(code: 0))
{%- else %}
            var errWriter = WireWriter()
            errWriter.writeString(String(describing: error))
            boltffiInvokeWireCallback(callback, callbackData, errWriter.finalize(), FfiStatus(code: 1))
{%- endif %}
{%- else %}
            var errWriter = WireWriter()
            errWriter.writeString(String(describing: error))
            boltffiInvokeWireCallback(callback, callbackData, errWriter.finalize(), FfiStatus(code: 1))
{%- endif %}
        }
{%- else %}
        {% if method.has_return() %}let result = {% endif %}await wrapper.impl_.{{ method.swift_name }}({% for param in method.params %}{{ param.label }}: {{ param.label }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- if method.has_return() %}
{%- if method.wire_encoded_return() %}
{%- if let Some(encoded) = method.wire_return_encode() %}
        {{ encoded }}
{%- endif %}
        boltffiInvokeWireCallback(callback, callbackData, encoded, FfiStatus(code: 0))
{%- else %}
        callback?(callbackData, result, FfiStatus(code: 0))
{%- endif %}
{%- else %}
        callback?(callbackData, FfiStatus(code: 0))
{%- endif %}
{%- endif %}
    }
}
{%- endif %}
{%- endfor %}

private var {{ callback.vtable_var }}: {{ callback.vtable_type }} = {
    {{ callback.vtable_type }}(
        free: { handle in
            guard handle != 0 else { return }
            Unmanaged<{{ callback.wrapper_class }}>.fromOpaque(UnsafeRawPointer(bitPattern: UInt(handle))!).release()
        },
        clone: { handle in
            guard handle != 0 else { return 0 }
            let wrapper = Unmanaged<{{ callback.wrapper_class }}>.fromOpaque(UnsafeRawPointer(bitPattern: UInt(handle))!)
            _ = wrapper.retain()
            return handle
        }{% for method in callback.methods %},
        {{ method.ffi_name }}: { handle{% for param in method.params %}{% for arg in param.ffi_args %}, {{ arg }}{% endfor %}{% endfor %}{% if method.has_out_param %}{% if method.wire_encoded_return() %}, outPtr, outLen{% else %}, outPtr{% endif %}{% endif %}{% if !method.is_async %}, statusPtr{% endif %}{% if method.is_async %}, callback, callbackData{% endif %} in
            guard handle != 0 else { {% if !method.is_async %}statusPtr?.pointee = FfiStatus(code: 1); {% endif %}return }
            let wrapper = Unmanaged<{{ callback.wrapper_class }}>.fromOpaque(UnsafeRawPointer(bitPattern: UInt(handle))!).takeUnretainedValue()
{%- for param in method.params %}
{%- match param.decode_prelude %}{% when Some with (prelude) %}
            {{ prelude }}
{%- when None %}{% endmatch %}
{%- endfor %}
{%- if method.is_async %}
            _{{ callback.vtable_var }}_{{ method.swift_name }}(wrapper, {% for param in method.params %}{{ param.call_arg }}, {% endfor %}callback, callbackData)
{%- else %}
            {% if method.has_return() %}let result = {% endif %}wrapper.impl_.{{ method.swift_name }}({% for param in method.params %}{{ param.label }}: {{ param.call_arg }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- if method.has_out_param %}
{%- if method.wire_encoded_return() %}
{%- if let Some(encoded) = method.wire_return_encode() %}
            {{ encoded }}
{%- endif %}
            _ = encoded.withUnsafeBytes { bytes in
                memcpy(outPtr!, bytes.baseAddress!, bytes.count)
            }
            outLen?.pointee = UInt(encoded.count)
{%- else %}
            outPtr?.pointee = result
{%- endif %}
{%- endif %}
            statusPtr?.pointee = FfiStatus(code: 0)
{%- endif %}
        }{% endfor %}
    )
}()

enum {{ callback.bridge_name }} {
    private static let _register: Void = {
        {{ callback.register_fn }}(&{{ callback.vtable_var }})
    }()

    static func create(_ impl: {{ callback.protocol_name }}) -> BoltFFICallbackHandle {
        _ = _register
        let wrapper = {{ callback.wrapper_class }}(impl)
        let handle = UInt64(UInt(bitPattern: Unmanaged.passRetained(wrapper).toOpaque()))
        return {{ callback.create_fn }}(handle)
    }
}
