/**
@file jni_dlm.cpp
Swype Connected SDK (Android Connected SDK - AC-SDK)
(c) Copyright 2013 Nuance Communications, Inc All Rights Reserved
*/

#include <jni.h>
#include "et9api.h"

#define C_DECL extern "C"

struct JXT9DlmDB {
    JNIEnv* env;
    jmethodID method;
    jobject callbackRef;

    JXT9DlmDB() : env(NULL) {
    }
    ~JXT9DlmDB() {
        if (env != NULL) {
            if (callbackRef != NULL) {
                env->DeleteGlobalRef(callbackRef);
            }
            env = NULL;
        }
    }
};

#ifdef ET9_ALPHABETIC_MODULE
#undef API
#define API(fun) Java_com_nuance_dlm_AlphaInput_##fun

extern "C" ET9AWLingInfo* getAlphaLingInfo();
/**
* getAlphaLingInfo()
*
* This should return the current ET9LingInfo for the current ling info 
*
*/

static JXT9DlmDB g_AlphaDlmDB_onEventCallback;

ET9STATUS DLMAlphaEventHandlerCallback(void* const pEventHandlerInfo, ET9U8 const * const pbBuf,
        const ET9U32 dwBufLen) {

    //LOGV("DLMAlphaEventHandlerCallback Type: %d, length: %d", (dwBufLen > 0) ? pbBuf[0] : -1, dwBufLen);
    JXT9DlmDB* dlmDB = (JXT9DlmDB*)pEventHandlerInfo;
    if (!dlmDB) {
        return ET9STATUS_ERROR;
    }

    jbyteArray event = dlmDB->env->NewByteArray(dwBufLen);
    dlmDB->env->SetByteArrayRegion(event, 0, dwBufLen, (jbyte*)pbBuf);
    dlmDB->env->CallVoidMethod(dlmDB->callbackRef, dlmDB->method, 
        event, ET9_SYNC_IsHighPriorityEvent(pbBuf, dwBufLen));
    dlmDB->env->DeleteLocalRef(event);

    return ET9STATUS_NONE;
}

C_DECL jint API(acAlphaExportAsEvent)(JNIEnv* env, jobject thiz, jboolean usingCategory, jint category) {
    //LOGV("acAlphaExportAsEvent");
    ET9AWLingInfo* info = getAlphaLingInfo();
    if (info == NULL) {
        return -1;
    }
    return ET9AWDLMExportAsEvents(info, (ET9BOOL)usingCategory, (ET9U16)category);
}

C_DECL jint API(acAlphaRegisterEventHandlerCallback)(JNIEnv* env, jobject thiz) {
    //LOGV("acAlphaRegisterEventHandlerCallback 1");
    ET9AWLingInfo* info = getAlphaLingInfo();
    g_AlphaDlmDB_onEventCallback.env = env;
    if (info == NULL || g_AlphaDlmDB_onEventCallback.env == NULL) {
        return -1;
    }

    if (g_AlphaDlmDB_onEventCallback.callbackRef != NULL) {
        g_AlphaDlmDB_onEventCallback.env->DeleteGlobalRef(g_AlphaDlmDB_onEventCallback.callbackRef);
    }
    g_AlphaDlmDB_onEventCallback.callbackRef = env->NewGlobalRef(thiz);
    jclass c = env->GetObjectClass(thiz);
    g_AlphaDlmDB_onEventCallback.method = env->GetMethodID(c, "onEventCallback", "([BZ)V");

    return ET9AWDLMRegisterForEvents(info, &DLMAlphaEventHandlerCallback, (void*)&g_AlphaDlmDB_onEventCallback);
}

