#include <stdio.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ctim.h"
#include "zhuyin_data.h"
#include "zhuyin_messages.h"

#define NAME_UTF8	"Codetable"
#define AUTHOR		"Ervin Yan <Ervin.Yan@sun.com>"
#define COPYRIGHT	"Copyright (c) 2004 Sun Microsystems"
#define HINTING		"Codetable Input Method"

ImeResult zhuyin_Initialize(ImeInfo ime_info);
ImeResult zhuyin_Destroy(ImeInfo ime_info);
ImeResult zhuyin_Process_Key_Event(ImeInputContext ic, ImeKey key_event);
ImeResult zhuyin_Create_Session(ImeInputContext ic);
ImeResult zhuyin_Destroy_Session(ImeInputContext ic);
ImeResult zhuyin_FocusOut(ImeInputContext ic);
ImeResult zhuyin_FocusIn(ImeInputContext ic);

ImmServices imm_services;

typedef struct _codetable_im_data_t {
	char *file_name;
	TZhuyinData *zhuyin_data;
} codetable_im_data_t;

ImeMethodsRec zhuyin_methods = {
	1,                                /* version */
	zhuyin_Initialize,                  /* ImeInitialize */
	zhuyin_Destroy,                     /* ImeDestroy  */
	zhuyin_Process_Key_Event,           /* ImeProcessKeyEvent */
	NULL,                             /* ImeProcessAuxEvent  */
	zhuyin_Create_Session,              /* ImeAttachSession */
	zhuyin_Destroy_Session,             /* ImeDetachSession */
	zhuyin_FocusIn,                     /* ImeFocusIn  */
	zhuyin_FocusOut,                    /* ImeFocusOut */
	NULL,                             /* ImeAttachUser */
	NULL,                             /* ImeDetachUser */
	NULL,                             /* ImeAttachDesktop */
	NULL,                             /* ImeDetachDesktop */
	NULL,                             /* ImeGetErrorMessage */
#if 0
	NULL,                             /* ImeDoConfig */
#endif
};

#ifdef	WIN32
#define EXPORT extern __declspec(dllexport)
EXPORT
#endif
ImeResult RegisterIME(ImmServices srvs, ImeInfo* ppinfo, ImeMethods* pmthds, int argc, char **argv)
{
	ImeInfoRec *zhuyin_info = NULL;
	char *zhuyin_config_file = NULL;
	char *base_dir = NULL;
	char file_path[256];
	TZhuyinData  *zhuyin_data = NULL;
	CodeTableStruct *ctHeader;

	int version_num, i, ret;

	DEBUG_printf("Register Codetable IM: argc: %d\n", argc);
	for (i=0; i<argc; i++) {
		if (!strcasecmp(argv[i], "-basedir")) {
			if (argv[i+1]) {
				base_dir = argv[i+1];
				DEBUG_printf("       setting base dir to: %s\n", argv[i+1]);
			}
			i++;
		} else if (!strcasecmp(argv[i], "-config")) {
			if (argv[i+1]) {
				zhuyin_config_file = argv[i+1];
				DEBUG_printf("       setting codetable file to: %s\n", argv[i+1]);
			}
			i++;
		}
	}

	if (!zhuyin_config_file || !*zhuyin_config_file)
		return (IME_FAIL);

	if (base_dir == NULL)
		base_dir = LE_BASE_DIR;

	if (base_dir && *base_dir && *zhuyin_config_file != '/') {
		snprintf(file_path, 256, "%s/%s", base_dir, zhuyin_config_file);
	}

	zhuyin_data = (TZhuyinData *)calloc(1, sizeof(TZhuyinData));
	if (zhuyin_data == NULL)
		return(IME_FAIL);

	ret = ZhuyinData_Init(file_path, zhuyin_data);
	if (ret == ZHUYIN_ERROR) {
		ZhuyinData_Free(zhuyin_data);
		free ((char *)zhuyin_data);
		return (IME_FAIL);
	}

	zhuyin_info = (ImeInfoRec *)calloc(1, sizeof(ImeInfoRec));
	if (zhuyin_info == NULL)  {
		ZhuyinData_Free(zhuyin_data);
		free ((char *)zhuyin_data);
		return (IME_FAIL);
	}

	ctHeader =(CodeTableStruct *)(zhuyin_data->pCodetableHeader);
	version_num = 1;
	if (*(ctHeader->Version))
		version_num = atoi(ctHeader->Version);
	version_num += CODETABLE_VERSION * 100;

	zhuyin_info->version           = version_num;
	zhuyin_info->encoding          = ctHeader->Encode;
	zhuyin_info->name              = (char *)strdup(ctHeader->Cname);
	zhuyin_info->uuid              = (char *)strdup(ctHeader->UUID);

	if (*ctHeader->Author)
		zhuyin_info->author    = (char *)strdup(ctHeader->Author);
	else
		zhuyin_info->author    = (char *)strdup(AUTHOR);
	if (*ctHeader->Copyright)
		zhuyin_info->copyright = (char *)strdup(ctHeader->Copyright);
	else
		zhuyin_info->copyright = (char *)strdup(COPYRIGHT);
	if (*ctHeader->Hinting)
		zhuyin_info->hinting   = (char *)strdup(ctHeader->Hinting);
	else
		zhuyin_info->hinting   = (char *)strdup(HINTING);

	zhuyin_info->icon_file         = (char *)strdup(ctHeader->IconPath);

	zhuyin_info->support_locales   = ZHUYIN_SUPPORT_LOCALES;

	zhuyin_info->specific_data     = (void *)zhuyin_data;

	zhuyin_Init_Ime_Properties (zhuyin_info, zhuyin_data);

	*ppinfo = zhuyin_info;
	*pmthds = &zhuyin_methods;

	imm_services = srvs;

	DEBUG_printf("begin leave Register IME\n");
	return (IME_OK);
}

