
#include <qapplication.h>
#include <qmutex.h>

#include <qtjava/QtSupport.h>
#include <qtjava/QtUtils.h>
#include "QtUtils.moc"

#define SYNC_EVENT1  60001
#define SYNC_EVENT2  60002
#define ASYNC_EVENT  60003

class QRunEvent : public QCustomEvent 
{

public:
    QRunEvent(int type, jobject r) :
        QCustomEvent(type), 
        runnable(r),
        res(0),
        lock(0)
    {}

    jobject runnable;
    jobject *res;
    QMutex *lock;
};


QtUtils* QtUtils::gUtils = 0;


QtUtils::QtUtils()
{
}

QtUtils::~QtUtils()
{
}

void QtUtils::postSync(JNIEnv* env, jobject runnable) {
    QMutex lock;
    QRunEvent *e = new QRunEvent(SYNC_EVENT1, env->NewGlobalRef(runnable));
    e->lock = &lock;
    lock.lock();

    //post the event to the QT-UI thread
    //and trigger its processing
    QApplication::postEvent(this, e);
    QApplication::sendPostedEvents();
    
    //the lock is gained only 
    //after executing the runnable    
    lock.lock();
    lock.unlock();
}

jobject QtUtils::postSyncRet(JNIEnv* env, jobject runnable) {
    QMutex lock;
    jobject res;
    QRunEvent *e = new QRunEvent(SYNC_EVENT2, env->NewGlobalRef(runnable));
    e->lock = &lock;
    e->res = &res;
    lock.lock();

    //post the event to the QT-UI thread and
    //trigger its processing
    QApplication::postEvent(this, e);
    QApplication::sendPostedEvents();
    
    //the lock is gained only
    //after executing the runnable
    lock.lock();
    lock.unlock();

    jobject lres = env->NewLocalRef(res);
    env->DeleteGlobalRef(res);
    
    return lres;
}

void QtUtils::postAsync(JNIEnv *env, jobject runnable) {
    QRunEvent *e = new QRunEvent(ASYNC_EVENT, env->NewGlobalRef(runnable));
    //post the event to the QT-UI thread, 
    //it will be processed in the next Qt-loop iterations
    QApplication::postEvent(this, e);
}
    
void QtUtils::customEvent(QCustomEvent *e) {
    if (e->type() >= SYNC_EVENT1 && e->type() <= ASYNC_EVENT) {
        QRunEvent *re = (QRunEvent*) e;
        JNIEnv *env = QtSupport::GetEnv();
        jclass cls = env->GetObjectClass(re->runnable);
        if (re->type() == SYNC_EVENT1) {
            jmethodID m = env->GetMethodID(cls, "run", "()V");
            if (m!=0) {
                env->CallObjectMethod(re->runnable, m);
             }
             re->lock->unlock();
        }
        else if (re->type() == SYNC_EVENT2) {
            jmethodID m = env->GetMethodID(cls, "run", "()Ljava/lang/Object;");
            if (m!=0) {
                jobject res = env->CallObjectMethod(re->runnable, m);
                *(re->res) = env->NewGlobalRef(res);
            }
            re->lock->unlock();
        }
        else {
            jmethodID m = env->GetMethodID(cls, "run", "()V");
            if (m!=0) {
                env->CallVoidMethod(re->runnable, m);
            }
        }
        //runnable is no longer needed
        env->DeleteGlobalRef(re->runnable);
    }
}

JNIEXPORT jobject JNICALL 
Java_org_kde_qt_QtUtils_execSyncOnGUIThread__Lorg_kde_qt_QtUtils_00024Compute_2
  (JNIEnv *env, jclass, jobject runnable)
{
    if (!runnable) return 0;
    if (QtUtils::gUtils==0) QtUtils::gUtils = new QtUtils(); 
    return QtUtils::gUtils->postSyncRet(env, runnable);
}

JNIEXPORT void JNICALL 
Java_org_kde_qt_QtUtils_execSyncOnGUIThread__Ljava_lang_Runnable_2
  (JNIEnv *env, jclass, jobject runnable)
{
    if (!runnable) return;
    if (QtUtils::gUtils==0) QtUtils::gUtils = new QtUtils(); 
    QtUtils::gUtils->postSync(env, runnable);
}

JNIEXPORT void JNICALL
Java_org_kde_qt_QtUtils_execAsyncOnGUIThread
  (JNIEnv *env, jclass, jobject runnable)
{
    if (!runnable) return;
    if (QtUtils::gUtils==0) QtUtils::gUtils = new QtUtils();
    QtUtils::gUtils->postAsync(env, runnable);
}