C_DECL jint API(acAlphaProcessEvent)(JNIEnv* env, jobject thiz, jbyteArray jevent) {
    //LOGV("acAlphaProcessEvent");
    ET9AWLingInfo* info = getAlphaLingInfo();
    if (info == NULL) {
        return -1;
    }
    jsize len = env->GetArrayLength(jevent);
    jbyte* event = env->GetByteArrayElements(jevent, NULL);

    ET9STATUS status = ET9AWDLMHandleEvents(info, (ET9U8*)event, (ET9U16) len);

    env->ReleaseByteArrayElements(jevent, event, JNI_ABORT);

    return status;
}

C_DECL jint API(acAlphaDeleteCategory)(JNIEnv* env, jobject thiz, jint category) {
    //LOGV("acAlphaDeleteCategory");
    ET9AWLingInfo* info = getAlphaLingInfo();
    if (info == NULL) {
        return -1;
    }
    return ET9AWDLMDeleteCategory(info, (ET9U16)category);
}

C_DECL jint API(acAlphaDeleteCategoryLanguage)(JNIEnv* env, jobject thiz, jint category, jint languageId) {
    //LOGV("acAlphaDeleteCategoryLanguage");
    ET9AWLingInfo* info = getAlphaLingInfo();
    if (info == NULL) {
        return -1;
    }
    return ET9AWDLMDeleteCategoryLanguage(info, (ET9U16)category, (ET9U16)languageId);
}

#endif


#ifdef ET9_KOREAN_MODULE
#undef API
#define API(fun) Java_com_nuance_dlm_KoreanInput_##fun

extern "C" ET9KLingInfo* getKoreanLingInfo();

static JXT9DlmDB g_KoreanDlmDB_onEventCallback;

ET9STATUS DLMKoreanEventHandlerCallback(void* const pEventHandlerInfo, ET9U8 const * const pbBuf,
        const ET9U32 dwBufLen) {

    //LOGV("DLMKoreanEventHandlerCallback Type: %d, length: %d", (dwBufLen > 0) ? pbBuf[0] : -1, dwBufLen);
    JXT9DlmDB* dlmDB = (JXT9DlmDB*)pEventHandlerInfo;
    if (!dlmDB) {
        return ET9STATUS_ERROR;
    }

    jbyteArray event = dlmDB->env->NewByteArray(dwBufLen);
    dlmDB->env->SetByteArrayRegion(event, 0, dwBufLen, (jbyte*)pbBuf);
    dlmDB->env->CallVoidMethod(dlmDB->callbackRef, dlmDB->method,
        event, ET9_SYNC_IsHighPriorityEvent(pbBuf, dwBufLen));
    dlmDB->env->DeleteLocalRef(event);

    return ET9STATUS_NONE;
}

C_DECL jint API(acKoreanExportAsEvent)(JNIEnv* env, jobject thiz, jboolean usingCategory, jint category) {
    //LOGV("acKoreanExportAsEvent");
    ET9KLingInfo* info = getKoreanLingInfo();
    if (info == NULL) {
        return -1;
    }
    return ET9KDLMExportAsEvents(info, (ET9BOOL)usingCategory, (ET9U16)category);
}

C_DECL jint API(acKoreanRegisterEventHandlerCallback)(JNIEnv* env, jobject thiz) {
    //LOGV("acKoreanRegisterEventHandlerCallback 1");
    ET9KLingInfo* info = getKoreanLingInfo();
    g_KoreanDlmDB_onEventCallback.env = env;
    if (info == NULL || g_KoreanDlmDB_onEventCallback.env == NULL) {
        return -1;
    }

    if (g_KoreanDlmDB_onEventCallback.callbackRef != NULL) {
        g_KoreanDlmDB_onEventCallback.env->DeleteGlobalRef(g_KoreanDlmDB_onEventCallback.callbackRef);
    }
    g_KoreanDlmDB_onEventCallback.callbackRef = env->NewGlobalRef(thiz);
    jclass c = env->GetObjectClass(thiz);
    g_KoreanDlmDB_onEventCallback.method = env->GetMethodID(c, "onEventCallback", "([BZ)V");

    return ET9KDLMRegisterForEvents(info, &DLMKoreanEventHandlerCallback, (void*)&g_KoreanDlmDB_onEventCallback);
}