ImeResult zhuyin_Initialize(ImeInfo zhuyin_info)
{
	TZhuyinData *zhuyin_data;
	int ret;

	DEBUG_printf("zhuyin_Initialize\n");

	if (zhuyin_info == NULL)
		return (IME_FAIL);

	zhuyin_data = (TZhuyinData *)zhuyin_info->specific_data;
	ret = ZhuyinData_Open(zhuyin_data);
	if (ret == ZHUYIN_ERROR) {
		ZhuyinData_Free(zhuyin_data);
		free ((char *)zhuyin_data);
		return (IME_FAIL);
	}

	return (IME_OK);
}

ImeResult zhuyin_Destroy(ImeInfo zhuyin_info)
{
	TZhuyinData *zhuyin_data;

	DEBUG_printf("zhuyin_Destroy\n");

	if (zhuyin_info != NULL) {
		if (zhuyin_info->uuid != NULL)
			free ((char *)zhuyin_info->uuid);
		if (zhuyin_info->icon_file != NULL)
			free ((char *)zhuyin_info->icon_file);
		if (zhuyin_info->name != NULL)
			free ((char *)zhuyin_info->name);
		if (zhuyin_info->author != NULL)
			free ((char *)zhuyin_info->author);
		if (zhuyin_info->copyright != NULL)
			free ((char *)zhuyin_info->copyright);
		if (zhuyin_info->hinting != NULL)
			free ((char *)zhuyin_info->hinting);

		zhuyin_Destroy_Ime_Properties(zhuyin_info);

		zhuyin_data = (TZhuyinData *)zhuyin_info->specific_data;
		if (zhuyin_data) {
			ZhuyinData_Free(zhuyin_data);
			free ((char *)zhuyin_data);
		}

		free ((char *)zhuyin_info);
	}

	return (IME_OK);
}

ImeResult zhuyin_Create_Session(ImeInputContext ic)
{
	int i;
	ImmResult imm_result;
	ImeBufferRec *ime_buffer = NULL;

	ime_buffer = (ImeBufferRec *)imm_services->ImmGetData(ic, IME_SCOPE_SESSION);
	DEBUG_printf("zhuyin_Create_Session ======= begin get ime_session_data: 0x%x\n", ime_buffer);
	if (ime_buffer == NULL) {
		ime_buffer = (ImeBufferRec *)calloc(1, sizeof(ImeBufferRec));
		if (ime_buffer == NULL)
			return (IME_FAIL);

		for (i = 0; i < MAX_CANDIDATES_NUM; i++) {
			ime_buffer->candidates[i] = ime_buffer->candidates_buf[i];
			ime_buffer->comments[i] = ime_buffer->comments_buf[i];
			ime_buffer->lookups[i] = ime_buffer->lookups_buf[i];
		}

		imm_result = imm_services->ImmSetData(ic, IME_SCOPE_SESSION, ime_buffer);
		if (imm_result == IMM_FAIL) {
			free ((char *)ime_buffer);
			return (IME_FAIL);
		}
	}
	
	return (IME_OK);
}

ImeResult zhuyin_Destroy_Session(ImeInputContext ic)
{
	ImeBufferRec *ime_buffer = NULL;

	ime_buffer = (ImeBufferRec *)imm_services->ImmGetData(ic, IME_SCOPE_SESSION);
	DEBUG_printf("zhuyin_Destroy_Session ======= begin get ime_session_data: 0x%x\n", ime_buffer);

	if (ime_buffer != NULL) {
		free ((char *)ime_buffer);
	}

	imm_services->ImmSetData(ic, IME_SCOPE_SESSION, NULL);

	return (IME_OK);
}

ImeResult zhuyin_FocusIn(ImeInputContext ic)
{
        DEBUG_printf("codetable: call zhuyin_FocusIn()\n");
        return(IME_OK);
}

ImeResult zhuyin_FocusOut(ImeInputContext ic)
{
        DEBUG_printf("codetable: call zhuyin_FocusOut()\n");
        return(IME_OK);
}

ImmResult zhuyin_beep(ImeInputContext ic)
{
	return (imm_services->ImmBeep(ic, ImeBeepWarning));
}

