#include <QtGui>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QQuickItem>
#include <QQmlContext>

#include <AppKit/AppKit.h>
#include <crt_externs.h>

class Object : public QObject
{
public:
    Object(const char *name) {
        setObjectName(name);
        qDebug() << __FUNCTION__ << this;
        deleteLater();
    }
    ~Object() { qDebug() << __FUNCTION__ << this; }
};

class Timer
{
public:
    template <typename Functor>
    explicit Timer(double interval, Functor functor) {
        
        @autoreleasepool {
            __block auto timerFunc = std::move(functor);
            
            m_timer = [[NSTimer alloc] initWithFireDate: [NSDate dateWithTimeIntervalSinceNow: 0.0]
                                               interval: interval
                                                repeats: YES
                                                  block: ^(NSTimer *) {
                
                timerFunc();
            }];
            
            [[NSRunLoop currentRunLoop] addTimer: static_cast<NSTimer *>(m_timer)
                                         forMode: NSRunLoopCommonModes];
        }
    }
    ~Timer() { if (m_timer)  { [m_timer invalidate]; } }
    
private:
    
    NSTimer * m_timer{nullptr};
};

class Window : public QQuickWindow
{
protected:
    void mousePressEvent(QMouseEvent *) { new Object(__FUNCTION__); }
    void keyPressEvent(QKeyEvent *) { new Object(__FUNCTION__); }
};

void setupQtApplication()
{
    new QGuiApplication(*_NSGetArgc(), *_NSGetArgv());
    auto *window = new Window;
    
    new Object(__FUNCTION__);
    
    QQmlEngine engine;
    QQmlComponent component(&engine);
    
    QObject::connect(&component, &QQmlComponent::statusChanged, []() { new Object("statusChanged"); });
    
    component.setData("import QtQuick\nRectangle{color:\"black\";x:25;y:25;width:100;height:100;focus:true}", QUrl());
    QQuickItem *item = qobject_cast<QQuickItem *>(component.create());
    
    QObject::connect(item, &QQuickItem::windowChanged, []() { new Object("windowChanged"); });
    
    item->setParent(window->contentItem());
    item->setParentItem(window->contentItem());
    
    QObject::connect(item, &QQuickItem::activeFocusChanged, []() { new Object("activeFocusChanged"); }); // does not leak, obv. coming from event loop
    
    item->setVisible(false);
    
    QObject::connect(item, &QQuickItem::visibleChanged, []() { new Object("visibleChanged"); });
    
    item->setVisible(true);
    window->show();
}

@interface ApplicationDelegate : NSObject <NSApplicationDelegate>
@end

@implementation ApplicationDelegate
- (void)applicationDidFinishLaunching: (NSNotification *)notification
{
    setupQtApplication();
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
    return YES;
}
@end

int main(int argc, const char** argv)
{
#if 1
    const Timer timer(0.5, []() {
        auto object = new Object("timer");
    });
    
    @autoreleasepool {
        [NSApplication sharedApplication].delegate = [[ApplicationDelegate new] autorelease];
        return NSApplicationMain(argc, argv);
    }
#else
    setupQtApplication();
    return qGuiApp->exec();
#endif
}
