#include "WaylandKeyboard.h"

#include "FileLogger.h"

#include <unistd.h>
#include <sys/mman.h>

void WaylandKeyboard::keyboardKeymapEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size)
{
    auto thisClass = static_cast<WaylandKeyboard*>(data);
    thisClass->onKeymapEvent(wl_keyboard, format, fd, size);
}

void WaylandKeyboard::keyboardEnterEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
{
    auto thisClass = static_cast<WaylandKeyboard*>(data);
    thisClass->onEnterEvent(keys);
}

void WaylandKeyboard::keyboardPressedEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
    auto thisClass = static_cast<WaylandKeyboard*>(data);
    thisClass->onPressedEvent(key, state);
}

static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface)
{

}

static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay)
{

}

void WaylandKeyboard::keyboardModifierEvent(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
{
    auto thisClass = static_cast<WaylandKeyboard*>(data);
    thisClass->onKeyboardModifier(mods_depressed, mods_latched, mods_locked, group);
}

void WaylandKeyboard::onKeyboardModifier(uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
{
    xkb_state_update_mask(mXkbState, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}

void WaylandKeyboard::onEnterEvent(struct wl_array *keys)
{
    void* key;
    wl_array_for_each(key, keys)
    {
        uint32_t* key_type = static_cast<uint32_t*>(key);

        char buf[128];
        xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, *key_type + 8);
        xkb_keysym_get_name(sym, buf, sizeof(buf));
        //fprintf(stderr, "sym: %-12s (%d), ", buf, sym);
        xkb_state_key_get_utf8(mXkbState, *key_type + 8, buf, sizeof(buf));
        //fprintf(stderr, "utf8: '%s'\n", buf);
    }
}

void WaylandKeyboard::onPressedEvent(uint32_t key, uint32_t state)
{
    char buf[128];
    uint32_t keycode = key + 8;
    xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, keycode);
    xkb_keysym_get_name(sym, buf, sizeof(buf));
    const char *action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release";
    //fprintf(stderr, "key %s: sym: %-12s (%d), ", action, buf, sym);
    xkb_state_key_get_utf8(mXkbState, keycode, buf, sizeof(buf));
    //fprintf(stderr, "utf8: '%s'\n", buf);
}

void WaylandKeyboard::onKeymapEvent(struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size)
{
    // assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
    char* map_shm = static_cast<char*>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0));
    // assert(map_shm != MAP_FAILED);

    struct xkb_keymap* xkb_keymap = xkb_keymap_new_from_string(mXkbContext, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
    munmap(map_shm, size);
    close(fd);

    struct xkb_state* xkb_state = xkb_state_new(xkb_keymap);
    xkb_keymap_unref(mXkbKeymap);
    xkb_state_unref(mXkbState);
    mXkbKeymap = xkb_keymap;
    mXkbState = xkb_state;
}

WaylandKeyboard::WaylandKeyboard(wl_keyboard* keyboard)
    : mKeyboard(keyboard)
{
    mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);

    mKeyboardListener.keymap = keyboardKeymapEvent;
    mKeyboardListener.enter = keyboardEnterEvent;
    mKeyboardListener.key = keyboardPressedEvent;
    mKeyboardListener.leave = wl_keyboard_leave;
    mKeyboardListener.modifiers = keyboardModifierEvent;
    mKeyboardListener.repeat_info = wl_keyboard_repeat_info;

    wl_keyboard_add_listener(mKeyboard, &mKeyboardListener, this);
}


WaylandKeyboard::~WaylandKeyboard()
{
    wl_keyboard_release(mKeyboard);
}
