/*
 * Copyright (c) 2011~2012 Samsung Electronics Co., Ltd.
 * All rights reserved.
 *
 * This software is a confidential and proprietary information of Samsung
 * Electronics, Inc. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with the terms
 * of the license agreement you entered into with Samsung Electronics.
 */

/**
 * This file contains the Chinese XT9 integration functions for Samsung IME
 *
 * @file       mdbConvert.c
 * @author     maming (m.ma@samsung.com)
 * @version    0.1
 * @brief      This file contains XT9 decoder service for mdb words.
 * 
 ******************************** REVISION HISTORY ***************************************

 * MM/DD/YYYY     Author            Email                Brief Description
   02/27/2013      maming		     m.ma                initial version.
   04/02/2015      Barath            barathraj.kr        Porting hotword
 *****************************************************************************************
 */

#include "et9api.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

/*

#ifdef NDK_DEBUG
#include <android/log.h>
#else
#include <cutils/log.h>
#include <utils/Log.h>
#endif

#define TAG "et9cphotword

#ifndef BSTDEBUG
#define BSTLOGD(...)   ((void)0)
#else
#define BSTLOGD(...)   __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#endif

#ifdef NDK_DEBUG
#define BSTLOGI(...)   __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#else
#define BSTLOGI(...)   ((void)ALOG(LOG_INFO, TAG, __VA_ARGS__))
#endif
*/

#define LDB_PATH "xt9db/"

/*-----------------------------------------------------------------------------
 *
 *  Function name   : xt9Hotword_Init
 *
 *  Description     : ini the xt9 core 
 *
 *  Pass            : none
 *  
 *  Return          : 0-ok,else not
 *
 *---------------------------------------------------------------------------*/
typedef struct T9CP_DATA {
	ET9WordSymbInfo sWordSymbInfo;
	ET9CPLingInfo sCPLingInfo; /* field info for Chinese XT9 chinese */
	ET9CPDBREADCALLBACK ET9CPLdbReadData;
	ET9U8 *pLdbData;
	ET9U32 nLDBSize;
	ET9U8 *pMdbData;
	AAssetManager *pAssetMgr;
	ET9BOOL bInit;
} xt9Hotword;

xt9Hotword gxt9HotwordData;

//the following three file Name has the full path.
const char *pSimplifiedLDBName = NULL;
const char *pTraditionalHKName = NULL;
const char *pTraditionalTWName = NULL;

//21k can hold 1000 words with average length 3.9
//Now xt9 can not return buffer full status,
//so set to 40k to make sure the size is large enough to hold 1000 hot words.
//after upgrade to XT9 V9.4, we can set the buffer to 21k.
static const ET9U32 g_mdbDataSize = 40*1024;
static const char *g_lang_zh_cn = "zh_CN";
static const char *g_lang_zh_hk = "zh_HK";
static const char *g_lang_zh_tw = "zh_TW";

/* only local methods are added here */
static ET9STATUS xt9Hotword_ldbRead(ET9CPLingInfo *pLingInfo, ET9U8 * ET9FARDATA *ppbSrc,  ET9U32 *pdwSizeInBytes);
static ET9STATUS xt9Hotword_activeMdb();
static ET9STATUS xt9Hotword_LoadLDB(ET9U32 wLdbNum);
static int xt9Hotword_mdbReadData(const char *pfileName, ET9U8** ppbSrc, ET9U32* pdwSizeInBytes);
static int xt9Hotword_loadFile(char* file, ET9U8** data, ET9U32* size);
static ET9STATUS xt9Hotword_getLdbBuffer(ET9CPLingInfo *pLingInfo, ET9U8 * ET9FARDATA *ppbSrc,ET9U32 *pdwSizeInBytes);

#define XT9_HOTWORD_CHECK_INIT(bInit)        \
    if (bInit == 0)  return(ET9STATUS_NO_INIT);

ET9STATUS xt9Hotword_Init(AAssetManager *pAssetMgr)
{
    ET9STATUS status = ET9STATUS_NONE;

	//BSTLOGD(" xt9Hotword Init(): =====Start===== ");

	memset(&gxt9HotwordData, 0, sizeof(xt9Hotword));

	gxt9HotwordData.pAssetMgr = pAssetMgr;

	gxt9HotwordData.pMdbData = (ET9U8 *)malloc(g_mdbDataSize);
	
	if(gxt9HotwordData.pMdbData != NULL)	{
		
		memset(gxt9HotwordData.pMdbData, 0x00, g_mdbDataSize);
		
		status = ET9CPSysInit(&gxt9HotwordData.sCPLingInfo, &gxt9HotwordData.sWordSymbInfo, NULL);
		
		if (ET9STATUS_NONE != status) {
			free(gxt9HotwordData.pMdbData);
			gxt9HotwordData.pMdbData = NULL;
			return status;
		}

		gxt9HotwordData.sCPLingInfo.dwLdbNum = ET9PLIDChineseSimplified;
		gxt9HotwordData.ET9CPLdbReadData = &xt9Hotword_getLdbBuffer;
		//status = xt9Hotword_LoadLDB(ET9PLIDChineseSimplified);

	} else	{
		status = ET9STATUS_FULL;
	}

	return status;
}

