
#include "../../SDL_internal.h"

#if SDL_VIDEO_DRIVER_WINRT

/* Windows includes */
#include <roapi.h>
#include <windows.foundation.h>
#include <EventToken.h>


/* SDL includes */
extern "C" {
#include "SDL_mouse.h"
#include "../SDL_sysvideo.h"
}
#include "SDL_winrtvideo_cpp.h"


/* Game Bar events can come in off the main thread.  Use the following
   WinRT CoreDispatcher to deal with them on SDL's thread.
*/
static Platform::WeakReference WINRT_MainThreadDispatcher;


/* Win10's initial SDK (the 10.0.10240.0 release) does not include references
   to Game Bar APIs, as the Game Bar was released via Win10 10.0.10586.0.

   Declare its WinRT/COM interface here, to allow compilation with earlier
   Windows SDKs.
*/
MIDL_INTERFACE("1DB9A292-CC78-4173-BE45-B61E67283EA7")
IGameBarStatics_ : public IInspectable
{
public:
    virtual HRESULT STDMETHODCALLTYPE add_VisibilityChanged( 
        __FIEventHandler_1_IInspectable *handler,
        Windows::Foundation::EventRegistrationToken *token) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE remove_VisibilityChanged( 
        Windows::Foundation::EventRegistrationToken token) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE add_IsInputRedirectedChanged( 
        __FIEventHandler_1_IInspectable *handler,
        Windows::Foundation::EventRegistrationToken *token) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE remove_IsInputRedirectedChanged( 
        Windows::Foundation::EventRegistrationToken token) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE get_Visible( 
        boolean *value) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE get_IsInputRedirected( 
        boolean *value) = 0;
};

/* Declare the game bar's COM GUID */
static GUID IID_IGameBarStatics_ = { MAKELONG(0xA292, 0x1DB9), 0xCC78, 0x4173, { 0xBE, 0x45, 0xB6, 0x1E, 0x67, 0x28, 0x3E, 0xA7 } };

/* Retrieves a pointer to the game bar, or NULL if it is not available.
   If a pointer is returned, it's ->Release() method must be called
   after the caller has finished using it.
*/
static IGameBarStatics_ *
WINRT_GetGameBar()
{
    wchar_t *wClassName = L"Windows.Gaming.UI.GameBar";
    HSTRING hClassName;
    IActivationFactory *pActivationFactory = NULL;
    IGameBarStatics_ *pGameBar = NULL;
    HRESULT hr;

    hr = ::WindowsCreateString(wClassName, (UINT32)wcslen(wClassName), &hClassName);
    if (FAILED(hr)) {
        goto done;
    }

    hr = Windows::Foundation::GetActivationFactory(hClassName, &pActivationFactory);
    if (FAILED(hr)) {
        goto done;
    }

    pActivationFactory->QueryInterface(IID_IGameBarStatics_, (void **) &pGameBar);

done:
    if (pActivationFactory) {
        pActivationFactory->Release();
    }
    if (hClassName) {
        ::WindowsDeleteString(hClassName);
    }
    return pGameBar;
}

static void
WINRT_HandleGameBarIsInputRedirected_MainThread()
{
    IGameBarStatics_ *gameBar;
    boolean isInputRedirected = 0;
    if (!WINRT_MainThreadDispatcher) {
        /* The game bar event handler has been deregistered! */
        return;
    }
    gameBar = WINRT_GetGameBar();
    if (!gameBar) {
        /* Shouldn't happen, but just in case... */
        return;
    }
    if (SUCCEEDED(gameBar->get_IsInputRedirected(&isInputRedirected))) {
        if ( ! isInputRedirected) {
            /* Input-control is now back to the SDL app. Restore the cursor,
               in case Windows does not (it does not in either Win10
               10.0.10240.0 or 10.0.10586.0, maybe later version(s) too.
            */
            SDL_Cursor *cursor = SDL_GetCursor();
            SDL_SetCursor(cursor);
        }
    }
    gameBar->Release();
}

static void
WINRT_HandleGameBarIsInputRedirected_NonMainThread(Platform::Object ^ o1, Platform::Object ^o2)
{
    Windows::UI::Core::CoreDispatcher ^dispatcher = WINRT_MainThreadDispatcher.Resolve<Windows::UI::Core::CoreDispatcher>();
    if (dispatcher) {
        dispatcher->RunAsync(
            Windows::UI::Core::CoreDispatcherPriority::Normal,
            ref new Windows::UI::Core::DispatchedHandler(&WINRT_HandleGameBarIsInputRedirected_MainThread));
    }
}

void
WINRT_InitGameBar(_THIS)
{
    SDL_VideoData *driverdata = (SDL_VideoData *)_this->driverdata;
    IGameBarStatics_ *gameBar = WINRT_GetGameBar();
    if (gameBar) {
        /* GameBar.IsInputRedirected events can come in via something other than
           the main/SDL thread.

           Get a WinRT 'CoreDispatcher' that can be used to call back into the
           SDL thread.
        */
        WINRT_MainThreadDispatcher = Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher;
        Windows::Foundation::EventHandler<Platform::Object ^> ^handler = \
            ref new Windows::Foundation::EventHandler<Platform::Object ^>(&WINRT_HandleGameBarIsInputRedirected_NonMainThread);
        __FIEventHandler_1_IInspectable * pHandler = reinterpret_cast<__FIEventHandler_1_IInspectable *>(handler);
        gameBar->add_IsInputRedirectedChanged(pHandler, &driverdata->gameBarIsInputRedirectedToken);
        gameBar->Release();
    }
}

void
WINRT_QuitGameBar(_THIS)
{
    SDL_VideoData *driverdata;
    IGameBarStatics_ *gameBar;
    if (!_this || !_this->driverdata) {
        return;
    }
    gameBar = WINRT_GetGameBar();
    if (!gameBar) {
        return;
    }
    driverdata = (SDL_VideoData *)_this->driverdata;
    if (driverdata->gameBarIsInputRedirectedToken.Value) {
        gameBar->remove_IsInputRedirectedChanged(driverdata->gameBarIsInputRedirectedToken);
        driverdata->gameBarIsInputRedirectedToken.Value = 0;
    }
    WINRT_MainThreadDispatcher = nullptr;
    gameBar->Release();
}

#endif /* SDL_VIDEO_DRIVER_WINRT */

/* vi: set ts=4 sw=4 expandtab: */
