/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <app.h>
#include <stdlib.h>
#include <Elementary.h>
#include <dlog.h>
#include <inputmethod.h>
#include <system_settings.h>

#include "samplenativeime.h"
#include "samplenativeimeui.h"
#include "samplenativeimelayout.h"
#include "samplenativeimeoption.h"
#include "samplenativeimefeedback.h"

/* edje file path for portrait and landscape mode */
static const char *g_edje_file_path[IME_DISPLAY_MODE_MAX] = {"edje/keyboard_layout.edj", "edje/keyboard_layout_landscape.edj"};

static double g_scale_ratio = 1.0;																		 /* scale ratio for different screen size */
static IMELayout g_sample_native_ime_layout[IME_DISPLAY_MODE_MAX][IME_KEYBOARD_LAYOUT_MAX];				 /* the layout for keyboard */
static IMEKeyboardLayoutType g_current_ime_layout = IME_KEYBOARD_LAYOUT_ENGLISH;						 /* the current layout */
static Evas_Object *g_ime_win = NULL;																	 /* keyboard window */
static IMEShiftState g_shift_state = IME_SHIFT_STATE_OFF;												 /* shift state of keyboard */
static IMEKeyEventCallBack g_key_event;																	 /* the callback events */
static Eina_Bool g_long_key_pressed = EINA_FALSE;														 /* the long press state for one key */
IMEDisplayMode g_current_ime_display_mode = IME_DISPLAY_MODE_PORTRAIT;									 /* the current display mode of keyboard */
Ecore_IMF_Input_Panel_Return_Key_Type g_return_key_type = ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT; /* the current state for return key */
bool g_return_key_disabled = false;																		 /* the enable state for return key */
char *g_system_language = NULL;																			 /* the system language */

static void ime_app_create_cb(void *user_data);
static void ime_app_terminate_cb(void *user_data);
static void ime_app_show_cb(int ic, ime_context_h context, void *user_data);
static void ime_app_hide_cb(int ic, void *user_data);

static void ime_app_cursor_position_updated_cb(int cursor_pos, void *user_data);
static void ime_app_focus_out_cb(int ic, void *user_data);
static void ime_app_focus_in_cb(int ic, void *user_data);
static void ime_app_return_key_type_set_cb(Ecore_IMF_Input_Panel_Return_Key_Type type, void *user_data);
static void ime_app_return_key_state_set_cb(bool disabled, void *user_data);
static void ime_app_layout_set_cb(Ecore_IMF_Input_Panel_Layout layout, void *user_data);
static bool ime_app_process_key_event_cb(ime_key_code_e keycode, ime_key_mask_e keymask, ime_device_info_h dev_info, void *user_data);
static void ime_app_display_language_changed_cb(const char *language, void *user_data);

/**
 * @brief set the system language
 *
 * @param language the system language to set
 *
 */
void set_system_language(const char *language)
{
	char *res_path = app_get_resource_path();
	if (res_path)
	{
		char *locale_path = (char *)malloc(strlen(res_path) + strlen("locale/") + 1);
		strncpy(locale_path, res_path, strlen(res_path) + 1);
		strncat(locale_path, "locale/", strlen("locale/") + 1);

		setlocale(LC_ALL, language);
		bindtextdomain(PACKAGE, locale_path);
		textdomain(PACKAGE);

		free(res_path);
		free(locale_path);
	}
}

/**
 * @brief this function will construct the path from the input file name
 *
 * @param edj_file_in the input edje file
 * @param edj_path_out the output path of edje file
 * @param edj_path_max max size of edje path
 *
 */
static void ime_app_app_get_resource(const char *edj_file_in, char *edj_path_out, int edj_path_max)
{
	/* get the sample ime application resource path */
	char *res_path = app_get_resource_path();
	if (res_path)
	{
		snprintf(edj_path_out, edj_path_max, "%s%s", res_path, edj_file_in);
		free(res_path);
	}
}

/**
 * @brief when the key is pressed down, this callback function will be called
 *
 * @param key_property the key property of key
 * @param data the data for key down
 *
 * @return the key pressed down, success or not
 */
static Eina_Bool ime_app_key_down_cb(IMEKeyProperty *key_property, void *data)
{
	sample_native_ime_feedback();
	/* show magnifier window under IME_KEY_TYPE_CHAR, IME_KEY_TYPE_STRING type */
	if ((IME_KEY_TYPE_CHAR == key_property->key_type) || (IME_KEY_TYPE_STRING == key_property->key_type))
		sample_native_ime_magnifier_win_show(key_property->x, key_property->y, key_property->width, key_property->height, key_property->main_label[g_shift_state].label_text, g_scale_ratio);

	g_long_key_pressed = EINA_FALSE;
	return EINA_TRUE;
}

