#include "et9keyboardDatabase.h"
#include <malloc.h>


#include <stdlib.h>

#include "et9api.h"

#include "log.h"


#define DEFAULT_PROTECTION_AREA_RATIO_WIDTH 0.6f
#define DEFAULT_PROTECTION_AREA_RATIO_HEIGHT 1.0f
int ET9KEY_OEM;
float PROTECTION_AREA_RATIO_WIDTH = 0.0f;
float PROTECTION_AREA_RATIO_HEIGHT = 0.0f;
short PROTECTION_AREA_VERTICAL_GAP = 0;
short PROTECTION_AREA_HORIZONTAL_GAP = 0;

ET9STATUS SetProtectPercent(float fWidthRatio, float fHeightRatio) {
	jshort wStatus = ET9STATUS_ERROR;
	PROTECTION_AREA_RATIO_WIDTH = fWidthRatio;
	PROTECTION_AREA_RATIO_HEIGHT = fHeightRatio;
    LOGD("PROTECTION_AREA_RATIO_WIDTH() : %f", PROTECTION_AREA_RATIO_WIDTH);
	LOGD("PROTECTION_AREA_RATIO_HEIGHT() : %f", PROTECTION_AREA_RATIO_HEIGHT);
	return wStatus = ET9STATUS_NONE;
}

ET9STATUS SetProtectionGap(short vertical, short horizontal) {
	PROTECTION_AREA_VERTICAL_GAP = vertical;
	PROTECTION_AREA_HORIZONTAL_GAP = horizontal;
	LOGD("PROTECTION_AREA_VERTICAL_GAP : %d", PROTECTION_AREA_VERTICAL_GAP);
	LOGD("PROTECTION_AREA_HORIZONTAL_GAP : %d", PROTECTION_AREA_HORIZONTAL_GAP);
	return ET9STATUS_NONE;
}

ET9STATUS ET9SetKeyboardDatabase(JNIEnv *env, jclass jcls,jobject jXT9KeyboardDatabase) {
	    jshort wStatus = ET9STATUS_ERROR;
	    CacheJavaFieldAndClassIDs(env, jXT9KeyboardDatabase);


	    int kdbId = (*env)->GetIntField(env, jXT9KeyboardDatabase, g_sXT9KeyboardDatabase.id);
	    int kdbPage = (*env)->GetIntField(env, jXT9KeyboardDatabase, g_sXT9KeyboardDatabase.page);
	    int layoutWidth = (*env)->GetIntField(env,  jXT9KeyboardDatabase, g_sXT9KeyboardDatabase.width);
	    int layoutHeight = (*env)->GetIntField(env,  jXT9KeyboardDatabase, g_sXT9KeyboardDatabase.height);

	    ET9KEY_OEM = 0x0B78;

	    // list of keys
	    jobject keyList = (*env)->GetObjectField(env, jXT9KeyboardDatabase, g_sXT9KeyboardDatabase.keys);
		
	    int keyCount = (*env)->CallIntMethod(env, keyList, g_sListClass.size);

	    setProperties(kdbId, kdbPage, layoutWidth, layoutHeight, keyCount);
	    
	    int i = 0;

	    // add keys to the current keyboard database
	    for (i ; i < keyCount; i++) {
	        // get the key object from the list
	       
	        jobject keyObject = (*env)->CallObjectMethod(env, keyList, g_sListClass.get, i);

	        // get the char[] codes define in the class object
	        jobject codesObject = (*env)->GetObjectField(env, keyObject, g_sXT9KeyboardDatabase_Key.codes);
		    jobject shfitCodesObject = (*env)->GetObjectField(env, keyObject, g_sXT9KeyboardDatabase_Key.shiftCodes);

	        // deference it
	        
	        jintArray* intArray = /*reinterpret_cast<jintArray*>*/(&codesObject);
	        jint* codes = (*env)->GetIntArrayElements(env, *intArray, NULL);
	        int len = (*env)->GetArrayLength(env, *intArray);

		    jintArray* shiftCodesArray = (&shfitCodesObject);
		    jint* shiftCodes = (*env)->GetIntArrayElements(env, *shiftCodesArray, NULL);
		    int shiftLen = (*env)->GetArrayLength(env, *shiftCodesArray);

	        int type = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.type);
	        int x = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.x);
	        int y = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.y);
	        int w = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.width);
	        int h = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.height);
            int biasOffset = (*env)->GetIntField(env, keyObject, g_sXT9KeyboardDatabase_Key.biasOffset);

	        addKey(type, codes, len, shiftCodes, shiftLen, x, y, x + w - 1, y + h - 1, biasOffset, &ET9KEY_OEM);

	        if(ET9KEY_OEM == 0x0B80){
	        	ET9KEY_OEM = 0x0B50;
	       	}

	       	if(ET9KEY_OEM == 0x0B55){
	       	  	ET9KEY_OEM = 0xAA37;
	       	}

	        (*env)->ReleaseIntArrayElements(env, *intArray, codes, 0);
                (*env)->ReleaseIntArrayElements(env, *shiftCodesArray, shiftCodes, 0);
	        (*env)->DeleteLocalRef(env, keyObject);
	    }

	return wStatus;
}

