#include "theme_listener.h"

#define HKEY_WINDOWS_THEME L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"

// disabling warning 4702 - unreachable code
// prevent the warning after the call off a infinite loop function
#pragma warning(push)
#pragma warning(disable : 4702)
DWORD WINAPI _checkTheme(LPVOID lpParam)
{
    auto listener = static_cast<ThemeListener*>(lpParam);
    listener->CheckTheme();
    return 0;
}
#pragma warning(pop)

void ThemeListener::AddChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    handles.push_back(handle);
}

void ThemeListener::DelChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    auto it = std::find(handles.begin(), handles.end(), handle);
    if (it != handles.end())
    {
        handles.erase(it);
    }
}

void ThemeListener::AddAppThemeChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    appThemeHandles.push_back(handle);
}

void ThemeListener::DelAppThemeChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    auto it = std::find(appThemeHandles.begin(), appThemeHandles.end(), handle);
    if (it != appThemeHandles.end())
    {
        appThemeHandles.erase(it);
    }
}

void ThemeListener::AddSystemThemeChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    systemThemeHandles.push_back(handle);
}

void ThemeListener::DelSystemThemeChangedHandler(THEME_HANDLE handle)
{
    std::lock_guard<std::mutex> lock(handlesMutex);
    auto it = std::find(systemThemeHandles.begin(), systemThemeHandles.end(), handle);
    if (it != systemThemeHandles.end())
    {
        systemThemeHandles.erase(it);
    }
}

void ThemeListener::CheckTheme()
{
    HANDLE hEvent;
    HKEY hKey;

    // Open the Key to listen
    RegOpenKeyEx(HKEY_CURRENT_USER, HKEY_WINDOWS_THEME, 0, KEY_NOTIFY, &hKey);

    while (true)
    {
        // Create an event.
        hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (hEvent != 0)
        {
            // Watch the registry key for a change of value.
            RegNotifyChangeKeyValue(hKey,
                                    TRUE,
                                    REG_NOTIFY_CHANGE_LAST_SET,
                                    hEvent,
                                    TRUE);

            WaitForSingleObject(hEvent, INFINITE);

            auto _appTheme = ThemeHelpers::GetAppTheme();
            auto _systemTheme = ThemeHelpers::GetSystemTheme();
            
            bool appThemeChanged = (AppTheme != _appTheme);
            bool systemThemeChanged = (SystemTheme != _systemTheme);
            
            if (appThemeChanged || systemThemeChanged)
            {
                AppTheme = _appTheme;
                SystemTheme = _systemTheme;
                
                // Copy handlers under lock, then invoke outside lock to avoid deadlock
                std::vector<THEME_HANDLE> handlesCopy;
                std::vector<THEME_HANDLE> appThemeHandlesCopy;
                std::vector<THEME_HANDLE> systemThemeHandlesCopy;
                
                {
                    std::lock_guard<std::mutex> lock(handlesMutex);
                    handlesCopy = handles;
                    if (appThemeChanged)
                    {
                        appThemeHandlesCopy = appThemeHandles;
                    }
                    if (systemThemeChanged)
                    {
                        systemThemeHandlesCopy = systemThemeHandles;
                    }
                }
                
                // Call generic handlers (backward compatible)
                for (const auto& handler : handlesCopy)
                {
                    handler();
                }
                
                // Call app theme specific handlers
                for (const auto& handler : appThemeHandlesCopy)
                {
                    handler();
                }
                
                // Call system theme specific handlers
                for (const auto& handler : systemThemeHandlesCopy)
                {
                    handler();
                }
            }
        }
    }
}