#include "GameKitInput.h"
#include <tt_lvgl_keyboard.h>

namespace GameKit {

InputKind keyToInput(uint32_t key) {
    switch (key) {
        case LV_KEY_UP: return InputKind::Up;
        case LV_KEY_DOWN: return InputKind::Down;
        case LV_KEY_LEFT: return InputKind::Left;
        case LV_KEY_RIGHT: return InputKind::Right;
        case LV_KEY_ENTER: return InputKind::Confirm;
        case LV_KEY_ESC: return InputKind::Cancel;
        default: break;
    }
    if (key == 'w' || key == 'W') return InputKind::Up;
    if (key == 's' || key == 'S') return InputKind::Down;
    if (key == 'a' || key == 'A') return InputKind::Left;
    if (key == 'd' || key == 'D') return InputKind::Right;
    if (key == 'q' || key == 'Q') return InputKind::Cancel;
    return InputKind::Unknown;
}

InputKind gestureToInput(lv_dir_t dir) {
    if (dir & LV_DIR_TOP) return InputKind::Up;
    if (dir & LV_DIR_BOTTOM) return InputKind::Down;
    if (dir & LV_DIR_LEFT) return InputKind::Left;
    if (dir & LV_DIR_RIGHT) return InputKind::Right;
    return InputKind::Unknown;
}

InputKind pointToQuadrant(lv_obj_t* obj, lv_point_t point) {
    const lv_coord_t w = lv_obj_get_width(obj);
    const lv_coord_t h = lv_obj_get_height(obj);
    const lv_coord_t cx = w / 2;
    const lv_coord_t cy = h / 2;
    const lv_coord_t dx = point.x - cx;
    const lv_coord_t dy = point.y - cy;
    if (LV_ABS(dx) > LV_ABS(dy)) return dx < 0 ? InputKind::Left : InputKind::Right;
    return dy < 0 ? InputKind::Up : InputKind::Down;
}

static void inputEvent(lv_event_t* e) {
    auto* scene = static_cast<Scene*>(lv_event_get_user_data(e));
    if (scene == nullptr) return;
    InputEvent input;
    const lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* target = lv_event_get_target_obj(e);
    if (code == LV_EVENT_KEY) {
        input.kind = keyToInput(lv_event_get_key(e));
    } else if (code == LV_EVENT_GESTURE) {
        input.kind = gestureToInput(lv_indev_get_gesture_dir(lv_indev_active()));
    } else if (code == LV_EVENT_CLICKED) {
        lv_indev_t* indev = lv_indev_active();
        if (indev) {
            lv_indev_get_point(indev, &input.point);
            lv_area_t coords;
            lv_obj_get_coords(target, &coords);
            input.point.x -= coords.x1;
            input.point.y -= coords.y1;
            input.kind = pointToQuadrant(target, input.point);
        } else {
            input.kind = InputKind::Confirm;
        }
    }
    if (input.kind != InputKind::Unknown) scene->onInput(input);
}

void attachInput(lv_obj_t* target, Scene* scene) {
    lv_obj_add_flag(target, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_add_event_cb(target, inputEvent, LV_EVENT_KEY, scene);
    lv_obj_add_event_cb(target, inputEvent, LV_EVENT_GESTURE, scene);
    lv_obj_add_event_cb(target, inputEvent, LV_EVENT_CLICKED, scene);
    if (tt_lvgl_hardware_keyboard_is_available()) {
        lv_group_t* group = lv_group_get_default();
        if (group) {
            lv_group_add_obj(group, target);
            lv_group_focus_obj(target);
            lv_group_set_editing(group, true);
        }
    }
}

} // namespace GameKit
