#include <wasm-rt.h>
#include <client.h>
#include <fugg.h>

#include <emscripten/bind.h>

#include <iostream>

#include "embind.h"

void platform_text_edited_hook(u32 ptr, u32 selection_start, u32 selection_end) {
    // Emscripten doesn't understand sending pointers, so we have to receive an int.

    auto text = fugg::RuntimePointer<const char>(ptr);
    
    using PlatformTextEditedFn = void (*)(const char*, u32, u32);
    const auto& platform_text_edited = fugg::function_ref<PlatformTextEditedFn>(
        fugg::embind::kFunctionMap["platform_text_edited"]->fn
    );        

    platform_text_edited(
        text, selection_start, selection_end
    );
}

void platform_got_devicemotion_permission_hook() {
    using PlatformGotDeviceMotionPermissionFn = void (*)(void);
    auto platform_got_devicemotion_permission = fugg::function_ref<PlatformGotDeviceMotionPermissionFn>(
        fugg::embind::kFunctionMap["platform_got_devicemotion_permission"]->fn
    );        

    platform_got_devicemotion_permission();
}

void platform_captcha_completed_hook(std::string token) {
    struct WireType {
        size_t length;
        char data[1];
    };

    WireType* wt = (WireType*)malloc(sizeof(size_t) + token.length());
    wt->length = token.length();
    memcpy(wt->data, token.data(), token.length());

    using PlatformCaptchaCompletedFn = void (*)(std::string);
    using PlatformCaptchaCompletedInvoker = void (*)(PlatformCaptchaCompletedFn, WireType*);
    // Don't transform this, as it will be passed to a function inside the runtime (the invoker).
    auto platform_captcha_completed = reinterpret_cast<PlatformCaptchaCompletedFn>(
        fugg::embind::kFunctionMap["platform_captcha_completed"]->fn
    );        
    // Do transform this, as it's being called from outside.
    auto platform_captcha_completed_invoker = fugg::function_ref<PlatformCaptchaCompletedInvoker>(
        fugg::embind::kFunctionMap["platform_captcha_completed"]->invoker
    );        

    platform_captcha_completed_invoker(
        platform_captcha_completed,
        fugg::RuntimePointer<WireType>(wt)
    );

}

using PlatformWebsocketFn = void (*)(emscripten::val event);

void platform_websocket_open_hook(emscripten::val event) {
    // std::cout << "platform_websocket_open_hook:" << std::endl;// event=" << event << std::endl;

    const auto& platform_websocket_open = fugg::function_ref<PlatformWebsocketFn>(
        fugg::embind::kFunctionMap["platform_websocket_open"]->fn
    );

    platform_websocket_open(event);
}

void platform_websocket_close_hook(emscripten::val event) {
    // std::cout << "platform_websocket_close_hook:" << std::endl;// code=" << code << std::endl;

    const auto& platform_websocket_close = fugg::function_ref<PlatformWebsocketFn>(
        fugg::embind::kFunctionMap["platform_websocket_close"]->fn
    );

    platform_websocket_close(event);
}

void platform_websocket_message_hook(emscripten::val event) {
    // std::cout << "platform_websocket_message: Got " << event["data"]["byteLength"].as<unsigned long>() << " bytes" << std::endl;

    struct Test {
        emscripten::EM_VAL handle;
    };

    auto test = new Test { event.as_handle() };

    using PlatformWebsocketFn = void (*)(Test* event);

    const auto& platform_websocket_message = fugg::function_ref<PlatformWebsocketFn>(
        fugg::embind::kFunctionMap["platform_websocket_message"]->fn
    );

    platform_websocket_message(fugg::RuntimePointer<Test>(test));

    delete test;
}

EMSCRIPTEN_BINDINGS(my_module) {
    emscripten::function("platform_text_edited", &platform_text_edited_hook);
    emscripten::function("platform_got_devicemotion_permission", &platform_got_devicemotion_permission_hook);
    emscripten::function("platform_captcha_completed", &platform_captcha_completed_hook);
    emscripten::function("platform_websocket_open", &platform_websocket_open_hook);
    emscripten::function("platform_websocket_close", &platform_websocket_close_hook);
    emscripten::function("platform_websocket_message", &platform_websocket_message_hook);
}

extern "C" uintptr_t EMSCRIPTEN_KEEPALIVE flyff_memory_start() {
    return fugg::wasm::kMemory.data();
}

extern "C" uintptr_t EMSCRIPTEN_KEEPALIVE flyff_memory_size() {
    return fugg::wasm::kMemory.size();
}