ET9STATUS CacheJavaFieldAndClassIDs(JNIEnv *env,  jobject jXT9KeyboardDatabase)
{
	jshort wStatus = ET9STATUS_NONE;
	jclass cls;
	cls = (*env)->GetObjectClass(env, jXT9KeyboardDatabase);

	if (g_sXT9KeyboardDatabase.clazz == NULL) {
	        g_sXT9KeyboardDatabase.clazz = (*env)->FindClass(env, "com/samsung/android/wristboard/predictionengine/core/xt9/XT9KeyboardDatabase");
	        g_sXT9KeyboardDatabase.id = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase.clazz, "id", "I");
	        g_sXT9KeyboardDatabase.page = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase.clazz, "page", "I");
	        g_sXT9KeyboardDatabase.width = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase.clazz, "width", "I");
	        g_sXT9KeyboardDatabase.height = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase.clazz, "height", "I");
	        g_sXT9KeyboardDatabase.keys = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase.clazz, "keys", "Ljava/util/List;");
		
	        g_sXT9KeyboardDatabase_Key.clazz = (*env)->FindClass(env, "com/samsung/android/wristboard/predictionengine/core/xt9/XT9KeyboardDatabase$XT9Key");
	        g_sXT9KeyboardDatabase_Key.codes = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "codes", "[I");		 
	        g_sXT9KeyboardDatabase_Key.type = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz ,"type", "I");			
	        g_sXT9KeyboardDatabase_Key.x = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "x", "I");		   
	        g_sXT9KeyboardDatabase_Key.y = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "y", "I");			
	        g_sXT9KeyboardDatabase_Key.width = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "width", "I");			
	        g_sXT9KeyboardDatabase_Key.height = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "height", "I");			
	        g_sXT9KeyboardDatabase_Key.biasOffset = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "biasOffset", "I");			
	    	g_sXT9KeyboardDatabase_Key.shiftCodes = (*env)->GetFieldID(env, g_sXT9KeyboardDatabase_Key.clazz , "shiftCodes", "[I");			
	}

	if (g_sListClass.clazz == NULL) {
		g_sListClass.clazz = (*env)->FindClass(env, "java/util/List");
		g_sListClass.size  = (*env)->GetMethodID(env, g_sListClass.clazz, "size", "()I");
		g_sListClass.get   = (*env)->GetMethodID(env, g_sListClass.clazz, "get", "(I)Ljava/lang/Object;");
		g_sListClass.add   = (*env)->GetMethodID(env, g_sListClass.clazz, "add", "(Ljava/lang/Object;)Z");			
	}
	return wStatus;
}

