#pragma once

#include <sdkddkver.h>

// Windows Header Files:
#include <windows.h>
#include <sal.h>
#include <psapi.h>
#include <strsafe.h>
#include <objbase.h>
#include <shobjidl.h>
#include <functiondiscoverykeys.h>
#include <guiddef.h>
#include <shlguid.h>

#include <wrl/client.h>
#include <wrl/implements.h>
#include <windows.ui.notifications.h>

#include <string>
#include <vector>

#include <napi.h>

#include "DesktopNotification.h"

using namespace Microsoft::WRL;
using namespace ABI::Windows::Data::Xml::Dom;

typedef ABI::Windows::Foundation::ITypedEventHandler<
    ABI::Windows::UI::Notifications::ToastNotification *, ::IInspectable *>
    DesktopToastActivatedEventHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<
    ABI::Windows::UI::Notifications::ToastNotification *,
    ABI::Windows::UI::Notifications::ToastDismissedEventArgs *>
    DesktopToastDismissedEventHandler;
typedef ABI::Windows::Foundation::ITypedEventHandler<
    ABI::Windows::UI::Notifications::ToastNotification *,
    ABI::Windows::UI::Notifications::ToastFailedEventArgs *>
    DesktopToastFailedEventHandler;

class DesktopNotificationsManager : public Microsoft::WRL::Implements<DesktopToastActivatedEventHandler,
                                                                      DesktopToastDismissedEventHandler,
                                                                      DesktopToastFailedEventHandler>
{
    friend class DesktopNotification;

public:
    DesktopNotificationsManager(const std::wstring &toastActivatorClsid,
                                Napi::Function &callback);
    ~DesktopNotificationsManager();

    const std::string getCurrentPermission();

    HRESULT displayToast(const std::wstring &id,
                         const std::wstring &title,
                         const std::wstring &body,
                         const std::wstring &userInfo);
    bool closeToast(const std::wstring &id);

    void handleActivatorEvent(const std::wstring &launchArgs);

    // DesktopToastActivatedEventHandler
    IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender,
                          _In_ IInspectable *args);

    // DesktopToastDismissedEventHandler
    IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender,
                          _In_ ABI::Windows::UI::Notifications::IToastDismissedEventArgs *e);

    // DesktopToastFailedEventHandler
    IFACEMETHODIMP Invoke(_In_ ABI::Windows::UI::Notifications::IToastNotification *sender,
                          _In_ ABI::Windows::UI::Notifications::IToastFailedEventArgs *e);

    // IUnknown
    IFACEMETHODIMP_(ULONG)
    AddRef()
    {
        return InterlockedIncrement(&m_ref);
    }

    IFACEMETHODIMP_(ULONG)
    Release()
    {
        ULONG l = InterlockedDecrement(&m_ref);
        if (l == 0)
        {
            delete this;
        }
        return l;
    }

    IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv)
    {
        if (IsEqualIID(riid, IID_IUnknown))
        {
            *ppv = static_cast<IUnknown *>(static_cast<DesktopToastActivatedEventHandler *>(this));
        }
        else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler)))
        {
            *ppv = static_cast<DesktopToastActivatedEventHandler *>(this);
        }
        else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler)))
        {
            *ppv = static_cast<DesktopToastDismissedEventHandler *>(this);
        }
        else if (IsEqualIID(riid, __uuidof(DesktopToastFailedEventHandler)))
        {
            *ppv = static_cast<DesktopToastFailedEventHandler *>(this);
        }
        else
        {
            *ppv = nullptr;
        }

        if (*ppv)
        {
            reinterpret_cast<IUnknown *>(*ppv)->AddRef();
            return S_OK;
        }

        return E_NOINTERFACE;
    }

private:
    Microsoft::WRL::ComPtr<ABI::Windows::UI::Notifications::IToastNotificationHistory> getHistory();

    Microsoft::WRL::ComPtr<ABI::Windows::UI::Notifications::IToastNotificationManagerStatics> m_toastManager;

    std::vector<std::shared_ptr<DesktopNotification>> m_desktopNotifications;

    void SignalObjectCountZero();
    HRESULT RegisterClassObjects(const std::wstring &toastActivatorClsid);
    HRESULT UnregisterClassObjects();
    bool closeNotification(std::shared_ptr<DesktopNotification> d);
    void DesktopNotificationsManager::invokeJSCallback(const std::string &eventName,
                                                       const std::string &notificationID,
                                                       const std::wstring &userInfo);

    // Identifiers of registered class objects. Used for unregistration.
    DWORD m_comCookies[1] = {0};
    const std::wstring m_toastActivatorClsid;
    Napi::ThreadSafeFunction m_callback;
    std::wstring m_appID;
    ULONG m_ref;
};

extern std::shared_ptr<DesktopNotificationsManager> desktopNotificationsManager;
