#include "et9api.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

#include <android/log.h>

#include "et9ChnCategoryDb.h"

#define TAG "et9ChnCategoryDb"
// #define DEBUGMODE

#ifndef DEBUGMODE
#define LOGD(...)   ((void)0)
#else
#define LOGD(...)   __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#endif

#define XCatDB_MAX_COUNT  8

typedef struct
{
    ET9U32  dwLangID;               /* LangID of the XCatDB */
    ET9U16  wCategoryID;            /* CatID of the XCatDB */
    char   *pFileName;              /* File name of the XCatDB */
    ET9U8  *pbData;                 /* Pointer to the XCatDB data */
    ET9U32  dwDataSize;             /* Size of the XCatDB data */
    ET9BOOL fEnabled;
    ET9U8   bPriority;              /* 0: not enabled, 1: HighestPrio, 2: AboveLdbPrio, 3: LdbPrio, 4: BelowLdbPrio, 5: LowestPrio */
    char    *pVersion;				/*now use update time for example: "20130917 16:00"*/
} XCatDB;



void InitAllXCatDBs();
void FreeAllXCatDBs(ET9CPLingInfo *pCLing);
int ReadDataFromAsset(AAssetManager *pAssetMgr, char *path, char* filename, ET9U8** data, ET9U32* size);
int ReadDataFromFile(char* filename, ET9U8** data, ET9U32* size);
ET9STATUS installCateDB(ET9CPLingInfo *pCLing, AAssetManager *pAssetMgr, ET9U16 wCategoryID, ET9U32 dwLangID, const char *pFileName, const char *pVersion, ET9BOOL bEnable);

AAssetManager *gpAssetMgr; //UnifiedIME

static XCatDB  g_aXCatDBs[XCatDB_MAX_COUNT] = {0}; /* Array of loaded XCatDB */

#define CDB_PATH "xt9db/cdb"

void InitAllXCatDBs()
{
    memset(g_aXCatDBs, 0, sizeof(g_aXCatDBs));
}

void FreeAllXCatDBs(ET9CPLingInfo *pCLing)
{
    int i;
    LOGD( "FreeAllXCatDBs!!!" );
    for (i = 0; i < XCatDB_MAX_COUNT; i++){
        FreeXCatDB( pCLing, &g_aXCatDBs[i]);
    }
}

void FreeXCatDB(ET9CPLingInfo *pCLing, XCatDB* pXCatDB)
{
    if ( pXCatDB != NULL ){
        if (pXCatDB->fEnabled )	{
            ET9CPDeactivateCatDB(pCLing, pXCatDB->dwLangID, pXCatDB->wCategoryID);
        }
        if (pXCatDB->pFileName){
            free(pXCatDB->pFileName);
        }
        if (pXCatDB->pbData){
            free(pXCatDB->pbData);
        }
        if(pXCatDB->pVersion)
        	free(pXCatDB->pVersion);

        memset(pXCatDB, 0, sizeof(XCatDB));
    }
}

//copied from ldbread.c
//filename does not include path
int ReadDataFromAsset(AAssetManager *pAssetMgr, char *path, char* filename, ET9U8** data, ET9U32* size)	{

	int ret = 0;

	if(pAssetMgr != NULL && filename != NULL)
	{
		char *pAssetPath = path;
		int assetstrlength = (strlen(filename) + 1)*sizeof(char);
		char *assetsFileName = NULL;

		if(pAssetPath != NULL)
			assetstrlength +=strlen(pAssetPath)*sizeof(char);

		assetsFileName = malloc(assetstrlength);

		if(assetsFileName != NULL)
		{
			memset(assetsFileName, 0, assetstrlength);
			if(pAssetPath != NULL)
				strcat(assetsFileName, pAssetPath);
			strcat(assetsFileName, filename);
		}
		else
		{
			assetsFileName = filename;
		}
		LOGD(" LoadFile: ===== assetsFileName = %s", assetsFileName);
		LOGD(" LoadFile: =====assetmanager= %d", pAssetMgr);
		AAsset* asset = NULL;
		asset = AAssetManager_open(pAssetMgr, (const char *)assetsFileName, AASSET_MODE_UNKNOWN);
		free(assetsFileName);

		LOGD(" LoadFile: ===== asset = %s", asset);
		if(asset != NULL)
		{
			off_t bufferSize = AAsset_getLength(asset);

			LOGD(" LoadFile: ======== file size = %d", bufferSize);
			if(bufferSize != 0)
			{
				*data = malloc(bufferSize);
				if(*data != NULL)
				{
					*size = AAsset_read(asset, *data, bufferSize);

					LOGD(" LoadFile: =====read size= %d", * size );

					if(*size == 0)
					{
						free(*data);
						*data = NULL;
					}
					else
						ret = 1;
				}
			 }
			 AAsset_close(asset);
		 }
	 }
	return ret;
}

