#include "ukuivirtualkeyboard.h"
#include "ukuidisplay_p.h"

#include "virtual-keyboard-unstable-v1.h"

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

#include <QDebug>

class UkuiVirtualKeyboardPrivate
{
public:
    explicit UkuiVirtualKeyboardPrivate(const char *keymap);
    ~UkuiVirtualKeyboardPrivate();

    void keyPress(uint32_t key);
    void keyRelease(uint32_t key);
    void modifiers(uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked);

private:
    wayland::zwp_virtual_keyboard_manager_v1_t m_virtualKeyboardManager;
    wayland::zwp_virtual_keyboard_v1_t m_virtualKeyboard;

    uint32_t symToKeyCode(uint32_t sym) const;
};

UkuiVirtualKeyboardPrivate::UkuiVirtualKeyboardPrivate(const char *keymap)
{
    if (!keymap || keymap[0] == '\0') {
        qWarning() << "Keymap is empty or nullptr";
        return;
    }

    m_virtualKeyboardManager = static_cast<wayland::zwp_virtual_keyboard_manager_v1_t>(ukuiDisplay()->bind(wayland::detail::zwp_virtual_keyboard_manager_v1_interface.name, m_virtualKeyboardManager, wayland::detail::zwp_virtual_keyboard_manager_v1_interface.version));
    if (!m_virtualKeyboardManager) {
        qWarning() << "Failed to bind zwp_virtual_keyboard_manager_v1";
        return;
    }
    m_virtualKeyboard = m_virtualKeyboardManager.create_virtual_keyboard(ukuiDisplay()->seat());
    std::string keymap_file = "/tmp/waylandpp-XXXXXX";
    int fd = mkstemp(const_cast<char *>(keymap_file.c_str()));
    if (fd == -1) {
        qWarning() << "Failed to create keymap file";
        return;
    }
    unlink(keymap_file.c_str());
    FILE *f = fdopen(fd, "w");
    fprintf(f, "%s", keymap);
    fflush(f);

    size_t keymap_size = ftell(f);

    m_virtualKeyboard.keymap(0, fd, keymap_size);
    fclose(f);
}

UkuiVirtualKeyboardPrivate::~UkuiVirtualKeyboardPrivate()
{
}

void UkuiVirtualKeyboardPrivate::keyPress(uint32_t key)
{
    if (!m_virtualKeyboard) {
        qWarning() << "Virtual keyboard is not initialized";
        return;
    }
    m_virtualKeyboard.key(0, key, 1);
}

void UkuiVirtualKeyboardPrivate::keyRelease(uint32_t key)
{
    if (!m_virtualKeyboard) {
        qWarning() << "Virtual keyboard is not initialized";
        return;
    }
    m_virtualKeyboard.key(0, key, 0);
}

void UkuiVirtualKeyboardPrivate::modifiers(uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked)
{
    if (!m_virtualKeyboard) {
        qWarning() << "Virtual keyboard is not initialized";
        return;
    }
    m_virtualKeyboard.modifiers(mods_depressed, mods_latched, mods_locked, 0);
}

UkuiVirtualKeyboard::UkuiVirtualKeyboard(const char *keymap)
    : m_ukuiVirtualKeyboardPrivate(new UkuiVirtualKeyboardPrivate(keymap))
{
}

UkuiVirtualKeyboard::~UkuiVirtualKeyboard()
{
    delete m_ukuiVirtualKeyboardPrivate;
    m_ukuiVirtualKeyboardPrivate = nullptr;
}

void UkuiVirtualKeyboard::keyPress(uint32_t key)
{
    if (m_ukuiVirtualKeyboardPrivate) {
        m_ukuiVirtualKeyboardPrivate->keyPress(key);
    }
}

void UkuiVirtualKeyboard::keyRelease(uint32_t key)
{
    if (m_ukuiVirtualKeyboardPrivate) {
        m_ukuiVirtualKeyboardPrivate->keyRelease(key);
    }
}

void UkuiVirtualKeyboard::modifiers(uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked)
{
    if (m_ukuiVirtualKeyboardPrivate) {
        m_ukuiVirtualKeyboardPrivate->modifiers(mods_depressed, mods_latched, mods_locked);
    }
}