ET9STATUS dynamicKdbLoadCallback(ET9KDBInfo *pKDBInfo, const ET9U32 dwKdbNum, const ET9U16 swPageNum){
	ET9STATUS status = ET9STATUS_READ_DB_FAIL;

	if ( (ET9U32) g_mKeyboardDatabase.kdbId == dwKdbNum && g_mKeyboardDatabase.kdbPage == swPageNum) {
	    	status = ET9KDB_Load_Reset(pKDBInfo);
	    	if (status != ET9STATUS_NONE) {
 		} else {
	    		ET9U8 primaryId   = (ET9U8)( g_mKeyboardDatabase.kdbId & 0x00ff);
	    		ET9U8 secondaryId = (ET9U8)(( g_mKeyboardDatabase.kdbId) >> 8);
	    		ET9U16 width = (ET9U16) g_mKeyboardDatabase.width;
	    		ET9U16 height = (ET9U16) g_mKeyboardDatabase.height;
	    		ET9U16 totalPages = (ET9U16) g_mKeyboardDatabase.totalPages;

				status = ET9KDB_Load_SetProperties(pKDBInfo, 1, 0, primaryId, secondaryId, width, height, totalPages);
				
				if (status != ET9STATUS_NONE) {
		    		
			} else {
					int i = 0 ;
					for (i = 0; i <  g_mKeyboardDatabase.numbKeys; i++) {
						mPkey key = & g_mKeyboardDatabase.keys[i];
			    		
						status = ET9KDB_Load_AddKey(pKDBInfo, key->index, key->type, key->left, key->top, key->right, key->bottom,
								 key->charsCount, key->chars);

						if (status != ET9STATUS_NONE) {
							__android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AddKey - key->index : %d, key->type %d, error : %d", key->index, key->type, status);
							return status;
						}


						status = ET9KDB_Load_AttachBias(pKDBInfo, key->biasOffset, PROTECTION_AREA_HORIZONTAL_GAP);
						if (status != ET9STATUS_NONE) {
							__android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachBias, error : %d",status);
							return status;
						}

						if (key->shiftedCharsCount > 0) {
							status = ET9KDB_Load_AttachShiftedChars(pKDBInfo, key->shiftedCharsCount, key->shiftedChars);
							if (status != ET9STATUS_NONE) {
								__android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachShiftedChars - key->index : %d, key->type %d, error : %d", key->index, key->type, status);
								return status;
							}
						}

						// if it is naragul keyboard...
						if (primaryId == 0x12 && secondaryId == 0x12
								&& (key->type == ET9LKT_REGIONAL || key->type == ET9LKT_NONREGIONAL)) {
							status = ET9KDB_Load_AttachMultitapInfo(pKDBInfo, key->charsCount, key->chars, 0, NULL);
							if (status != ET9STATUS_NONE) {
								__android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachMultitapInfo - key->charsCount : %d, key->type %d, error : %d", key->charsCount, key->type, status);
								break;
							}
						} else if(primaryId == 0x12 && secondaryId == 0x07 && key->type == ET9LKT_REGIONAL) { 		//single-vowel
						    status = ET9KDB_Load_AttachMultitapInfo(pKDBInfo, key->charsCount, key->chars, 0, NULL);
							if (status != ET9STATUS_NONE) {
							    __android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachMultitapInfo - key->charsCount : %d, key->type %d, error : %d", key->charsCount, key->type, status);
								break;
							}
						} else if(primaryId == 0x12 && secondaryId == 0x01 && key->type == ET9LKT_NONREGIONAL) { 		//Chunjiin plus
						    status = ET9KDB_Load_AttachMultitapInfo(pKDBInfo, key->charsCount, key->chars, 0, NULL);
							if (status != ET9STATUS_NONE) {
							    __android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachMultitapInfo - key->charsCount : %d, key->type %d, error : %d", key->charsCount, key->type, status);
								break;
							}
						} else if(primaryId == 0x12 && secondaryId == 0x06 && key->type == ET9LKT_NONREGIONAL) { 		//Chunjiin
							status = ET9KDB_Load_AttachMultitapInfo(pKDBInfo, key->charsCount, key->chars, 0, NULL);
							if (status != ET9STATUS_NONE) {
								__android_log_print(ANDROID_LOG_DEBUG,"SamsungIME","ET9KDB_Load_AttachMultitapInfo - key->charsCount : %d, key->type %d, error : %d", key->charsCount, key->type, status);
								break;
							}
						}
					}
				}
			if (status == ET9STATUS_NONE) {
				ET9U32 keypadType = dwKdbNum & 0x0f00;
				ET9KDB_Load_SetSmartTouch(pKDBInfo, TRUE, 1.0f);
				if (keypadType == ET9SKIDQwerty || keypadType == ET9SKIDATQwerty) {
					if(PROTECTION_AREA_RATIO_WIDTH == 0.0f) {
						PROTECTION_AREA_RATIO_WIDTH =DEFAULT_PROTECTION_AREA_RATIO_WIDTH;
					}
					if(PROTECTION_AREA_RATIO_HEIGHT == 0.0f) {
						PROTECTION_AREA_RATIO_HEIGHT = DEFAULT_PROTECTION_AREA_RATIO_HEIGHT;
					}
                    LOGD("PROTECTION_AREA_RATIO_WIDTH() - call back : %f", PROTECTION_AREA_RATIO_WIDTH);
					LOGD("PROTECTION_AREA_RATIO_HEIGHT() - call back : %f", PROTECTION_AREA_RATIO_HEIGHT);
				    ET9KDB_Load_SetSmartTouchProtectiveArea(pKDBInfo, PROTECTION_AREA_RATIO_WIDTH, PROTECTION_AREA_RATIO_HEIGHT);
				}
			}
	    	}
	} else {
    }

    return (status);
}