C_DECL jint API(acKoreanProcessEvent)(JNIEnv* env, jobject thiz, jbyteArray jevent) {
    //LOGV("acKoreanProcessEvent");
    ET9KLingInfo* info = getKoreanLingInfo();
    if (info == NULL) {
        return -1;
    }
    jsize len = env->GetArrayLength(jevent);
    jbyte* event = env->GetByteArrayElements(jevent, NULL);

    ET9STATUS status = ET9KDLMHandleEvents(info, (ET9U8*)event, (ET9U16) len);

    env->ReleaseByteArrayElements(jevent, event, JNI_ABORT);

    return status;
}

C_DECL jint API(acKoreanDeleteCategory)(JNIEnv* env, jobject thiz, jint category) {
    //LOGV("acKoreanDeleteCategory");
    ET9KLingInfo* info = getKoreanLingInfo();
    if (info == NULL) {
        return -1;
    }
    return ET9KDLMDeleteCategory(info, (ET9U16)category);
}

C_DECL jint API(acKoreanDeleteCategoryLanguage)(JNIEnv* env, jobject thiz, jint category, jint languageId) {
    //LOGV("acKoreanDeleteCategoryLanguage");
    return API(acKoreanDeleteCategory)(env, thiz, category);
}

#endif

#ifdef ET9_CHINESE_MODULE
#undef API
#define API(fun) Java_com_nuance_dlm_ChineseInput_##fun

extern "C" ET9CPLingInfo* getChineseLingInfo();

static JXT9DlmDB g_ChineseDlmDB_onEventCallback;

ET9STATUS DLMChineseEventHandlerCallback(void* const pEventHandlerInfo, ET9U8 const * const pbBuf,
        const ET9U32 dwBufLen) {

    //LOGV("DLMChineseEventHandlerCallback Type: %d, length: %d", (dwBufLen > 0) ? pbBuf[0] : -1, dwBufLen);
    return ET9STATUS_NONE;
}

C_DECL jint API(acChineseExportAsEvent)(JNIEnv* env, jobject thiz, jboolean usingCategory, jint category) {
    //LOGV("acChineseExportAsEvent");
    return -1;
}

C_DECL jint API(acChineseRegisterEventHandlerCallback)(JNIEnv* env, jobject thiz) {
    //LOGV("acChineseRegisterEventHandlerCallback");
    ET9CPLingInfo* info = getChineseLingInfo();
    g_ChineseDlmDB_onEventCallback.env = env;
    if (info == NULL || g_ChineseDlmDB_onEventCallback.env == NULL) {
        return -1;
    }
    return -1;
}

C_DECL jint API(acChineseProcessEvent)(JNIEnv* env, jobject thiz, jbyteArray jevent) {
    //LOGV("acChineseProcessEvent");
    ET9CPLingInfo* info = getChineseLingInfo();
    if (info == NULL) {
        return -1;
    }
    jsize len = env->GetArrayLength(jevent);
    jbyte* event = env->GetByteArrayElements(jevent, NULL);

    ET9STATUS status = ET9CPSyncHandleEvents(info, (ET9U8*)event, (ET9U16) len);

    env->ReleaseByteArrayElements(jevent, event, JNI_ABORT);

    return status;
}

C_DECL jint API(acChineseDeleteCategory)(JNIEnv* env, jobject thiz, jint category) {
    //LOGV("acChineseDeleteCategory");
    return -1;
}

C_DECL jint API(acChineseDeleteCategoryLanguage)(JNIEnv* env, jobject thiz, jint category, jint languageId) {
    //LOGV("acChineseDeleteCategoryLanguage");
    return API(acChineseDeleteCategory)(env, thiz, category);
}

#endif
