final class Native {
    static {
        String preferredLibrary = "{{ module.lib_name }}_jni";
        String fallbackLibrary = "{{ module.lib_name }}";
        String vmName = System.getProperty("java.vm.name");
        if (vmName == null) vmName = "";
        boolean isAndroidRuntime =
            vmName.toLowerCase().contains("dalvik") ||
            vmName.toLowerCase().contains("art");
        if (isAndroidRuntime) {
            System.loadLibrary(fallbackLibrary);
        } else {
            System.loadLibrary(preferredLibrary);
        }
    }

    private Native() {}

    @SuppressWarnings("unused")
    static void boltffiFutureContinuationCallback(long handle, byte pollResult) {
{%- if module.has_async() || module.has_streams() %}
        BoltFFIContinuationMap.complete(handle, pollResult);
{%- endif %}
    }

    static native void {{ module.prefix }}_free_string(long ptr);
    static native byte[] {{ module.prefix }}_last_error_message();
{%- for func in module.functions %}
{%- if func.is_async() %}
{%- match func.async_call %}
{%- when Some with (ac) %}
    static native long {{ func.ffi_name }}({% for param in func.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
    static native void {{ ac.poll }}(long future, long contHandle);
    static native {{ func.native_return_type() }} {{ ac.complete }}(long future);
    static native void {{ ac.cancel }}(long future);
    static native void {{ ac.free }}(long future);
{%- when None %}
{%- endmatch %}
{%- else %}
    static native {{ func.native_return_type() }} {{ func.ffi_name }}({% for param in func.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endif %}
{%- endfor %}
{%- for record in module.records %}
{%- for ctor in record.constructors %}
    static native {{ ctor.return_plan.native_return_type }} {{ ctor.ffi_name }}({% for param in ctor.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in record.methods %}
    static native {{ method.return_plan.native_return_type }} {{ method.ffi_name }}({% for param in method.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- endfor %}
{%- for enumeration in module.enums %}
{%- for ctor in enumeration.constructors %}
    static native {{ ctor.return_plan.native_return_type }} {{ ctor.ffi_name }}({% for param in ctor.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in enumeration.methods %}
    static native {{ method.return_plan.native_return_type }} {{ method.ffi_name }}({% for param in method.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- endfor %}
{%- for class in module.classes %}
    static native void {{ class.ffi_free }}(long handle);
{%- for ctor in class.constructors %}
    static native long {{ ctor.ffi_name }}({% for param in ctor.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in class.methods %}
{%- if method.is_async() %}
{%- match method.async_call %}
{%- when Some with (ac) %}
{%- if method.is_static %}
    static native long {{ method.ffi_name }}({% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
    static native void {{ ac.poll }}(long future, long contHandle);
    static native {{ method.native_return_type() }} {{ ac.complete }}(long future);
    static native void {{ ac.cancel }}(long future);
    static native void {{ ac.free }}(long future);
{%- else %}
    static native long {{ method.ffi_name }}(long handle{% if !method.params.is_empty() %}, {% endif %}{% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
    static native void {{ ac.poll }}(long handle, long future, long contHandle);
    static native {{ method.native_return_type() }} {{ ac.complete }}(long handle, long future);
    static native void {{ ac.cancel }}(long handle, long future);
    static native void {{ ac.free }}(long handle, long future);
{%- endif %}
{%- when None %}
{%- endmatch %}
{%- else %}
{%- if method.is_static %}
    static native {{ method.native_return_type() }} {{ method.ffi_name }}({% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- else %}
    static native {{ method.native_return_type() }} {{ method.ffi_name }}(long handle{% if !method.params.is_empty() %}, {% endif %}{% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endif %}
{%- endif %}
{%- endfor %}
{%- for stream in class.streams %}
    static native long {{ stream.subscribe }}(long handle);
    static native void {{ stream.poll }}(long subscription, long contHandle);
    static native byte[] {{ stream.pop_batch }}(long subscription, long maxCount);
    static native int {{ stream.wait }}(long subscription, int timeout);
    static native void {{ stream.unsubscribe }}(long subscription);
    static native void {{ stream.free }}(long subscription);
{%- endfor %}
{%- endfor %}
{%- for invoker in module.async_callback_invokers %}
    static native void {{ invoker.success_name() }}(long callbackPtr, long callbackData{% if invoker.has_result() %}, {{ invoker.result_jni_type() }} result{% endif %});
    static native void {{ invoker.failure_name() }}(long callbackPtr, long callbackData);
{%- endfor %}
{%- for closure in module.closures %}
{%- if closure.supports_proxy_wrap %}
    static native long {{ closure.proxy_clone_native_name() }}(long handle);
    static native void {{ closure.proxy_release_native_name() }}(long handle);
    static native {{ closure.proxy.return_plan.native_return_type }} {{ closure.proxy.native_name }}(long handle{% for param in closure.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %});
{%- endif %}
{%- endfor %}
{%- for callback in module.callbacks %}
    static native long {{ callback.proxy_clone_native_name() }}(long handle);
    static native void {{ callback.proxy_release_native_name() }}(long handle);
{%- for method in callback.sync_methods %}
    static native {{ method.proxy.return_plan.native_return_type }} {{ method.proxy.native_name }}(long handle{% for param in method.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %});
{%- endfor %}
{%- for method in callback.async_methods %}
    static native void {{ method.proxy.native_name }}(long handle{% for param in method.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %}, long callbackData);
{%- endfor %}
{%- endfor %}
}