ET9STATUS setProperties(int kdbId, int kdbPage, unsigned short width, unsigned short height, unsigned short maxKeys)
{
    g_mKeyboardDatabase.hashCode = width + height + maxKeys + 1;

    g_mKeyboardDatabase.numbKeys = 0;
    g_mKeyboardDatabase.kdbId = kdbId;
    g_mKeyboardDatabase.kdbPage= kdbPage;
    g_mKeyboardDatabase.width = width;
    g_mKeyboardDatabase.height = height;
    g_mKeyboardDatabase.totalPages = 0x3fff;

    free(g_mKeyboardDatabase.keys);

    if (maxKeys > 0) {
        g_mKeyboardDatabase.keys = (mkey*)calloc(maxKeys, sizeof(mkey));
    }

    g_mKeyboardDatabase.maxKeys = maxKeys;
    g_mKeyboardDatabase.numbKeys = 0;

    return ET9STATUS_NONE;
}

ET9STATUS addKey(int type, int* chars, unsigned short charsCount, int* shiftedChars, unsigned short shiftedCharsCount, unsigned short left, unsigned short top, unsigned short right, unsigned short bottom, unsigned short biasOffset, int* oemKeyCode)
{
    if (g_mKeyboardDatabase.numbKeys > g_mKeyboardDatabase.maxKeys) {

        return ET9STATUS_ERROR;
    }

    if (g_mKeyboardDatabase.keys == 0) {

        return ET9STATUS_ERROR;
    }

    ET9LOADKEYTYPE keyType;
    int i = 1;

    if (type != -1) {
		
        keyType = (ET9LOADKEYTYPE)type;
      

    } else if (ET9IsFunctionKeySymbol(chars[0])) {
        keyType = ET9LKT_FUNCTION;


        // Core doesn't allow mixing function keys with other key codes
        for (i = 1; i < charsCount; i++) {
            if (!ET9IsFunctionKeySymbol(chars[i])) {
                
                charsCount = i;
                break;
            }
        }

    } else {
        ET9U8 charClass = ET9GetSymbolClass(chars[0]);
        switch (charClass) {
            case ET9SYMALPHA:
				
                keyType = ET9LKT_REGIONAL;
                break;

            default:
				
                keyType = ET9LKT_NONREGIONAL;
                break;
        }
        

        // Core doesn't allow mixing function keys with other key codes
        
        for ( i =1; i < charsCount; i++) {
            if (ET9IsFunctionKeySymbol(chars[i])) {

                charsCount = i;
                break;
            }
        }
    }

    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].index = g_mKeyboardDatabase.numbKeys;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].left = left;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].top = top;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].right = right;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].bottom = bottom;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].type = keyType;
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].biasOffset = biasOffset;

    g_mKeyboardDatabase.hashCode = g_mKeyboardDatabase.hashCode + keyType + (bottom - top) + (right - left);

    for (i = 0; i < charsCount && i < 64; i++) {
        g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] = (unsigned short)chars[i];
        g_mKeyboardDatabase.hashCode = g_mKeyboardDatabase.hashCode + g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i];
    }
	if(g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].type == ET9LKT_FUNCTION) {	
		for (i = 0; i < charsCount && i < 64; i++) {
			if (g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] == 65136) {
				g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] = 4068;
			} else if (g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] != 0x0020) {
       	 	    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] = (*oemKeyCode)++ ;
		    }
			g_mKeyboardDatabase.hashCode = g_mKeyboardDatabase.hashCode + g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i];
  		  }
   		 g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].charsCount = 1;
	} else {
		for (i = 0; i < charsCount && i < 64; i++) {
	        	g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i] = (unsigned short)chars[i];
	       	 g_mKeyboardDatabase.hashCode = g_mKeyboardDatabase.hashCode + g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].chars[i];
    		}
		 g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].charsCount = i;
	}

    int count = 0;
    for (i = 0; i < shiftedCharsCount && i < 64; i++) {
        if (!ET9IsFunctionKeySymbol((unsigned short)shiftedChars[i]) && shiftedChars[i] != 0) {
            g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].shiftedChars[count] = (unsigned short)shiftedChars[i];
            g_mKeyboardDatabase.hashCode = g_mKeyboardDatabase.hashCode + g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].shiftedChars[count];
            count++;
        }
    }
    g_mKeyboardDatabase.keys[g_mKeyboardDatabase.numbKeys].shiftedCharsCount = count;

    g_mKeyboardDatabase.numbKeys++;

    return ET9STATUS_NONE;
}
