#include "pch.h"
#include "powertoys_events.h"
#include "lowlevel_keyboard_event.h"
#include "win_hook_event.h"
#include "system_menu_helper.h"

void first_subscribed(const std::wstring& event)
{
    if (event == ll_keyboard)
        start_lowlevel_keyboard_hook();
    else if (event == win_hook_event)
        start_win_hook_event();
}

void last_unsubscribed(const std::wstring& event)
{
    if (event == ll_keyboard)
        stop_lowlevel_keyboard_hook();
    else if (event == win_hook_event)
        stop_win_hook_event();
}

PowertoysEvents& powertoys_events()
{
    static PowertoysEvents powertoys_events;
    return powertoys_events;
}

void PowertoysEvents::register_receiver(const std::wstring& event, PowertoyModuleIface* module)
{
    std::unique_lock lock(mutex);
    auto& subscribers = receivers[event];
    if (subscribers.empty())
    {
        first_subscribed(event);
    }
    subscribers.push_back(module);
}

void PowertoysEvents::unregister_receiver(PowertoyModuleIface* module)
{
    std::unique_lock lock(mutex);
    for (auto& [event, subscribers] : receivers)
    {
        subscribers.erase(remove(begin(subscribers), end(subscribers), module), end(subscribers));
        if (subscribers.empty())
        {
            last_unsubscribed(event);
        }
    }
}

void PowertoysEvents::register_system_menu_action(PowertoyModuleIface* module)
{
    std::unique_lock lock(mutex);
    system_menu_receivers.insert(module);
}

void PowertoysEvents::unregister_system_menu_action(PowertoyModuleIface* module)
{
    std::unique_lock lock(mutex);
    auto it = system_menu_receivers.find(module);
    if (it != system_menu_receivers.end())
    {
        SystemMenuHelperInstace().Reset(module);
        system_menu_receivers.erase(it);
    }
}

void PowertoysEvents::handle_system_menu_action(const WinHookEvent& data)
{
    if (data.event == EVENT_SYSTEM_MENUSTART)
    {
        for (auto& module : system_menu_receivers)
        {
            SystemMenuHelperInstace().Customize(module, data.hwnd);
        }
    }
    else if (data.event == EVENT_OBJECT_INVOKED)
    {
        if (PowertoyModuleIface * module{ SystemMenuHelperInstace().ModuleFromItemId(data.idChild) })
        {
            std::wstring itemName = SystemMenuHelperInstace().ItemNameFromItemId(data.idChild);
            // Process event on specified system menu item by responsible module.
            module->signal_system_menu_action(itemName.c_str());
            // Process event on specified system menu item by system menu helper (check/uncheck if needed).
            SystemMenuHelperInstace().ProcessSelectedItem(module, GetForegroundWindow(), itemName.c_str());
        }
    }
}

intptr_t PowertoysEvents::signal_event(const std::wstring& event, intptr_t data)
{
    intptr_t rvalue = 0;
    std::shared_lock lock(mutex);
    if (auto it = receivers.find(event); it != end(receivers))
    {
        for (auto& module : it->second)
        {
            if (module)
                rvalue |= module->signal_event(event.c_str(), data);
        }
    }
    return rvalue;
}