int ReadDataFromFile(char* filename, ET9U8** data, ET9U32* size) {
    FILE* hFile;
    LOGD(" ReadLDBFormFile: ===== filename = %s", filename);
	hFile = fopen(filename, "rb");
	if(hFile == NULL) {
		LOGD(" ReadLDBFormFile: ===== open failed");
		return 0;
	}
	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 <= 0) {
		// 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)
	{
		// 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);
	return 1;
}
//copied from ldbread.c

static XCatDB * getCatDB(ET9U16 wCategoryID, ET9U32 dwLangID){

	ET9U8 index  = 0, freeIndex = XCatDB_MAX_COUNT;
	XCatDB *pCatDB = NULL;

	for(; index<XCatDB_MAX_COUNT; index++)	{
		if(g_aXCatDBs[index].dwLangID == dwLangID && g_aXCatDBs[index].wCategoryID == wCategoryID)	{
			pCatDB = &g_aXCatDBs[index];
			break;
		}

		if(freeIndex == XCatDB_MAX_COUNT && g_aXCatDBs[index].dwLangID == 0)
			freeIndex = index;
	}

	if(pCatDB == NULL && freeIndex != XCatDB_MAX_COUNT)	{
		pCatDB = &g_aXCatDBs[freeIndex];
	}

	return pCatDB;
}

static ET9BOOL XCatDBLoadFile(XCatDB *pXCatDB)
{
    ET9BOOL ret = 0;
    pXCatDB->pbData = NULL;
    pXCatDB->dwDataSize = 0;
    ret = ReadDataFromFile(pXCatDB->pFileName, &(pXCatDB->pbData),&(pXCatDB->dwDataSize));
    if(ret <= 0)
    	ret = ReadDataFromAsset(gpAssetMgr, NULL, pXCatDB->pFileName, &(pXCatDB->pbData),&(pXCatDB->dwDataSize));
    LOGD( "XCatDBLoadFile pFileName = %s,DataSize = %d, ret = %d", pXCatDB->pFileName, pXCatDB->dwDataSize, ret);
    return ret;
}

static ET9STATUS  EnableXCatDB(ET9CPLingInfo *pCLing, XCatDB* pXCatDB)
{
    ET9STATUS status = ET9STATUS_NONE;
    if ( pXCatDB->fEnabled ){
    	if (NULL == pXCatDB->pbData){
			ET9BOOL fSucceed = XCatDBLoadFile(pXCatDB);
			LOGD( "EnableXCatDB XCatDBLoadFile fSucceed = %d", fSucceed);
			if (!fSucceed ){
				FreeXCatDB(pCLing, pXCatDB);
				return ET9STATUS_READ_DB_FAIL;
			}
    	}
        status = ET9CPActivateCatDB( pCLing, pXCatDB->dwLangID, pXCatDB->wCategoryID );
        LOGD( "EnableXCatDB status = %d, langID = %d, catID = %d", status, pXCatDB->dwLangID, pXCatDB->wCategoryID);
        if ( status != ET9STATUS_NONE ){
        	FreeXCatDB(pCLing, pXCatDB);
            return status;
        }
        if ( pXCatDB->bPriority == 0 || pXCatDB->bPriority > ET9CP_LOWEST_PRIORITY /* 5 */ )
            pXCatDB->bPriority = ET9CP_LDB_PRIORITY;
        ET9CPSetPriorityCatDB(pCLing, pXCatDB->dwLangID, pXCatDB->wCategoryID, (ET9CP_CatDB_Priority)pXCatDB->bPriority);
    }else{
        status = ET9CPDeactivateCatDB( pCLing, pXCatDB->dwLangID, pXCatDB->wCategoryID );
        FreeXCatDB(pCLing, pXCatDB);
    }
    return status;
}

static ET9BOOL isSameVersion(const char *pOrigVersion, const char *pCurrentVersion)
{
	ET9BOOL ret = 0;
	if(pOrigVersion == NULL || pCurrentVersion == NULL)
	{
		if(pOrigVersion == NULL && pCurrentVersion == NULL)
			ret = 1;
		else
			ret = 0;
	}
	else if(strcmp(pOrigVersion, pCurrentVersion) == 0)
	{
		ret = 1;
	}
    return ret;
}

ET9STATUS installCateDB(ET9CPLingInfo *pCLing, AAssetManager *pAssetMgr, ET9U16 wCategoryID, ET9U32 dwLangID, const char *pFileName, const char *pVersion, ET9BOOL bEnable)
{
	XCatDB *pCatDB = NULL;
	ET9STATUS status = ET9STATUS_ERROR;

	gpAssetMgr = pAssetMgr; //UnifiedIME
	pCatDB = getCatDB(wCategoryID, dwLangID);

	if(pCatDB != NULL)
	{
		if(bEnable)
		{

			if(pFileName == NULL)
			{
				//no version info, we can not know it is newer or older db
				LOGD("installCateDB  DB file info lost, keep current status.");
			}
			else if(!isSameVersion(pCatDB->pVersion, pVersion) || pCatDB->fEnabled == 0)
			{
				//version info changes, update it.
				ET9U32 nFileNameSize = (strlen(pFileName) + 1)*sizeof(char);
				ET9U32 nFileVersionSize = (strlen(pVersion) + 1)*sizeof(char);

				FreeXCatDB(pCLing, pCatDB);
				pCatDB->pFileName = (char *)malloc(nFileNameSize);

				pCatDB->pVersion = (char *)malloc(nFileVersionSize);

				if(pCatDB->pFileName != NULL)	{

					memset(pCatDB->pFileName, 0, nFileNameSize);
					strcpy(pCatDB->pFileName, pFileName);
					if(pCatDB->pVersion != NULL)
					{
						memset(pCatDB->pVersion, 0, nFileVersionSize);
						strcpy(pCatDB->pVersion, pVersion);
					}
					pCatDB->wCategoryID = wCategoryID;
					pCatDB->dwLangID = dwLangID;
					pCatDB->fEnabled = 1;
					status = EnableXCatDB(pCLing, pCatDB);
					LOGD("installCateDB  catDB %d enable_status(%d)", wCategoryID, status);
				}
			} else
			{
				LOGD("installCateDB  catDB %d version is the same, keep current status!, enabled : %d", wCategoryID, pCatDB->fEnabled);
			}
		}
		else
		{
			pCatDB->fEnabled = 0;
			status = EnableXCatDB(pCLing, pCatDB);
			LOGD("installCateDB  catDB %d disabled status(%d)", wCategoryID, status);
		}
	}
	return status;
}

/* Depending whether data is being read from the internal ldb or external, this function
 * will delegate to the correct ldb reading function.  */
ET9STATUS ET9FARCALL ET9CDBReadData(
    ET9CPLingInfo          *pLingInfo,              /**< pointer to Chinese information structure */
    ET9U8 * ET9FARDATA *    ppbSrc,                 /**< pointer to a pointer to the integration supplied buffer */
    ET9U32                 *pdwSizeInBytes          /**< pointer to enter LDB size in bytes  */
)
{
    ET9U32 dwLangID = ET9CPGetChineseLdbNum(pLingInfo);
    ET9U16 wCatID = ET9CPGetReadingCatID(pLingInfo);

    LOGD( "ET9CDBReadData CatID = %d, LandID = %d", wCatID, dwLangID);
    if (wCatID == ET9CP_STATIC_LDB_CAT_ID){
    	/* wCatID == ET9CP_STATIC_LDB_CAT_ID  --- Reading LDB */
    	//return ET9CPLdbReadData(pLingInfo, ppbSrc, pdwSizeInBytes);
    	return ET9CP_LdbReadData(pLingInfo, ppbSrc, pdwSizeInBytes); //UnifiedIME
    }
    else {
    	/* wCatID != ET9CP_STATIC_LDB_CAT_ID  --- Reading CatDB */
        XCatDB *pXCatDB = getCatDB(wCatID, dwLangID);
        /* reading external CatDB */
        if (pXCatDB && pXCatDB->pbData) {
            *ppbSrc = pXCatDB->pbData;
            *pdwSizeInBytes = pXCatDB->dwDataSize;
            if (0 == *pdwSizeInBytes) {
                return ET9STATUS_READ_DB_FAIL;
            }
            else {
                return ET9STATUS_NONE;
            }
        }
        else
            return ET9STATUS_CatDB_ID_ERROR;
    }
}
