#import "_SentryDispatchQueueWrapperInternal.h"
#import "SentryInternalDefines.h"

NS_ASSUME_NONNULL_BEGIN

@implementation _SentryDispatchQueueWrapperInternal

- (instancetype)init
{
    // DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL is requires iOS 10. Since we are targeting
    // iOS 9 we need to manually add the autoreleasepool.
    dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
        DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self = [self initWithName:"io.sentry.default" attributes:attributes];
    return self;
}

- (instancetype)initWithName:(const char *)name
                  attributes:(nullable dispatch_queue_attr_t)attributes;
{
    if (self = [super init]) {
        _queue = dispatch_queue_create(name, attributes);
    }
    return self;
}

- (instancetype)initWithName:(const char *)name relativePriority:(int)relativePriority
{
    if (self = [super init]) {
        SENTRY_CASSERT(relativePriority <= 0 && relativePriority >= QOS_MIN_RELATIVE_PRIORITY,
            @"Relative priority must be between 0 and %d", QOS_MIN_RELATIVE_PRIORITY);
        dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
            DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, relativePriority);
        _queue = dispatch_queue_create(name, attributes);
    }
    return self;
}

- (void)dispatchAsyncOnMainQueueIfNotMainThread:(void (^)(void))block
{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            @autoreleasepool {
                block();
            }
        });
    }
}

- (void)dispatchAsyncWithBlock:(void (^)(void))block
{
    dispatch_async(_queue, ^{
        @autoreleasepool {
            block();
        }
    });
}

- (void)dispatchSync:(void (^)(void))block
{
    dispatch_sync(_queue, block);
}

- (void)dispatchSyncOnMainQueue:(void (^)(void))block
{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

- (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout
{
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        dispatch_async(dispatch_get_main_queue(), ^{
            block();
            dispatch_semaphore_signal(semaphore);
        });

        dispatch_time_t timeout_t
            = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
        return dispatch_semaphore_wait(semaphore, timeout_t) == 0;
    }
    return YES;
}

- (void)dispatchAfter:(NSTimeInterval)interval block:(void (^)(void))block
{
    dispatch_time_t delta = (int64_t)(interval * NSEC_PER_SEC);
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delta);
    dispatch_after(when, _queue, ^{
        @autoreleasepool {
            block();
        }
    });
}

- (void)dispatchOnce:(dispatch_once_t *)predicate block:(void (^)(void))block
{
    dispatch_once(predicate, block);
}

@end

NS_ASSUME_NONNULL_END