/**
 * @brief set the disable status for return key
 *
 * @param disabled the disable status for return key
 *
 */
static void ime_app_set_return_key_disable(bool disabled)
{
	sample_native_ime_set_return_disable(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], disabled);
	g_return_key_disabled = disabled;
}

/**
 * @brief set the type for return key
 *
 * @param type the type of return key
 *
 */
static void ime_app_set_return_key_type(Ecore_IMF_Input_Panel_Return_Key_Type type)
{
	sample_native_ime_set_return_type(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], type);
	g_return_key_type = type;
}

/*
 * @brief when release the key, this callback function will be called
 *
 * @param key_property the key property of key
 * @param data the data for key up
 *
 * @return the key up, success or not
 */
static Eina_Bool ime_app_key_up_cb(IMEKeyProperty *key_property, void *data)
{
	/* distinguish key press event and long key press event */
	IMEKeyType press_key_type = IME_KEY_TYPE_NONE;
	if (g_long_key_pressed && (IME_KEY_TYPE_NONE != key_property->long_key_type))
		press_key_type = key_property->long_key_type;
	else
		press_key_type = key_property->key_type;

	switch (press_key_type)
	{
	case IME_KEY_TYPE_NONE:
	{
		break;
	}
	case IME_KEY_TYPE_CHAR:
	{
		char *key_value = NULL;
		/* set key value for key press and long press */
		if (g_long_key_pressed && (IME_KEY_TYPE_NONE != key_property->long_key_type))
			key_value = key_property->key_value[g_shift_state][1];
		else
			key_value = key_property->key_value[g_shift_state][0];
		/* send key event must contain IME_KEY_MASK_PRESSED and IME_KEY_MASK_RELEASED */
		ime_send_key_event(key_value[0], IME_KEY_MASK_PRESSED, true);
		ime_send_key_event(key_value[0], IME_KEY_MASK_RELEASED, true);
		if (IME_SHIFT_STATE_ON == g_shift_state)
		{
			g_shift_state = IME_SHIFT_STATE_OFF;
			sample_native_ime_update_layout(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_shift_state);
		}
		sample_native_ime_magnifier_win_hide();
		break;
	}
	case IME_KEY_TYPE_CONTROL:
	{
		if (!strcmp(key_property->key_value[g_shift_state][0], "Shift"))
		{
			/* current shift state will be g_shift_state mod IME_SHIFT_STATE_MAX */
			g_shift_state++;
			g_shift_state = g_shift_state % IME_SHIFT_STATE_MAX;
			sample_native_ime_update_layout(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_shift_state);
		}
		else if (!strcmp(key_property->key_value[g_shift_state][0], "Back"))
		{
			ime_send_key_event(IME_KEY_BackSpace, IME_KEY_MASK_PRESSED, true);
			ime_send_key_event(IME_KEY_BackSpace, IME_KEY_MASK_RELEASED, true);
		}
		else if (!strcmp(key_property->key_value[g_shift_state][0], "Space"))
		{
			ime_send_key_event(IME_KEY_space, IME_KEY_MASK_PRESSED, true);
			ime_send_key_event(IME_KEY_space, IME_KEY_MASK_RELEASED, true);
		}
		else if (!strcmp(key_property->key_value[g_shift_state][0], "Enter"))
		{
			ime_send_key_event(IME_KEY_Return, IME_KEY_MASK_PRESSED, true);
			ime_send_key_event(IME_KEY_Return, IME_KEY_MASK_RELEASED, true);
		}
		break;
	}
	case IME_KEY_TYPE_MODECHANGE:
	{
		IMEKeyboardLayoutType layoutType = sample_native_ime_get_layout_type_by_name(key_property->key_value[g_shift_state][0]);
		if ((IME_KEYBOARD_LAYOUT_OPTION != layoutType) && (IME_KEYBOARD_LAYOUT_MAX != layoutType))
		{
			/* when mode change, set shift state off */
			g_shift_state = IME_SHIFT_STATE_OFF;
			sample_native_ime_update_layout(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_shift_state);
			sample_native_ime_layout_hide(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);
			g_current_ime_layout = layoutType;
			/* whether the layout has been initialized, if not, should initialize before showing */
			if (!g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout].fvalid)
			{
				char keyboard_edj_path[SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH] = {0};
				ime_app_app_get_resource(g_edje_file_path[g_current_ime_display_mode], keyboard_edj_path, SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH);
				sample_native_ime_layout_init(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_current_ime_display_mode, g_current_ime_layout);
				sample_native_ime_layout_create(g_ime_win, &g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], keyboard_edj_path, g_scale_ratio, g_shift_state);
			}
			ime_app_set_return_key_type(g_return_key_type);
			ime_app_set_return_key_disable(g_return_key_disabled);
			sample_native_ime_layout_show(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);
		}
		else if (IME_KEYBOARD_LAYOUT_OPTION == layoutType)
		{
			ime_create_option_window();
		}
		break;
	}
	case IME_KEY_TYPE_STRING:
	{
		char *key_value = NULL;
		if (g_long_key_pressed && (IME_KEY_TYPE_NONE != key_property->long_key_type))
			key_value = key_property->key_value[g_shift_state][1];
		else
			key_value = key_property->key_value[g_shift_state][0];
		/* different from send char */
		ime_commit_string(key_value);
		sample_native_ime_magnifier_win_hide();
		break;
	}
	default:
	{
		break;
	}
	}

	return EINA_TRUE;
}

