#include <emscripten.h>
#include <emscripten/html5.h>

#include <emscripten/val.h>

#include "fugg.h"

#define TRACE_CALLBACK_CALLS 0

u32 original_resize_callback = 0;
/* import: 'a' 'Qa' */
// "Qa": "_emscripten_set_resize_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_resize_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    // From https://emscripten.org/docs/api_reference/html5.h.html
    // For the resize callback, pass in target = EMSCRIPTEN_EVENT_TARGET_WINDOW to get resize events from the Window object.
    const auto& target = 
        reinterpret_cast<void*>(a) <= EMSCRIPTEN_EVENT_TARGET_SCREEN ? 
        reinterpret_cast<const char*>(a) : fugg::ModulePointer<const char> { a };

    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_resize_callback_on_thread("
            //   << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_ui_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_resize_callback = callback_;
    auto hook = [](int eventType, const EmscriptenUiEvent* event, void* userData) {
        assert(original_resize_callback);
        
        const auto& callback = fugg::function_ref<em_ui_callback_func>(original_resize_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenUiEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_resize_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_devicemotion_callback = 0;
/* import: 'a' 'sa' */
// "sa": "_emscripten_set_devicemotion_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_devicemotion_callback_on_thread) = [](u32 a, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& user_data = fugg::ModulePointer<void> { a };

#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_devicemotion_callback_on_thread(" 
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_devicemotion_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_devicemotion_callback = callback_;
    auto hook = [](int eventType, const EmscriptenDeviceMotionEvent* event, void* userData) {
        assert(original_devicemotion_callback);
        
        const auto& callback = fugg::function_ref<em_devicemotion_callback_func>(original_devicemotion_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenDeviceMotionEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_devicemotion_callback_on_thread(user_data, use_capture, hook, target_thread)
    );
};
u32 original_beforeunload_callback = 0;
/* import: 'a' 'va' */
// "va": "_emscripten_set_beforeunload_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_beforeunload_callback_on_thread) = [](u32 a, u32 callback_, u32 target_thread) {
    const auto& user_data = fugg::ModulePointer<void> { a };

#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_beforeunload_callback_on_thread("
              << user_data << ", "
              << emscripten_rt::fn<em_beforeunload_callback>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_beforeunload_callback = callback_;
    auto hook = [](int eventType, const void* event, void* userData) {
        assert(original_beforeunload_callback);
        
        const auto& callback = fugg::function_ref<em_beforeunload_callback>(original_beforeunload_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const void> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_beforeunload_callback_on_thread(user_data, hook, target_thread)
    );
};
u32 original_focusout_callback = 0;
/* import: 'a' 'Ca' */
/* _emscripten_set_focusout_callback_on_thread */
WASM_IMPORT_IMPL(env, _emscripten_set_focusout_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_focusout_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_focus_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_focusout_callback = callback_;
    auto hook = [](int eventType, const EmscriptenFocusEvent* event, void* userData) {
        assert(original_focusout_callback);
        
        const auto& callback = fugg::function_ref<em_focus_callback_func>(original_focusout_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenFocusEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_focusout_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};

extern void on_keyup_hook(int, const EmscriptenKeyboardEvent*, void*);

u32 original_keyup_callback = 0;
/* import: 'a' 'Da' */
/* _emscripten_set_keyup_callback_on_thread */
WASM_IMPORT_IMPL(env, _emscripten_set_keyup_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_keyup_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_key_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_keyup_callback = callback_;
    auto hook = [](int eventType, const EmscriptenKeyboardEvent* event, void* userData) {
        // Call hook
        on_keyup_hook(eventType, event, userData);

        assert(original_keyup_callback);
        
        const auto& callback = fugg::function_ref<em_key_callback_func>(original_keyup_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenKeyboardEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_keyup_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_keydown_callback = 0;
/* import: 'a' 'Ea' */
/* _emscripten_set_keydown_callback_on_thread */
WASM_IMPORT_IMPL(env, _emscripten_set_keydown_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_keydown_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_key_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_keydown_callback = callback_;
    auto hook = [](int eventType, const EmscriptenKeyboardEvent* event, void* userData) {
        assert(original_keydown_callback);
        
        const auto& callback = fugg::function_ref<em_key_callback_func>(original_keydown_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenKeyboardEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_keydown_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};

u32 original_wheel_callback = 0;
/* import: 'a' 'Fa' */
/* _emscripten_set_wheel_callback_on_thread */
WASM_IMPORT_IMPL(env, _emscripten_set_wheel_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_wheel_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_wheel_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_wheel_callback = callback_;
    auto hook = [](int eventType, const EmscriptenWheelEvent* event, void* userData) {
        assert(original_wheel_callback);
        
        const auto& callback = fugg::function_ref<em_wheel_callback_func>(original_wheel_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenWheelEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_wheel_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};

u32 original_mouseleave_callback = 0;
/* import: 'a' 'Ga' */
// "Ga": "_emscripten_set_mouseleave_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_mouseleave_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_mouseleave_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_mouse_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_mouseleave_callback = callback_;
    auto hook = [](int eventType, const EmscriptenMouseEvent* event, void* userData) {
        assert(original_mouseleave_callback);

        const auto& callback = fugg::function_ref<em_mouse_callback_func>(original_mouseleave_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenMouseEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_mouseleave_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_mousemove_callback = 0;
/* import: 'a' 'Ha' */
// "Ha": "_emscripten_set_mousemove_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_mousemove_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };

#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_mousemove_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_mouse_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_mousemove_callback = callback_;
    auto hook = [](int eventType, const EmscriptenMouseEvent* event, void* userData) {
        assert(original_mousemove_callback);

        const auto& callback = fugg::function_ref<em_mouse_callback_func>(original_mousemove_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenMouseEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_mousemove_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_mouseup_callback = 0;
/* import: 'a' 'Ia' */
// "Ia": "_emscripten_set_mouseup_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_mouseup_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_mouseup_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_mouse_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_mouseup_callback = callback_;
    auto hook = [](int eventType, const EmscriptenMouseEvent* event, void* userData) {
        assert(original_mouseup_callback);

        const auto& callback = fugg::function_ref<em_mouse_callback_func>(original_mouseup_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenMouseEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_mouseup_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 orignal_mousedown_callback = 0;
/* import: 'a' 'Ja' */
// "Ja": "_emscripten_set_mousedown_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_mousedown_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_mousedown_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_mouse_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    orignal_mousedown_callback = callback_;
    auto hook = [](int eventType, const EmscriptenMouseEvent* event, void* userData) {
        assert(orignal_mousedown_callback);

        const auto& callback = fugg::function_ref<em_mouse_callback_func>(orignal_mousedown_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenMouseEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_mousedown_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_touchcancel_callback = 0;
/* import: 'a' 'Ka' */
// "Ka": "_emscripten_set_touchcancel_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_touchcancel_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {    
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_touchcancel_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_touch_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_touchcancel_callback = callback_;
    auto hook = [](int eventType, const EmscriptenTouchEvent* event, void* userData) {
        assert(original_touchcancel_callback);

        const auto& callback = fugg::function_ref<em_touch_callback_func>(original_touchcancel_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenTouchEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_touchcancel_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_touchmove_callback = 0;
/* import: 'a' 'La' */
// "La": "_emscripten_set_touchmove_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_touchmove_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_touchmove_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_touch_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_touchmove_callback = callback_;
    auto hook = [](int eventType, const EmscriptenTouchEvent* event, void* userData) {
        assert(original_touchmove_callback);

        const auto& callback = fugg::function_ref<em_touch_callback_func>(original_touchmove_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenTouchEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_touchmove_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_touchend_callback = 0;
/* import: 'a' 'Ma' */
// "Ma": "_emscripten_set_touchend_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_touchend_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_touchend_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_touch_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_touchend_callback = callback_;
    auto hook = [](int eventType, const EmscriptenTouchEvent* event, void* userData) {
        assert(original_touchend_callback);

        const auto& callback = fugg::function_ref<em_touch_callback_func>(original_touchend_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenTouchEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_touchend_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_touchstart_callback = 0;
/* import: 'a' 'Na' */
// "Na": "_emscripten_set_touchstart_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_touchstart_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_touchstart_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_touch_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_touchstart_callback = callback_;
    auto hook = [](int eventType, const EmscriptenTouchEvent* event, void* userData) {
        assert(original_touchstart_callback);

        const auto& callback = fugg::function_ref<em_touch_callback_func>(original_touchstart_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenTouchEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_touchstart_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_orientationchange_callback = 0;
/* import: 'a' 'Pa' */
// "Pa": "_emscripten_set_orientationchange_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_orientationchange_callback_on_thread) = [](u32 a, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& user_data = fugg::ModulePointer<void> { a };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_orientationchange_callback_on_thread("
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_orientationchange_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_orientationchange_callback = callback_;
    auto hook = [](int eventType, const EmscriptenOrientationChangeEvent* event, void* userData) {
        assert(original_orientationchange_callback);

        const auto& callback = fugg::function_ref<em_orientationchange_callback_func>(original_orientationchange_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenOrientationChangeEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_orientationchange_callback_on_thread(user_data, use_capture, hook, target_thread)
    );
};
u32 original_visibilitychange_callback = 0;
/* import: 'a' 'Ra' */
// "Ra": "_emscripten_set_visibilitychange_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_visibilitychange_callback_on_thread) = [](u32 a, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& user_data = fugg::ModulePointer<void> { a };

#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_visibilitychange_callback_on_thread("
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_visibilitychange_callback_func>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_visibilitychange_callback = callback_;
    auto hook = [](int eventType, const EmscriptenVisibilityChangeEvent* event, void* userData) {
        assert(original_visibilitychange_callback);

        const auto& callback = fugg::function_ref<em_visibilitychange_callback_func>(original_visibilitychange_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenVisibilityChangeEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_visibilitychange_callback_on_thread(user_data, use_capture, hook, target_thread)
    );
};
u32 original_webglcontextrestored_callback = 0;
/* import: 'a' 'Ba' */
/* _emscripten_set_webglcontextrestored_callback_on_thread */
WASM_IMPORT_IMPL(env, _emscripten_set_webglcontextrestored_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_webglcontextrestored_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_webgl_context_callback>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_webglcontextrestored_callback = callback_;
    auto hook = [](int eventType, const void* event, void* userData) {
        assert(original_webglcontextrestored_callback);
        
        const auto& callback = fugg::function_ref<em_webgl_context_callback>(original_webglcontextrestored_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const void> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_webglcontextrestored_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_webglcontextlost_callback = 0;
/* import: 'a' 'eb' */
// "eb": "_emscripten_set_webglcontextlost_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_webglcontextlost_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };
    
#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_webglcontextlost_callback_on_thread("
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_webgl_context_callback>(callback_) << ", "
              << target_thread << ")" << std::endl;
#endif

    original_webglcontextlost_callback = callback_;
    auto hook = [](int eventType, const void* event, void* userData) {
        assert(original_webglcontextlost_callback);

        const auto& callback = fugg::function_ref<em_webgl_context_callback>(original_webglcontextlost_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const void> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };

    return static_cast<u32>(
        emscripten_set_webglcontextlost_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};
u32 original_fullscreenchange_callback = 0;
/* import: 'a' 'yb' */
// "yb": "_emscripten_set_fullscreenchange_callback_on_thread",
WASM_IMPORT_IMPL(env, _emscripten_set_fullscreenchange_callback_on_thread) = [](u32 a, u32 b, u32 use_capture, u32 callback_, u32 target_thread) {
    const auto& target = fugg::ModulePointer<const char> { a };
    const auto& user_data = fugg::ModulePointer<void> { b };

#if TRACE_CALLBACK_CALLS
    std::cout << "_emscripten_set_fullscreenchange_callback_on_thread(" 
              << target << ", "
              << user_data << ", "
              << use_capture << ", "
              << emscripten_rt::fn<em_fullscreenchange_callback_func>(callback_) << ", "
              << target_thread << ", "
              << ")" << std::endl;
#endif

    original_fullscreenchange_callback = callback_;
    auto hook = [](int eventType, const EmscriptenFullscreenChangeEvent* event, void* userData) {
        assert(original_fullscreenchange_callback);

        const auto& callback = fugg::function_ref<em_fullscreenchange_callback_func>(original_fullscreenchange_callback);

        return callback(
            eventType, 
            fugg::RuntimePointer<const EmscriptenFullscreenChangeEvent> { event }, 
            fugg::RuntimePointer<void> { userData }
        );
    };


    return static_cast<u32>(
        emscripten_set_fullscreenchange_callback_on_thread(target, user_data, use_capture, hook, target_thread)
    );
};