ET9STATUS xt9Hotword_Exit()
{
	
	if(gxt9HotwordData.pLdbData) {
		free(gxt9HotwordData.pLdbData);
		gxt9HotwordData.pLdbData = NULL;
		gxt9HotwordData.nLDBSize = 0;
	}
	
	if(gxt9HotwordData.pMdbData) {
		free(gxt9HotwordData.pMdbData);
		gxt9HotwordData.pMdbData = NULL;
	}

	gxt9HotwordData.bInit = 0;

	return ET9STATUS_NONE;
}

ET9STATUS xt9Hotword_setLDBFileName(const char *dbFileName, ET9U32 dbId)
{
    ET9STATUS status = ET9STATUS_ERROR;

    switch(dbId)
    {
        case ET9PLIDChineseSimplified:      /*SimeDBName.XT9_LDB_SIMPLIFIED*/
            pSimplifiedLDBName = dbFileName;
            status = ET9STATUS_NONE;
            break;
        case ET9PLIDChineseHongkong:        /*SimeDBName.XT9_LDB_HK*/
            pTraditionalHKName = dbFileName;
            status = ET9STATUS_NONE;
            break;
        case ET9PLIDChineseTraditional:     /*SimeDBName.XT9_LDB_TW*/
            pTraditionalTWName = dbFileName;
            status = ET9STATUS_NONE;
            break;
        default:
            break;
    }

    return status;
}

ET9STATUS xt9Hotword_setLanguage(const char *langName)
{
    ET9U32 wLdbNum = 0; //get the language id from lang
    ET9STATUS status = ET9STATUS_ERROR;

    if(strcmp(langName, g_lang_zh_cn) == 0) {
        wLdbNum = ET9PLIDChineseSimplified;
    } else if(strcmp(langName, g_lang_zh_hk) == 0) {
        wLdbNum = ET9PLIDChineseHongkong;
    } else if(strcmp(langName, g_lang_zh_tw) == 0) {
        wLdbNum = ET9PLIDChineseTraditional;
    }

    if(wLdbNum != 0) {
        status = xt9Hotword_LoadLDB(wLdbNum);
    }

    return status;
}

ET9STATUS xt9Hotword_addPhrase(ET9SYMB *pHotword, ET9U8 wordLen, ET9SYMB *pSpell, ET9U8 spellLen)
{
	ET9STATUS status = ET9STATUS_ERROR;

	XT9_HOTWORD_CHECK_INIT(gxt9HotwordData.bInit);

	if(pHotword != NULL && wordLen > 0)	{

		ET9CPPhrase sPhrase;
		ET9CPSpell spell;

		ET9U8 copyLen = wordLen;
		if(copyLen > ET9CPMAXPHRASESIZE) copyLen = ET9CPMAXPHRASESIZE;
		memset(&sPhrase, 0x00, sizeof(sPhrase));
		memcpy(sPhrase.pSymbs, pHotword, copyLen*sizeof(ET9SYMB));
		sPhrase.bLen = copyLen;

		if(pSpell != NULL && spellLen > 0)
		{
			memset(&spell, 0x00, sizeof(spell));
			copyLen = spellLen;
			if(copyLen > ET9CPMAXSPELLSIZE) copyLen = ET9CPMAXSPELLSIZE;
			memcpy(spell.pSymbs, pSpell, copyLen*sizeof(ET9SYMB));
			spell.bLen = copyLen;
			status = ET9CPMdbAddPhrase(&gxt9HotwordData.sCPLingInfo, &sPhrase, &spell, 0);
			if(status == ET9STATUS_BAD_PARAM)
				status = ET9CPMdbAddPhrase(&gxt9HotwordData.sCPLingInfo, &sPhrase, NULL, 0);
		}
		else
		{
			status = ET9CPMdbAddPhrase(&gxt9HotwordData.sCPLingInfo, &sPhrase, NULL, 1);
		}
	}
	return status;
}