/**
 * @brief when the key is long pressed, this callback function will be called
 *
 * @param key_property the key property of key
 * @param the data for key long press
 *
 * @return the key long press, success or not
 */
static Eina_Bool ime_app_key_long_press_cb(IMEKeyProperty *key_property, void *data)
{
	if ((IME_KEY_TYPE_CHAR == key_property->long_key_type) || (IME_KEY_TYPE_STRING == key_property->long_key_type))
		sample_native_ime_magnifier_win_show(key_property->x, key_property->y, key_property->width, key_property->height, key_property->sub_label[g_shift_state].label_text, g_scale_ratio);

	if (!strcmp(key_property->key_value[g_shift_state][0], "Back"))
	{
		ime_send_key_event(IME_KEY_BackSpace, IME_KEY_MASK_PRESSED, true);
		ime_send_key_event(IME_KEY_BackSpace, IME_KEY_MASK_RELEASED, true);
	}

	g_long_key_pressed = EINA_TRUE;
	return EINA_TRUE;
}

/**
 * @brief when ime created, this function will be called
 *
 * @param user_data the user data
 *
 */
static void ime_app_create_cb(void *user_data)
{
	int w, h;
	g_ime_win = ime_get_main_window();
	if (!g_ime_win)
	{
		dlog_print(DLOG_DEBUG, LOG_TAG, "Can't get main window: %d", get_last_result());
		return;
	}

	/* adjust the different screen resolution */
	elm_win_screen_size_get(g_ime_win, NULL, NULL, &w, &h);
	g_scale_ratio = MIN((double)w / SAMPLE_NATIVE_IME_IME_WIDTH_P, (double)h / SAMPLE_NATIVE_IME_IME_HEIGHT_P);
	ime_set_size(SAMPLE_NATIVE_IME_IME_WIDTH_P * g_scale_ratio, SAMPLE_NATIVE_IME_IME_HEIGHT_P * g_scale_ratio, SAMPLE_NATIVE_IME_IME_WIDTH_L * g_scale_ratio, SAMPLE_NATIVE_IME_IME_HEIGHT_L * g_scale_ratio);

	char keyboard_edj_path[SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH] = {0};
	ime_app_app_get_resource(g_edje_file_path[g_current_ime_display_mode], keyboard_edj_path, SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH);

	/* initialize layout and register call back function */
	g_key_event.key_down_cb = ime_app_key_down_cb;
	g_key_event.key_down_data = NULL;
	g_key_event.key_up_cb = ime_app_key_up_cb;
	g_key_event.key_up_data = NULL;
	g_key_event.key_long_press_cb = ime_app_key_long_press_cb;
	g_key_event.key_long_press_data = NULL;
	if (!g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout].fvalid)
	{
		sample_native_ime_layout_init(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_current_ime_display_mode, g_current_ime_layout);
		sample_native_ime_layout_create(g_ime_win, &g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], keyboard_edj_path, g_scale_ratio, g_shift_state);
	}
	sample_native_ime_set_key_events_callback(&g_key_event);
	sample_native_ime_feedback_initialize();
}

/**
 * @brief when app terminate, this function will be called
 *
 * @param user_data the user data
 *
 */
