#include "pch.h"

#include "platform_windows/windows_stylus_input.h"

#include "app.h"
#include "log.h"
#include "platform_windows/windows_runtime_shell.h"
#include "wacom.h"

namespace pp::platform::windows {

namespace {

struct StylusInputState final
{
    float timer_stylus = 0.0f;
    float timer_ink_touch = 0.0f;
    float timer_ink_pen = 0.0f;

    BOOL (*get_pointer_info)(UINT32, POINTER_INFO*) = nullptr;
    BOOL (*get_pointer_type)(UINT32, POINTER_INPUT_TYPE*) = nullptr;
    BOOL (*get_pointer_touch_info)(UINT32, POINTER_TOUCH_INFO*) = nullptr;
    BOOL (*get_pointer_pen_info)(UINT32, POINTER_PEN_INFO*) = nullptr;
};

StylusInputState& stylus_input_state()
{
    static StylusInputState state;
    return state;
}

[[nodiscard]] WacomTablet& active_wacom_tablet()
{
    auto* tablet = bound_wacom_tablet();
    assert(tablet);
    return *tablet;
}

}

void initialize_stylus_input()
{
    auto& state = stylus_input_state();
    HMODULE dll = LoadLibrary(L"User32.dll");
    if (!dll)
    {
        LOG("cannot load User32.dll");
        return;
    }

    LOG("loaded User32.dll");
    state.get_pointer_info =
        reinterpret_cast<decltype(state.get_pointer_info)>(GetProcAddress(dll, "GetPointerInfo"));
    state.get_pointer_type =
        reinterpret_cast<decltype(state.get_pointer_type)>(GetProcAddress(dll, "GetPointerType"));
    state.get_pointer_touch_info =
        reinterpret_cast<decltype(state.get_pointer_touch_info)>(GetProcAddress(dll, "GetPointerTouchInfo"));
    state.get_pointer_pen_info =
        reinterpret_cast<decltype(state.get_pointer_pen_info)>(GetProcAddress(dll, "GetPointerPenInfo"));
}

void update_stylus_state(float dt)
{
    auto& state = stylus_input_state();
    auto* app = bound_app();
    auto& tablet = active_wacom_tablet();
    state.timer_stylus += dt;
    state.timer_ink_touch += dt;
    state.timer_ink_pen += dt;

    if (state.timer_stylus > 0.1f && (tablet.m_stylus || tablet.m_eraser))
    {
        tablet.m_stylus = false;
        tablet.m_eraser = false;
        app->redraw = true;
    }
    if (state.timer_ink_pen > 0.1f && tablet.m_ink_pen)
    {
        tablet.m_ink_pen = false;
        app->redraw = true;
    }
    if (state.timer_ink_touch > 0.1f && tablet.m_ink_touch)
    {
        tablet.m_ink_touch = false;
        app->redraw = true;
    }
}

void note_wintab_packet()
{
    auto& state = stylus_input_state();
    bound_app()->set_stylus();
    state.timer_stylus = 0.0f;
}

void handle_pointer_update_message(WPARAM wp)
{
    auto& state = stylus_input_state();
    auto& tablet = active_wacom_tablet();
    if (!state.get_pointer_info)
        return;

    POINTER_TOUCH_INFO touch_info {};
    POINTER_PEN_INFO pen_info {};
    POINTER_INFO pointer_info {};
    const UINT32 pointer_id = GET_POINTERID_WPARAM(wp);
    POINTER_INPUT_TYPE pointer_type = PT_POINTER;

    if (!state.get_pointer_info(pointer_id, &pointer_info))
        return;

    if (!state.get_pointer_type(pointer_id, &pointer_type))
        pointer_type = PT_POINTER;

    switch (pointer_type)
    {
    case PT_TOUCH:
        if (!state.get_pointer_touch_info(pointer_id, &touch_info))
            return;

        state.timer_ink_touch = 0.0f;
        tablet.m_ink_touch = true;
        tablet.m_pen_pres = 1.0f;
        return;

    case PT_PEN:
        if (!state.get_pointer_pen_info(pointer_id, &pen_info))
            return;

        state.timer_ink_pen = 0.0f;
        tablet.m_ink_pen = true;
        tablet.m_pen_pres = static_cast<float>(pen_info.pressure) / 1024.0f;
        bound_app()->set_stylus();
        return;

    default:
        return;
    }
}

}