ET9STATUS xt9Hotword_deletePhrase(ET9SYMB *pPhrase, ET9U8 wordLen)
{
	ET9STATUS status = ET9STATUS_ERROR;

	XT9_HOTWORD_CHECK_INIT(gxt9HotwordData.bInit);

	if(pPhrase != NULL && wordLen > 0)	{

		ET9CPPhrase sPhrase;

		ET9U8 copyLen = wordLen;
		if(copyLen > ET9CPMAXPHRASESIZE) copyLen = ET9CPMAXPHRASESIZE;
		memset(&sPhrase, 0x00, sizeof(sPhrase));
		memcpy(sPhrase.pSymbs, pPhrase, copyLen*sizeof(ET9SYMB));
		sPhrase.bLen = copyLen;

		status = ET9CPMdbDeletePhrase(&gxt9HotwordData.sCPLingInfo, &sPhrase);
	}
	return status;
}

//export the data to file : pFileName
ET9STATUS xt9Hotword_Export(char const * pFileName)	{

	ET9STATUS status = ET9STATUS_ERROR;

	XT9_HOTWORD_CHECK_INIT(gxt9HotwordData.bInit);

	if(pFileName != NULL)	{

		ET9U32 dwBufSize = 0;
		ET9UINT nSuccessCount = 0, nWrite = 0;

		status = ET9CPMdbGetExportSize(&gxt9HotwordData.sCPLingInfo, &dwBufSize);

		//BSTLOGD("mdb Convert getExportSize status = %d, size = %d", status, dwBufSize);

		if(status == ET9STATUS_NONE && dwBufSize > 0)	{

			ET9U8 *pbBuf = (ET9U8 *)malloc(dwBufSize);

			if(pbBuf != NULL)	{

				memset(pbBuf, 0x00, dwBufSize);
				status = ET9CPMdbExport(&gxt9HotwordData.sCPLingInfo, pbBuf, dwBufSize, 0, &nSuccessCount);

				//BSTLOGD("mdb Convert ET9CPMdbExport status = %d, sucessCount = %d", status, nSuccessCount);

				if(status == ET9STATUS_NONE && nSuccessCount > 0)	{

				    //BSTLOGD("mdb Convert export file = %s", pFileName);

					FILE *pFile = fopen(pFileName, "wb");

					if (NULL != pFile) {
						nWrite = fwrite(pbBuf, sizeof(ET9U8), dwBufSize, pFile);
						//BSTLOGD("mdb Convert fwrite writeCount = %d", nWrite);
						fclose(pFile);
					}

					free(pbBuf);
					if (dwBufSize != nWrite) {
					    //BSTLOGD("export write file error");
					}
				}
			}
		}
	}
	return status;
}

/*-----------------------------------------------------------------------------
 *
 *  Function name   : xt9Hotword_Import
 *
 *  Description     : import the MDB
 *
 *  Pass            :  fileName
 *
 *  Return          : void
 *
 *---------------------------------------------------------------------------*/
ET9STATUS xt9Hotword_Import(const char *pFileName)	{

	//import MDB
	//read from file
	ET9U8 *pbBuf = NULL;
	ET9U32 nFileSize = 0;
	ET9UINT nAcceptCount = 0, nRejectCount = 0;
	ET9STATUS status = ET9STATUS_ERROR;

	XT9_HOTWORD_CHECK_INIT(gxt9HotwordData.bInit);

	if(xt9Hotword_mdbReadData(pFileName, &pbBuf, &nFileSize) >0 && pbBuf != NULL)
	{
	    //BSTLOGD("++++ ET9CPMdbReset status = %d", status);
		status = ET9CPMdbImport(&gxt9HotwordData.sCPLingInfo, pbBuf, nFileSize, &nAcceptCount, &nRejectCount, 0);
		free(pbBuf);
		//BSTLOGD("++++ mdb import status = %d, acceptNum = %d, rejectNum = %d", status, nAcceptCount, nRejectCount);
	}
	return status;
}

ET9STATUS xt9Hotword_Reset()
{
    ET9STATUS status = ET9STATUS_ERROR;

    if(gxt9HotwordData.pMdbData) {
        memset(gxt9HotwordData.pMdbData, 0, g_mdbDataSize);
        status = xt9Hotword_activeMdb();
    }

    // = ET9CPMdbReset(&sCPIME.sCPLingInfo); //can I call this function?
    return status;
}