static void ime_app_terminate_cb(void *user_data)
{
	sample_native_ime_feedback_deinitialize();
	dlog_print(DLOG_DEBUG, LOG_TAG, "ime_app_terminate_cb");
}

/**
 * @brief when ime show, the show callback function will be called
 *
 * @param ic IMContext ID
 * @param context the context for ime
 * @param user_data the user data
 *
 */
static void ime_app_show_cb(int ic, ime_context_h context, void *user_data)
{
	Ecore_IMF_Input_Panel_Layout layout;
	ime_layout_variation_e layout_variation;
	int cursor_pos;
	Ecore_IMF_Autocapital_Type autocapital_type;
	Ecore_IMF_Input_Panel_Return_Key_Type return_key_type;
	bool return_key_state, prediction_mode, password_mode;

	dlog_print(DLOG_DEBUG, LOG_TAG, "%s, %d", "ime_app_show_cb", ic);

	if (ime_context_get_layout(context, &layout) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "layout: %d", layout);
	if (ime_context_get_layout_variation(context, &layout_variation) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "layout variation: %d", layout_variation);
	if (ime_context_get_cursor_position(context, &cursor_pos) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "cursor position: %d", cursor_pos);
	if (ime_context_get_autocapital_type(context, &autocapital_type) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "autocapital_type: %d", autocapital_type);
	if (ime_context_get_return_key_type(context, &return_key_type) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "return_key_type: %d", return_key_type);
	if (ime_context_get_return_key_state(context, &return_key_state) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "return_key_state: %d", return_key_state);
	if (ime_context_get_prediction_mode(context, &prediction_mode) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "prediction_mode: %d", prediction_mode);
	if (ime_context_get_password_mode(context, &password_mode) == IME_ERROR_NONE)
		dlog_print(DLOG_DEBUG, LOG_TAG, "password_mode: %d", password_mode);

	/* set return key type and disable status */
	ime_app_set_return_key_type(return_key_type);
	ime_app_set_return_key_disable(return_key_state);
	Evas_Object *ime_win = ime_get_main_window();
	if (!ime_win)
	{
		dlog_print(DLOG_DEBUG, LOG_TAG, "%d", get_last_result());
		return;
	}

	/* show ime */
	evas_object_show(ime_win);
	sample_native_ime_layout_show(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);
}

/*
 * @brief when ime hide, the show callback function will be called
 *
 * @param ic IMContext ID
 * @param user_data the user data
 *
 */
static void ime_app_hide_cb(int ic, void *user_data)
{
	Evas_Object *ime_win = ime_get_main_window();
	if (!ime_win)
	{
		dlog_print(DLOG_DEBUG, LOG_TAG, "%d", get_last_result());
		return;
	}

	evas_object_hide(ime_win);
	sample_native_ime_layout_hide(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);
}

static void ime_app_cursor_position_updated_cb(int cursor_pos, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "cursor position: %d", cursor_pos);
}

static void ime_app_focus_out_cb(int ic, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "focus out: %d", ic);
}

static void ime_app_focus_in_cb(int ic, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "focus in: %d", ic);
}

/**
 * @brief return key type callback function
 *
 * @param type the return key type
 * @param user_data the user data
 *
 */
static void ime_app_return_key_type_set_cb(Ecore_IMF_Input_Panel_Return_Key_Type type, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "Return key type: %d", type);
	ime_app_set_return_key_type(type);
}

/**
 * @brief retrun key state callback
 *
 * @param disabled the disable state
 * @user_data the user data
 *
 */
static void ime_app_return_key_state_set_cb(bool disabled, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "Return key disabled: %d", disabled);
	ime_app_set_return_key_disable(disabled);
}

static void ime_app_layout_set_cb(Ecore_IMF_Input_Panel_Layout layout, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "layout: %d", layout);
}

static bool ime_app_process_key_event_cb(ime_key_code_e keycode, ime_key_mask_e keymask, ime_device_info_h dev_info, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "keycode=0x%x, keymask=0x%x", keycode, keymask);

	if ((keymask & IME_KEY_MASK_CONTROL) || (keymask & IME_KEY_MASK_ALT) || (keymask & IME_KEY_MASK_META) || (keymask & IME_KEY_MASK_WIN) || (keymask & IME_KEY_MASK_HYPER))
		return false;

	return false;
}

static void ime_app_display_language_changed_cb(const char *language, void *user_data)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "language: %s", language);

	setlocale(LC_ALL, language);
	g_system_language = strdup(language);
}

