#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.Control.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Storage.h>
#include <wchar.h>
#include <windows.h>

extern "C"
{
#include "media_windows.dll.h"

const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover)
{
    // C++/WinRT requires Windows 8.1+ and C++ runtime (std::string, exceptions and other stuff)
    // Make it a separate dll in order not to break Windows 7 support
    using namespace winrt::Windows::Media::Control;
    using namespace winrt::Windows::ApplicationModel;

    try
    {
        auto manager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get();
        if (!manager)
            return "winrt: GlobalSystemMediaTransportControlsSessionManager::RequestAsync() failed";

        auto session = manager.GetCurrentSession();
        if (!session)
            return "winrt: GetCurrentSession() failed";

        auto mediaProps = session
            .TryGetMediaPropertiesAsync()
            .get();
        if (!mediaProps)
            return "winrt: TryGetMediaPropertiesAsync() failed";

        if (auto playbackInfo = session.GetPlaybackInfo())
        {
            switch (playbackInfo.PlaybackStatus())
            {
            #define FF_MEDIA_SET_STATUS(status_code) \
    case GlobalSystemMediaTransportControlsSessionPlaybackStatus::status_code: result->status = #status_code; break;
                FF_MEDIA_SET_STATUS(Closed)
                FF_MEDIA_SET_STATUS(Opened)
                FF_MEDIA_SET_STATUS(Changing)
                FF_MEDIA_SET_STATUS(Stopped)
                FF_MEDIA_SET_STATUS(Playing)
                FF_MEDIA_SET_STATUS(Paused)
            #undef FF_MEDIA_SET_STATUS
            }
        }

        ::wcsncpy(result->playerId, session.SourceAppUserModelId().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
        result->playerId[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
        ::wcsncpy(result->song, mediaProps.Title().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
        result->song[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
        ::wcsncpy(result->artist, mediaProps.Artist().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
        result->artist[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
        ::wcsncpy(result->album, mediaProps.AlbumTitle().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
        result->album[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
        try
        {
            // Only works for UWP apps
            ::wcsncpy(result->playerName, AppInfo::GetFromAppUserModelId(session.SourceAppUserModelId()).DisplayInfo().DisplayName().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
            result->playerName[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
        } catch (...) { }

        if (saveCover)
        {
            using namespace winrt::Windows::Storage;
            using namespace winrt::Windows::Storage::Streams;
            if (auto thumbRef = mediaProps.Thumbnail())
            {
                try
                {
                    if (auto stream = thumbRef.OpenReadAsync().get())
                    {
                        if (stream.Size() > 0)
                        {
                            Buffer buffer(static_cast<uint32_t>(stream.Size()));
                            stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();

                            wchar_t tempPath[MAX_PATH];
                            if (GetTempPathW(MAX_PATH, tempPath) > 0)
                            {
                                auto tempFolder = StorageFolder::GetFolderFromPathAsync(tempPath).get();
                                auto tempFile = tempFolder.CreateFileAsync(L"ff_thumb.img", CreationCollisionOption::GenerateUniqueName).get();
                                FileIO::WriteBufferAsync(tempFile, buffer).get();

                                ::wcsncpy(result->cover, tempFile.Path().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
                                result->cover[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
                            }
                        }
                    }
                }
                catch (...)
                {
                    // Ignore thumbnail errors
                }
            }
        }

        return NULL;
    }
    catch (...)
    {
        return "A C++ exception is thrown";
    }
}

} // extern "C"