static ET9STATUS xt9Hotword_activeMdb()
{
	ET9STATUS status = ET9CPMdbActivate(&gxt9HotwordData.sCPLingInfo, NULL, (ET9U8 ET9FARDATA *)gxt9HotwordData.pMdbData, g_mdbDataSize);
	
	if (ET9STATUS_NO_RUDB == status) {
		/* language matches but other things incompatible, must reset the MDB to blank */
		memset((void*)gxt9HotwordData.pMdbData, 0, g_mdbDataSize);
		status = ET9CPMdbActivate(&gxt9HotwordData.sCPLingInfo, NULL, (ET9U8 ET9FARDATA *)gxt9HotwordData.pMdbData, g_mdbDataSize);
	}

	return status;
}  

static ET9STATUS xt9Hotword_LoadLDB(ET9U32 wLdbNum)
{
	ET9STATUS status = ET9STATUS_NONE;

	if ((ET9PLIDChineseSimplified == wLdbNum)
			|| (ET9PLIDChineseTraditional == wLdbNum)
			|| (ET9PLIDChineseHongkong == wLdbNum)) {

		status = ET9CPLdbValidate(&gxt9HotwordData.sCPLingInfo, wLdbNum,
				xt9Hotword_ldbRead);

		if (ET9STATUS_NONE == status) {
			status = ET9CPLdbInit(&gxt9HotwordData.sCPLingInfo, wLdbNum,
					gxt9HotwordData.ET9CPLdbReadData);
		}

		//BSTLOGD("  mdbConvert LoadLDB(): ET9CPLdbInit() wLdbNum=%x  status = %d", wLdbNum, status);

		if (status == ET9STATUS_NONE) {

			gxt9HotwordData.bInit = 1;

			if (gxt9HotwordData.pMdbData) {
				memset(gxt9HotwordData.pMdbData, 0, g_mdbDataSize);
				status = xt9Hotword_activeMdb();
			}
		}
	} else {
		status = ET9STATUS_LDB_VERSION_ERROR;
	}

	//BSTLOGD("  CHN_LoadLDB(): CHN_LoadLDB() wLdbNum=%x status = %d", wLdbNum, status);
	return status;
}

static ET9STATUS xt9Hotword_getLdbBuffer(ET9CPLingInfo *pLingInfo,
                                      ET9U8         * ET9FARDATA *ppbSrc,
                                      ET9U32        *pdwSizeInBytes)
{
	// notifying the core of the location of the ldb
	if(gxt9HotwordData.pLdbData == NULL || gxt9HotwordData.nLDBSize == 0)
		return ET9STATUS_READ_DB_FAIL;
	else	{
		*ppbSrc = gxt9HotwordData.pLdbData;
		*pdwSizeInBytes = gxt9HotwordData.nLDBSize;
		return ET9STATUS_NONE;
	}
}
/*---------------------------------------------------------------------------
 *
 *   Function: ldbRead
 *
 *   Synopsis: This function reads LDB data - using direct access
 *
 *      Input: pLingInfo            = Pointer to Chinese information structure.
 *             ppbSrc               = Pointer to the variable that receives the data pointer
 *             pdwSizeInBytes       = Pointer to the variable that receives the data size in bytes
 *
 *     Return: ET9STATUS_NONE on success, otherwise return ET9 error code.
 *                                                                            
 *---------------------------------------------------------------------------*/
static ET9STATUS xt9Hotword_ldbRead(ET9CPLingInfo *pLingInfo,
                                      ET9U8         * ET9FARDATA *ppbSrc, 
                                      ET9U32        *pdwSizeInBytes)
{
	char *pStr = NULL;
	ET9U32 dwFileSize = 0;
	ET9U8* pLdbFile;

	switch (pLingInfo->dwLdbNum) {

		case ET9PLIDChineseSimplified: /* Simplified Chinese */
			pStr = pSimplifiedLDBName;
		break;

	#ifdef HK_TW
		case ET9PLIDChineseTraditional: /* Traditional Chinese */
			pStr = pTraditionalTWName;
		break;

		case ET9PLIDChineseHongkong : /* HongKong Chinese */
			pStr = pTraditionalHKName;
		break;
	#endif

		default :
			return ET9STATUS_READ_DB_FAIL;
	}

	if(pStr != NULL)	{

		if(xt9Hotword_loadFile(pStr, &pLdbFile, &dwFileSize) <= 0)	{
		    //BSTLOGD(" ET9CPLdbReadData: =====db read failed===");
			return ET9STATUS_ERROR;
		}
	} else	{
	    //BSTLOGD(" ET9CPLdbReadData: =====db not installed===");
		return ET9STATUS_ERROR;
	}

    // notifying the core of the ldb size
    *pdwSizeInBytes = dwFileSize;
	
	// freeing the previous ldb memory
	if(gxt9HotwordData.pLdbData)
		free(gxt9HotwordData.pLdbData);

	// saving the pointer and size of the new ldb
	gxt9HotwordData.pLdbData = pLdbFile;

	// notifying the core of the location of the ldb
	*ppbSrc = pLdbFile;

	// notifying the core of the ldb size
	*pdwSizeInBytes = dwFileSize;

	gxt9HotwordData.nLDBSize = dwFileSize;
	//BSTLOGD(" ET9CPLdbReadData: =====ldb file =  %s, size = %d, buffer = 0x%d", pStr, dwFileSize, gxt9HotwordData.pLdbData);
	
    // successfully changed the ldb
    return ET9STATUS_NONE;
}

