// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qdarwinpermissionplugin_p.h"

QT_BEGIN_NAMESPACE

QDarwinPermissionPlugin::QDarwinPermissionPlugin(QDarwinPermissionHandler *handler)
    : QPermissionPlugin()
    , m_handler(handler)
{
}

QDarwinPermissionPlugin::~QDarwinPermissionPlugin()
{
    [m_handler release];
}

Qt::PermissionStatus QDarwinPermissionPlugin::checkPermission(const QPermission &permission)
{
    return [m_handler checkPermission:permission];
}

void QDarwinPermissionPlugin::requestPermission(const QPermission &permission, const PermissionCallback &callback)
{
    if (!verifyUsageDescriptions(permission)) {
        callback(Qt::PermissionStatus::Denied);
        return;
    }

    [m_handler requestPermission:permission withCallback:[=](Qt::PermissionStatus status) {
        // In case the callback comes in on a secondary thread we need to marshal it
        // back to the main thread. And if it doesn't, we still want to propagate it
        // via an event, to avoid any GCD locks deadlocking the application on iOS
        // if the user responds to the result by running a nested event loop.
        // Luckily Qt::QueuedConnection gives us exactly what we need.
        QMetaObject::invokeMethod(this, "permissionUpdated", Qt::QueuedConnection,
            Q_ARG(Qt::PermissionStatus, status), Q_ARG(PermissionCallback, callback));
    }];
}

void QDarwinPermissionPlugin::permissionUpdated(Qt::PermissionStatus status, const PermissionCallback &callback)
{
    callback(status);
}

bool QDarwinPermissionPlugin::verifyUsageDescriptions(const QPermission &permission)
{
    // FIXME: Look up the responsible process and inspect that,
    // as that's what needs to have the usage descriptions.
    // FIXME: Verify entitlements if the process is sandboxed.
    auto *infoDictionary = NSBundle.mainBundle.infoDictionary;
    for (auto description : [m_handler usageDescriptionsFor:permission]) {
        if (!infoDictionary[description.toNSString()]) {
            qCWarning(lcPermissions) <<
                "Requesting" << permission.type().name() <<
                "requires" << description << "in Info.plist";
            return false;
        }
    }
    return true;
}

QT_END_NAMESPACE

QT_USE_NAMESPACE

@implementation QDarwinPermissionHandler

- (Qt::PermissionStatus)checkPermission:(QPermission)permission
{
    Q_UNREACHABLE(); // All handlers should at least provide a check
}

- (void)requestPermission:(QPermission)permission withCallback:(PermissionCallback)callback
{
    Q_UNUSED(permission);
    qCWarning(lcPermissions).nospace() << "Could not request " << permission.type().name() << ". "
        << "Please make sure you have included the required usage description in your Info.plist";
    callback(Qt::PermissionStatus::Denied);
}

- (QStringList)usageDescriptionsFor:(QPermission)permission
{
    return {};
}

@end

#include "moc_qdarwinpermissionplugin_p.cpp"