/**
 * @brief ime rotation degree change callback
 *
 * @param the rotation degree
 * @user_data the user data
 *
 */
static void ime_app_rotation_degree_changed_cb(int degree, void *user_data)
{
	sample_native_ime_layout_hide(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);

	if ((0 == degree) || (180 == degree))
		g_current_ime_display_mode = IME_DISPLAY_MODE_PORTRAIT;
	else if ((90 == degree) || (270 == degree))
		g_current_ime_display_mode = IME_DISPLAY_MODE_LANDSCAPE;

	/* set the rotation degree and initialize the layout */
	sample_native_ime_set_screen_rotation_degree(degree);
	if (!g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout].fvalid)
	{
		char keyboard_edj_path[SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH] = {0};
		ime_app_app_get_resource(g_edje_file_path[g_current_ime_display_mode], keyboard_edj_path, SAMPLE_NATIVE_IME_MAX_SIZE_OF_PATH);
		sample_native_ime_layout_init(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], g_current_ime_display_mode, g_current_ime_layout);
		sample_native_ime_layout_create(g_ime_win, &g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout], keyboard_edj_path, g_scale_ratio, g_shift_state);
	}
	ime_app_set_return_key_type(g_return_key_type);
	ime_app_set_return_key_disable(g_return_key_disabled);
	sample_native_ime_layout_show(&g_sample_native_ime_layout[g_current_ime_display_mode][g_current_ime_layout]);
}

/*
 * @brief ime option window create callback
 *
 * @param window the option window
 * @param type the type of option window
 * @param user_data the user data
 *
 */
static void ime_app_option_window_created_cb(Evas_Object *window, ime_option_window_type_e type, void *user_data)
{
	sample_native_ime_option_window_created(window, type);
}

/* ime option window destroy callback */
static void ime_app_option_window_destroyed_cb(Evas_Object *window, void *user_data)
{
	sample_native_ime_option_window_destroyed();
}

/**
 * @brief get locale language
 *
 * @param the pointer of language string
 *
 * @return get locale language, success or not
 */
bool get_locale_language(char **language)
{
	char *setting_locale = NULL;
	int ret = system_settings_get_value_string(SYSTEM_SETTINGS_KEY_LOCALE_LANGUAGE, &setting_locale);
	if (ret != SYSTEM_SETTINGS_ERROR_NONE)
		return false;

	char *local_language = (char *)malloc(strlen(setting_locale) + strlen(".UTF-8") + 1);
	strncpy(local_language, setting_locale, strlen(setting_locale) + 1);
	strncat(local_language, ".UTF-8", strlen(".UTF-8") + 1);
	*language = strdup(local_language);

	free(setting_locale);
	free(local_language);

	return true;
}

#ifndef TEST_BUILD

void main(int argc, char **argv)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "Start main function !");

	ime_callback_s basic_callback = {
		ime_app_create_cb,
		ime_app_terminate_cb,
		ime_app_show_cb,
		ime_app_hide_cb,
	};

	/* Set locale */
	get_locale_language(&g_system_language);
	dlog_print(DLOG_DEBUG, LOG_TAG, "Initial locale : %s", g_system_language);
	set_system_language(g_system_language);

	/* Set the necessary callback functions */
	ime_event_set_focus_in_cb(ime_app_focus_in_cb, NULL);
	ime_event_set_focus_out_cb(ime_app_focus_out_cb, NULL);
	ime_event_set_cursor_position_updated_cb(ime_app_cursor_position_updated_cb, NULL);
	ime_event_set_layout_set_cb(ime_app_layout_set_cb, NULL);
	ime_event_set_return_key_type_set_cb(ime_app_return_key_type_set_cb, NULL);
	ime_event_set_return_key_state_set_cb(ime_app_return_key_state_set_cb, NULL);
	ime_event_set_process_key_event_cb(ime_app_process_key_event_cb, NULL);
	ime_event_set_display_language_changed_cb(ime_app_display_language_changed_cb, NULL);
	ime_event_set_rotation_degree_changed_cb(ime_app_rotation_degree_changed_cb, NULL);
	ime_event_set_option_window_created_cb(ime_app_option_window_created_cb, NULL);
	ime_event_set_option_window_destroyed_cb(ime_app_option_window_destroyed_cb, NULL);

	/* Start IME */
	ime_run(&basic_callback, NULL);

	return 0;
}

#endif
