#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#include <QtVersionChecks>

#if QT_VERSION != QT_VERSION_CHECK(6, 9, 1)
#error "This code is only compatible with Qt 6.9.1. Hopefully QTBUG-137757 has been fixed"
#endif

static Class kQMacAccessibilityElementClass = Nil;
static void (*originalReleaseIMP)(id, SEL) = NULL;

@interface NSObject (SafeRelease)
- (oneway void)safe_release;
@end

@implementation NSObject (SafeRelease)

+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = NSClassFromString(@"QMacAccessibilityElement");
        if (!cls) {
            NSLog(@"QMacAccessibilityElement not found");
            return;
        }

        kQMacAccessibilityElementClass = cls;

        // Swizzle release method
        SEL origReleaseSel = @selector(release);
        SEL swizzReleaseSel = @selector(safe_release);

        // obtaining -release from NSObject
        Method origReleaseMethod = class_getInstanceMethod(cls, origReleaseSel);
        // getting our own safe_release
        Method swizzReleaseMethod = class_getInstanceMethod(self, swizzReleaseSel);

        if (origReleaseMethod && swizzReleaseMethod) {
            originalReleaseIMP = (void (*)(id, SEL)) method_getImplementation(origReleaseMethod);

            method_exchangeImplementations(origReleaseMethod, swizzReleaseMethod);
            NSLog(@"Swizzled NSObject.release");
        } else {
            NSLog(@"Swizzling NSObject release failed!");
        }
    });
}

- (oneway void)safe_release
{
    // Workaround for https://bugreports.qt.io/browse/QTBUG-137757
    // Wait for 5 milliseconds before actually performing the release since Qt internally
    // releases a pointer it wants to retain
    if ([self isKindOfClass:kQMacAccessibilityElementClass]) {
        // Create a timer that will release after 5ms
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.005 // 5 milliseconds
                                                         repeats:NO
                                                           block:^(NSTimer *timer) {
                                                               // Perform the actual release
                                                               originalReleaseIMP(self, @selector(release));
                                                           }];
        return;
    }

    originalReleaseIMP(self, @selector(release));
}

@end