static int xt9Hotword_mdbReadData(const char *pfileName, ET9U8** ppbSrc, ET9U32* pdwSizeInBytes)
{


	 if(xt9Hotword_loadFile(pfileName, ppbSrc, pdwSizeInBytes) <=0)
     {
		// failed to load the ldb file
	     //BSTLOGD(" MdbReadData load file: =====failed===");
		return -1;
     }

	 //BSTLOGD(" MdbReadData load file size = %d", *pdwSizeInBytes);

	 return 1;

}

static int  xt9Hotword_loadFile(char* file, ET9U8** data, ET9U32* size)
{
	FILE* hFile;
	int ret = 0;

	// attempting to open the file
	hFile = fopen(file, "rb");

	// checking to see if the file was opened successfully
	//BSTLOGI(" LoadFile: ===== file = %s, open ret = %d", file, hFile);

	if (hFile == NULL) {
		// failed to open the file
		//use assetManager to load the file
		if (gxt9HotwordData.pAssetMgr != NULL) {
		    //BSTLOGD(" LoadFile: =====assetmanager= %d", gxt9HotwordData.pAssetMgr);

			char *fileName = strrchr(file, '/');

			if (fileName != NULL) {
			    //BSTLOGI(" LoadFile: ===== file = %s", (fileName+1));

				AAsset* asset = NULL;

				char *pLdbPath = LDB_PATH;
				int fileNameSize = (strlen(pLdbPath) + strlen(fileName + 1) + 1)
						* sizeof(char); //(FULL_NAME_LEN+1)*sizeof(char);//
				//BSTLOGD(" LoadFile: =====fileNameSize size= %d", fileNameSize);
				char *fullFileName = malloc(fileNameSize);
				if (fullFileName != NULL) {
					memset(fullFileName, 0, fileNameSize);
					strcat(fullFileName, pLdbPath);
					strcat(fullFileName, fileName + 1);
					asset = AAssetManager_open(gxt9HotwordData.pAssetMgr,
							(const char *) fullFileName, AASSET_MODE_UNKNOWN);
					free(fullFileName);
				}

				if (asset != NULL) {
					off_t bufferSize = AAsset_getLength(asset);

					//BSTLOGI(" LoadFile: ======== file size = %d", bufferSize);
					if (bufferSize != 0) {
						*data = malloc(bufferSize);
						if (*data != NULL) {
							*size = AAsset_read(asset, *data, bufferSize);

							//BSTLOGI(" LoadFile: =====read size= %d", * size);

							if (*size != bufferSize) {
								free(*data);
								*data = NULL;
							} else
								ret = 1;
						}
					}
					AAsset_close(asset);
				}
			}
		}

	} else {
		long dwFileSize;

		// seeking to the end of the file to determine it's file size
		fseek(hFile, 0, SEEK_END);

		// retrieving the file size
		dwFileSize = ftell(hFile);

		// checking to see if the size is valid
		if (!dwFileSize) {
			// closing the file
			fclose(hFile);

			// invalid file size
			return 0;
		}

		// seeking back to the beginning of the file
		fseek(hFile, 0, SEEK_SET);

		// attempting to allocate memory for the file
		*data = malloc(dwFileSize);

		// checking to see if the memory was allocated successfully
		if (!*data) {
			// closing the file
			fclose(hFile);

			// failed to allocate memory for the file
			return 0;
		}

		// reading the file data into memory
		*size = fread(*data, 1, dwFileSize, hFile);

		// verifying that the file was read into memory
		if (*size != dwFileSize) {
			// closing the file
			fclose(hFile);

			// freeing the file memory
			free(*data);

			// resetting memory buffer pointer
			*data = NULL;

			// failed to load the file into memory
			return 0;
		}

		// closing the file
		fclose(hFile);
		ret = 1;
	}
	//BSTLOGI(" LoadFile: =====read ret= %d, size = %d", ret, *size);
	// successfully loaded the file into memory
	return ret;
}