ImmResult zhuyin_commit(ImeInputContext ic, int encoding, char *commit_buf, int commit_len)
{
	if (commit_len <= 0)
		return (IMM_FAIL);

	if (commit_buf == NULL)
		return (IMM_FAIL);

	return (imm_services->ImmCommit(ic, commit_buf));
}

ImmResult zhuyin_update_preedit(ImeInputContext ic, int encoding,
				char *preedit_buf, int preedit_len, int caret_pos)
{
	ImePreeditRec ime_preedit;

	memset(&ime_preedit, 0, sizeof(ImePreeditRec));

	if (preedit_len == 0) {
		return (imm_services->ImmHidePreedit(ic));
	}

        imm_services->ImmShowPreedit(ic);

	ime_preedit.caret = caret_pos;
	ime_preedit.preedit.text = preedit_buf;

	return (imm_services->ImmUpdatePreedit(ic, &ime_preedit));
}

ImmResult zhuyin_update_candidates(ImeInputContext ic, int encoding, char **candidates, int num_candidates, int page_state)
{
	int i;
	ImmResult imm_result;
	ImeCandidatesRec ime_candidates;

	memset(&ime_candidates, 0, sizeof(ImeCandidatesRec));

	if (num_candidates == 0 || candidates == NULL) {
		return (imm_services->ImmHideCandidates(ic));
	}

	imm_services->ImmShowCandidates(ic);

	ime_candidates.title = NULL;
	ime_candidates.focus = 0;
	ime_candidates.page_state = page_state;
	ime_candidates.numbers = NULL;
	ime_candidates.count = num_candidates;
	ime_candidates.candidates = (ImeTextRec *)calloc(num_candidates, sizeof(ImeTextRec));
	if (ime_candidates.candidates == NULL)
		return (IMM_FAIL);

	for (i=0; i<num_candidates; i++) {
		ime_candidates.candidates[i].text = candidates[i];
	}

	imm_result = imm_services->ImmUpdateCandidates(ic, &ime_candidates);

	free ((char *)ime_candidates.candidates);
	return(imm_result);
}

/* process key input event */
/* return value:  IME_UNUSED_KEY:  if IME not use this key, return this key to systerm directly */
/*                IME_OK:      if IME has used this key */
ImeResult zhuyin_Process_Key_Event(ImeInputContext ic, ImeKey key_event)
{
	ImeInfoRec *zhuyin_info = NULL;
	ImeBufferRec *ime_buffer = NULL;

	TZhuyinData *zhuyin_data;
	unsigned char key;
	int ret;

	DEBUG_printf("zhuyin_Process_Key_Event: ic: 0x%x\n", ic);
	ime_buffer = (ImeBufferRec *)imm_services->ImmGetData(ic, IME_SCOPE_SESSION);
	if (ime_buffer == NULL)
		return (IME_UNUSED_KEY);

	zhuyin_info = (ImeInfo)imm_services->ImmGetImeInfo(ic);
	if (zhuyin_info == NULL || zhuyin_info->specific_data == NULL)
		return (IME_UNUSED_KEY);

	zhuyin_data = (TZhuyinData *)zhuyin_info->specific_data;
	if (zhuyin_data == NULL ||
	    zhuyin_data->pCodetableHeader == NULL)
		return (IME_UNUSED_KEY);

	if (imm_services->ImmPrefilterKey == NULL) {
		DEBUG_printf("zhuyin_Process_Key_Event: imm_services->ImmPrefilterKey is NULL\n");
		return (IME_UNUSED_KEY);
	}
		
	key = imm_services->ImmPrefilterKey(key_event);
	DEBUG_printf("zhuyin_Process_Key_Event: imm_services->ImmPrefilterKey return: 0x%x\n", key);
	if (key == IME_FILTERED_KEY_UNUSED)
		return (IME_UNUSED_KEY);

	zhuyin_Set_Ime_Properties(ic, zhuyin_data);

	ret = zhuyin_filter(zhuyin_data, key, ime_buffer);

	if (ime_buffer->return_status & IME_PREEDIT_AREA) {
		zhuyin_update_preedit(ic, ime_buffer->encoding, ime_buffer->preedit_buf,
				      ime_buffer->preedit_len, ime_buffer->preedit_caretpos);
	}

	if (ime_buffer->return_status & IME_LOOKUP_AREA) {
		zhuyin_update_candidates(ic, ime_buffer->encoding, ime_buffer->lookups, ime_buffer->num_candidates, ime_buffer->page_state);
	}
	
	if (ime_buffer->return_status & IME_COMMIT) {
		zhuyin_commit(ic, ime_buffer->encoding, ime_buffer->commit_buf, ime_buffer->commit_len);
	}

        if (ime_buffer->return_status & IME_BEEP) {
		zhuyin_beep(ic);
        }

	if (ret == IME_UNUSED_KEY)
		return (IME_UNUSED_KEY);

	return (IME_OK);
}

