#include <stdio.h>
#include <locale.h>
#include <sys/stat.h>

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

#include "property_data.h"

#include "imbean.h"

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

/*********************************************************************/
/*                       ImePropertyRec utilities                    */
/*********************************************************************/
#define IME_PROPERTY_LIST_NUM_ALLOC 10

static ImePropertyRec *ime_property_new()
{
    ImePropertyRec *ime_property = NULL;

    ime_property = (ImePropertyRec *) calloc(1, sizeof(ImePropertyRec));

    return ime_property;
}

void ime_property_destroy(ImePropertyRec * ime_property)
{
    if (ime_property == NULL)
	return;

    if (ime_property->key)
	free((char *) ime_property->key);

    if (ime_property->name)
	free((char *) ime_property->name);

    if (ime_property->tip)
	free((char *) ime_property->tip);

    if (ime_property->type == ImeProperty_Selection) {
	char **value_ptr;
	int i;
	value_ptr = ime_property->range.multiString_range;
	if (value_ptr != NULL) {
	    for (i = 0; value_ptr[i]; i++) {
		free((char *) value_ptr[i]);
	    }
	    free((char *) value_ptr);
	}
    }
}

void ime_property_print(ImePropertyRec * ime_property)
{
    if (ime_property == NULL)
	return;

    printf(" ime property: \n");

    if (ime_property->key)
	printf("  key: %s\n", (char *) ime_property->key);

    if (ime_property->name)
	printf("  name: %s\n", (char *) ime_property->name);

    if (ime_property->tip)
	printf("  tip: %s\n", (char *) ime_property->tip);

    if (ime_property->type)
	printf("  type: %d\n", (char *) ime_property->type);

    if (ime_property->type == ImeProperty_Selection) {
	char **value_ptr;
	int i;
	value_ptr = ime_property->range.multiString_range;
	if (value_ptr != NULL) {
	    for (i = 0; value_ptr[i]; i++) {
		printf("  options[%d]: %s\n", i, (char *) value_ptr[i]);
	    }
	}
    } else if (ime_property->type == ImeProperty_Int) {
	int min, max;
	printf("  min: %d\n", ime_property->range.int_range.min);
	printf("  max: %d\n", ime_property->range.int_range.max);
    }

    printf("\n");
}

ImePropertyListRec *ime_property_list_new()
{
    ImePropertyListRec *ime_property_list = NULL;

    ime_property_list =
	(ImePropertyListRec *) calloc(1, sizeof(ImePropertyListRec));

    return ime_property_list;
}

void ime_property_list_destroy(ImePropertyListRec * ime_property_list)
{
    int i;

    if (ime_property_list != NULL) {
        if (ime_property_list->properties != NULL) {
            for (i = 0; i < ime_property_list->count; i++) {
                ImePropertyRec *p = &(ime_property_list->properties[i]);
                ime_property_destroy(p);
            }
            free((char *) ime_property_list->properties);
        }
        free((char *) ime_property_list);
    }
}

void ime_property_list_print(ImePropertyListRec * ime_property_list)
{
#if DEBUG
    int i;

    if (ime_property_list != NULL) {
        if (ime_property_list->properties != NULL) {
            printf("ime_property_list: count: %d\n", ime_property_list->count);
            for (i = 0; i < ime_property_list->count; i++) {
                ImePropertyRec *p = &(ime_property_list->properties[i]);
                ime_property_print(p);
            }
        }
    }
#endif
}

int ime_property_list_prepare_memory(ImePropertyListRec *ime_property_list)
{
    int i, count;

    if (ime_property_list == NULL)
        return 0;

    if (ime_property_list->properties == NULL) {
        ime_property_list->properties =
            (ImePropertyRec *) calloc(IME_PROPERTY_LIST_NUM_ALLOC,
				      sizeof(ImePropertyRec));
        if (ime_property_list->properties == NULL)
            return 0;
    }

    count = ime_property_list->count;
    if ((count + 1) % IME_PROPERTY_LIST_NUM_ALLOC == 0) {
        int num = count + 1 + IME_PROPERTY_LIST_NUM_ALLOC;

	ime_property_list->properties =
	    (ImePropertyRec *) realloc(ime_property_list->properties,
                                       num * sizeof(ImePropertyRec));
        if (ime_property_list->properties == NULL)
            return 0;

        for (i = count; i < num; i++)
	    memset(&(ime_property_list->properties[i]),
                   0, sizeof(ImePropertyRec));
    }

    return 1;
}

int ime_property_list_add_new_item(ImePropertyListRec * ime_property_list,
				   char *key, char *options, char *type,
				   char *value)
{
    int i, ret;
    ImePropertyRec *ime_property;

    DEBUG_printf
	("ime_property_list_add_new_item: key: %s, options: %s, type: %s, value: %s\n",
	 key, options, type, value);
    if (ime_property_list == NULL)
	return;

    if (!key || !*key)
	return;

    if (!options || !*options)
	return;

    if (!type || !*type)
	return;

    if (!value || !*value)
	return;

    ret = ime_property_list_prepare_memory(ime_property_list);
    if (ret == 0)
        return;

    ime_property = &(ime_property_list->properties[ime_property_list->count]);

    ime_property->key = (char *) strdup(key);

    if (!strcasecmp(type, "ImeProperty_Toggle")) {
	ime_property->type = ImeProperty_Toggle;
    } else if (!strcasecmp(type, "ImeProperty_Int")) {
	ime_property->type = ImeProperty_Int;
    } else if (!strcasecmp(type, "ImeProperty_Selection")) {
	ime_property->type = ImeProperty_Selection;
    }

    if (ime_property->type == ImeProperty_Toggle) {
	ime_property->name = (char *) strdup(options);
    } else if (ime_property->type == ImeProperty_Int) {
	char *name, *min_str, *max_str;

	name = options;
	min_str = strchr(options, '/');
	max_str = NULL;
	if (min_str != NULL) {
	    *min_str = 0;
	    min_str++;
	    max_str = strchr(min_str, '/');
	    if (max_str != NULL) {
		*max_str = 0;
		max_str++;
	    }
	}

	if (min_str != NULL && max_str != NULL) {
	    ime_property->name = (char *) strdup(name);
	    ime_property->range.int_range.min = atoi(min_str);
	    ime_property->range.int_range.max = atoi(max_str);
	}
    } else if (ime_property->type == ImeProperty_Selection) {
	char *name, *option_ptr;
	char *ptr;
	char **multiString_range;
	int option_num = 0;
	int option_id;

	name = options;
	option_ptr = options;
	while (option_ptr) {
	    option_ptr = strchr(option_ptr, '/');
	    if (option_ptr) {
		option_num++;
		option_ptr++;
	    }
	}

	DEBUG_printf("option_num: %d\n", option_num);
	if (option_num == 0)
	    return;

	multiString_range =
	    (char **) calloc(option_num + 1, sizeof(char *));

	name = options;
	option_ptr = strchr(options, '/');
	if (option_ptr != NULL) {
	    *option_ptr = 0;
	    option_ptr++;
	    ime_property->name = (char *) strdup(name);
	}

	option_id = 0;
	do {
	    ptr = strchr(option_ptr, '/');
	    if (ptr)
		*ptr = 0;

	    DEBUG_printf("option_ptr : %s, ptr: %p\n", option_ptr, ptr);
	    multiString_range[option_id] = (char *) strdup(option_ptr);
	    option_id++;
	    option_ptr = ptr + 1;
	} while (ptr);

	ime_property->range.multiString_range = multiString_range;
    }

    ime_property->value.int_value = atoi(value);
    ime_property_list->count++;
}

/*********************************************************************/
/*                       ime_module_t utilities                      */
/*********************************************************************/
static ime_module_t *ime_module_new()
{
    ime_module_t *ime_module = NULL;

    ime_module = (ime_module_t *) calloc(1, sizeof(ime_module_t));

    return ime_module;
}

void ime_module_destroy(ime_module_t * ime_module)
{
    if (ime_module == NULL)
	return;

    if (ime_module->uuid)
	free((char *) ime_module->uuid);

    if (ime_module->name)
	free((char *) ime_module->name);

    if (ime_module->author)
	free((char *) ime_module->author);

    if (ime_module->copyright)
	free((char *) ime_module->copyright);

    if (ime_module->hinting)
	free((char *) ime_module->hinting);

    if (ime_module->icon_file)
	free((char *) ime_module->icon_file);

    if (ime_module->icon_pixbuf)
	gdk_pixbuf_unref(ime_module->icon_pixbuf);

    if (ime_module->property_list)
	ime_property_list_destroy(ime_module->property_list);

    if (ime_module->vkbs) {
	int i;
	for (i = 0; i < ime_module->num_vkbs; i ++)
	    vkb_layout_destroy (ime_module->vkbs[i]);
	free((char *)ime_module->vkbs);
    }

    free ((char *)ime_module);
}

void ime_module_print(ime_module_t * ime_module)
{
#if DEBUG
    if (ime_module == NULL)
	return;

    printf("ime: \n");
    printf("  version: %d\n", ime_module->version);

    if (ime_module->uuid)
	printf("  uuid: %s\n", (char *) ime_module->uuid);

    if (ime_module->name)
	printf("  name: %s\n", (char *) ime_module->name);

    if (ime_module->author)
	printf("  author: %s\n", (char *) ime_module->author);

    if (ime_module->copyright)
	printf("  copyright: %s\n", (char *) ime_module->copyright);

    if (ime_module->hinting)
	printf("  hinting: %s\n", (char *) ime_module->hinting);

    if (ime_module->icon_file)
	printf("  icon_file: %s\n", (char *) ime_module->icon_file);

    printf("  icon_pixbuf: %p.\n", ime_module->icon_pixbuf);
    printf("  enabled:  %d.\n", ime_module->enabled);

    printf("  property_list:  %p.\n", ime_module->property_list);
    if (ime_module->property_list)
	ime_property_list_print(ime_module->property_list);

    if (ime_module->vkbs) {
	int i;
	for (i = 0; i < ime_module->num_vkbs; i ++)
	    vkb_layout_print (ime_module->vkbs[i]);
    }

    printf("\n");
#endif
}

#define VKB_LAYOUT_NUM_ALLOC   6
int ime_module_pushback_vkb(ime_module_t *ime_module, vkb_layout_t *vkb)
{
    int i, num_vkbs;

    if (ime_module == NULL || vkb == NULL)
        return 0;

    if (ime_module->vkbs == NULL) {
        ime_module->vkbs = (vkb_layout_t **)calloc (VKB_LAYOUT_NUM_ALLOC,
                                                    sizeof(vkb_layout_t *));
        if (ime_module->vkbs == NULL)
            return 0;
    }

    num_vkbs = ime_module->num_vkbs;
    if ((num_vkbs + 1) % VKB_LAYOUT_NUM_ALLOC == 0) {
        int num = num_vkbs + 1 + VKB_LAYOUT_NUM_ALLOC;

        ime_module->vkbs = (vkb_layout_t **)realloc(ime_module->vkbs,
                                             num * sizeof(vkb_layout_t *));
        if (ime_module->vkbs == NULL)
            return 0;

        for (i = num_vkbs; i < num; i++)
            ime_module->vkbs[i] = NULL;
    }

    ime_module->vkbs[num_vkbs] = vkb;
    ime_module->num_vkbs ++;

    return 1;
}

/*********************************************************************/
/*                    property_data_t utilities                      */
/*********************************************************************/

property_data_t *property_data_new()
{
    property_data_t *property_data = NULL;

    property_data = (property_data_t *) calloc(1, sizeof(property_data_t));
    if (property_data == NULL)
	return NULL;

    property_data->modified_flag   = 0;

    property_data->show_ime_button = 1;
    property_data->show_qjbj_button = 1;
    property_data->show_punct_button = 1;
    property_data->show_vkb_button = 1;
    property_data->show_utility_button = 1;
    property_data->show_with_vertical = 0;

    property_data->pos_x_palette = 0;
    property_data->pos_y_palette = 0;
    
    property_data->tooltips_enabled = 1;
    property_data->beep_enabled = 1;

    property_data->fkey_vkb = 'K';

    property_data->composite_style = 1;

    property_data->num_ime_modules = 0;
    property_data->ime_modules = NULL;

    return property_data;
}

void property_data_destroy(property_data_t * property_data)
{
    int i;

    if (property_data == NULL)
	return;

    if (property_data->ime_modules)  {
	for (i = 0; i < property_data->num_ime_modules; i++) {
	    ime_module_destroy(property_data->ime_modules[i]);
	}
	free ((char *)property_data->ime_modules);
    }

    free ((char *)property_data);
}

void property_data_print(property_data_t * property_data)
{
#if DEBUG
    int i;

    if (property_data == NULL)
	return;

    printf(" modified_flag:        %d\n", property_data->modified_flag);

    printf(" show_ime_button:      %d\n", property_data->show_ime_button);
    printf(" show_qjbj_button:     %d\n", property_data->show_qjbj_button);
    printf(" show_punct_button:    %d\n", property_data->show_punct_button);
    printf(" show_vkb_button:      %d\n", property_data->show_vkb_button);
    printf(" show_utility_button:  %d\n", property_data->show_utility_button);
    printf(" show_with_vertical:   %d\n", property_data->show_with_vertical);

    printf(" pos_x_palette:        %d\n", property_data->pos_x_palette);
    printf(" pos_y_palette:        %d\n", property_data->pos_y_palette);

    printf(" beep_enabled:         %d\n", property_data->beep_enabled);

    printf(" composite_style:      %d\n", property_data->composite_style);

    if (property_data->ime_modules)  {
	for (i = 0; i < property_data->num_ime_modules; i++) {
	    ime_module_print(property_data->ime_modules[i]);
	}
    }
#endif
}

void property_data_set_enabled_by_uuid(property_data_t *property_data,
				       char *uuid, int enabled)
{
    int i;
    ime_module_t *ime_module;

    if (property_data == NULL)
	return;

    for (i = 0; i < property_data->num_ime_modules; i++) {
	ime_module = property_data->ime_modules[i];

	if (ime_module == NULL || ime_module->uuid == NULL)
	    continue;

	if (!strcmp(ime_module->uuid, uuid))
	    ime_module->enabled = enabled;
    }

    return;
}

ime_module_t *property_data_get_ime_module_by_uuid(property_data_t *property_data,
						   char *uuid)
{
    int i;
    ime_module_t *ime_module;

    if (property_data == NULL)
	return NULL;

    for (i = 0; i < property_data->num_ime_modules; i++) {
	ime_module = property_data->ime_modules[i];

	if (ime_module == NULL || ime_module->uuid == NULL)
	    continue;

	if (!strcmp(ime_module->uuid, uuid))
	    return ime_module;
    }

    return NULL;
}

void property_data_merge(property_data_t *target,
			 property_data_t *source)
{
    if (target == NULL || source == NULL)
        return;

    target->time_stamp = source->time_stamp;
    target->fkey_vkb   = source->fkey_vkb;

    target->show_ime_button     = source->show_ime_button;
    target->show_qjbj_button    = source->show_qjbj_button;
    target->show_punct_button   = source->show_punct_button;
    target->show_vkb_button     = source->show_vkb_button;
    target->show_utility_button = source->show_utility_button;
    target->show_with_vertical  = source->show_with_vertical;

    target->tooltips_enabled    = source->tooltips_enabled;
    target->beep_enabled        = source->beep_enabled;
}

#define IME_MODULE_LIST_NUM_ALLOC 10
int property_data_pushback_ime_module(property_data_t *property_data,
                                      ime_module_t *ime_module)
{
    int i, num_ime_modules;

    if (property_data == NULL || ime_module == NULL)
        return 0;

    if (property_data->ime_modules == NULL) {
        property_data->ime_modules = (ime_module_t **)
                                          calloc (IME_MODULE_LIST_NUM_ALLOC,
                                                  sizeof(ime_module_t *));
        if (property_data->ime_modules == NULL)
            return 0;
    }

    num_ime_modules = property_data->num_ime_modules;
    if ((num_ime_modules + 1) % IME_MODULE_LIST_NUM_ALLOC == 0) {
        int num = num_ime_modules + 1 + IME_MODULE_LIST_NUM_ALLOC;

        property_data->ime_modules = (ime_module_t **)realloc(property_data->ime_modules,
                                                              num * sizeof(ime_module_t *));
        if (property_data->ime_modules == NULL)
            return 0;

        for (i = num_ime_modules; i < num; i++)
            property_data->ime_modules[i] = NULL;
    }

    property_data->ime_modules[num_ime_modules] = ime_module;
    property_data->num_ime_modules ++;

    return 1;
}

/*********************************************************************/
/*      property_data_t utilities to save/get from user profile      */
/*********************************************************************/

#define USER_PROFILE_PATH ".iiim"
#define USER_PROFILE_NAME "ime_config.xml"

#define ALL_ENGINE_CATAGORY  "all_engines"
#define ENGINE_PROPERTIES_CATAGORY  "engine_properties"
#define SYSTEM_PROPERTIES_CATAGORY  "le_settings"
#define STATUSBAR_PROPERTIES_CATAGORY  "statusbar_properties"
#define IIIMSERVER_PROPERTIES_CATAGORY "iiim_properties"

char *property_data_parse_ime_vkb_info(ime_module_t *ime_module,
				       char *options)
{
    int i, ret;
    vkb_layout_t *vkb;

    char label_str[256];
    char key_value_pair[256];
    char keymapping_str[1024];
    char new_option_str[1024];
    char *p;

    if (ime_module == NULL)
        return NULL;

    if (!options || !*options)
        return NULL;

    /* make sure to clean the old vkb infos in ime_module */
    if (ime_module->vkbs != NULL) {
        for (i = 0; i < ime_module->num_vkbs; i++) {
            vkb_layout_destroy (ime_module->vkbs[i]);
        }

        free ((char *) ime_module->vkbs);
        ime_module->num_vkbs = 0;
    }

    /* begin parse option string      */
    /* two tasks:                     */
    /*    1. create vkb_layout list   */
    /*    2. recreate new options str */
    p = options;

    memset(new_option_str, 0, 1024);

    /* get label_str */
    i = 0;
    memset (label_str, 0, 256);
    while (*p) {
        if (i >= 256 || (*p == '/' && (i == 0 || *(p-1) != '\\')))
            break;

        label_str[i++] = *(p ++);
    }
    DEBUG_printf("lable_str:  %s\n", label_str);
    strncpy(new_option_str, label_str, 1024);

    if (!*p || *p != '/')
        return NULL;

    p ++;
    
    while (*p) {
        char *ptr;
        char vkb_name[256];
        char vkb_key_pair[256];

        /* try to get keymapping string, like:  vkb_name|aA|bB|... */
        i = 0;
	memset(keymapping_str, 0, 1024);
        while (*p) {
            if (i >= 1024 || (*p == '/' && (i == 0 || *(p-1) != '\\')))
                break;

            keymapping_str[i++] = *(p ++);
        }

        /* process keymapping string */
        if (*keymapping_str) {
            DEBUG_printf("keymapping_str:  %s\n", keymapping_str);

            ptr = keymapping_str;

            /* try to get vkb name string, like:  vkb_name */
            i = 0;
	    memset(vkb_name, 0, 256);
            while (*ptr) {
                if (i >= 256 || (*ptr == '|' && (i == 0 || *(ptr-1) != '\\')))
                    break;

                vkb_name[i++] = *(ptr ++);
            }

            if (*vkb_name) {
                strncat(new_option_str, "/", 1024);
                strncat(new_option_str, vkb_name, 1024);

                /* create vkb_layout */
		if (*ptr == '|') {
                    DEBUG_printf("key_value_pair: %s\n", ptr);
                    vkb = (vkb_layout_t *) vkb_layout_new();
                    if (vkb == NULL)
                        break;

                    vkb->type = KEYBOARD_KEY_TYPE;
                    vkb_layout_set_keyboard_ename(vkb, "keymap");
                    vkb_layout_set_keyboard_label(vkb, vkb_name);
                    vkb_layout_set_ctrlkey_label(vkb);

                    ptr++;
                    while (*ptr) {
                        i = 0;
                        memset(key_value_pair, 0, 256);

                        if (*ptr == '\\' &&
                            (*(ptr+1) == '/' || *(ptr+1) == '|'))
                            ptr++;

                        key_value_pair[i++] = *ptr++;
                        key_value_pair[i++] = 0x20;
    
                        if (!*ptr) break;

                        while (*ptr) {
                            if (i >= 256 || (*ptr == '|' && (i == 0 || *(ptr-1) != '\\')))
                                break;

                            key_value_pair[i++] = *(ptr ++);
                        }

                        DEBUG_printf(" key_value_pair: %s\n", key_value_pair);
                        vkb_layout_set_basekey_label(vkb, key_value_pair);

                        if (*ptr != '|')
                            break;

                        ptr ++;
                    }

                    vkb_layout_print(vkb);
                    ret = ime_module_pushback_vkb(ime_module, vkb);
                    if (ret == 0) {
                        vkb_layout_destroy(vkb);
                    }
                }
            }
        }

        if (!*p || *p != '/')
            break;

        p ++;
    }

    if (ime_module->num_vkbs > 1) {
	int len = strlen (options) + 1;
        strncpy(options, new_option_str, len);
        return options;
    }

    return NULL;
}

int property_data_parser_ime_property(ime_module_t *ime_module,
				      IbmlProperty *ibml_property)
{
    char *name, *value, *type, *options;

    name = (char *) ibml_property->name;
    if (!name || !*name)
	return;

    value = (char *) ibml_property->value;
    if (!value || !*value)
	return;

    if (!strcasecmp(name, "name")) {
	if (ime_module->name)
	    free ((char *)ime_module->name);
	ime_module->name = (char *) strdup(value);
	return;
    } else if (!strcasecmp(name, "version")) {
	if (ime_module->version)
	    free ((char *)ime_module->version);
	ime_module->version = atoi (value);
	return;
    } else if (!strcasecmp(name, "copyright")) {
	if (ime_module->copyright)
	    free ((char *)ime_module->copyright);
	ime_module->copyright = (char *) strdup(value);
	return;
    } else if (!strcasecmp(name, "author")) {
	if (ime_module->author)
	    free ((char *)ime_module->author);
	ime_module->author = (char *) strdup(value);
	return;
    } else if (!strcasecmp(name, "hinting")) {
	if (ime_module->hinting)
	    free ((char *)ime_module->hinting);
	ime_module->hinting = (char *) strdup(value);
	return;
    } else if (!strcasecmp(name, "icon_file")) {
	if (ime_module->icon_file)
	    free ((char *)ime_module->icon_file);
	ime_module->icon_file = (char *) strdup(value);
	return;
    } else if (!strcasecmp(name, "enabled")) {
	char *enabled_str = (char *) value;
	ime_module->enabled = 1;
	if (enabled_str && (!strcasecmp(enabled_str, "false"))) {
	    ime_module->enabled = 0;
	}
	return;
    }

    options = (char *) ibml_property->options;
    if (!options || !*options)
	return;

    type = (char *) ibml_property->type;
    if (!type || !*type)
	return;

    /* parse keymapping info */
    if (!strcasecmp(name, "/keymapping")) {
	options = property_data_parse_ime_vkb_info(ime_module, options);
    }

    if (!options || !*options)
	return;

    /* add IME property list */
    if (ime_module->property_list == NULL) {
	ime_module->property_list = (ImePropertyListRec *)ime_property_list_new();
    }

    ime_property_list_add_new_item(ime_module->property_list,
				   name, options, type, value);

    return;
}

int property_data_parser_iiimserver_properties(property_data_t *property_data,
					       IbmlProperty *ibml_property)
{
    char *name, *value;

    name = (char *) ibml_property->name;
    if (!name || !*name)
	return;

    value = (char *) ibml_property->value;
    if (!value || !*value)
	return;

    DEBUG_printf("property_data_parser_iiimserver_properties ===\n");
    if (!strcasecmp(name, "time_stamp")) {
	property_data->time_stamp = atol(value);
    } else if (!strcasecmp(name, "shortcutkey_vkb")) {
	property_data->fkey_vkb = value[0];
    }

    return;
}

int property_data_parser_statusbar_properties(property_data_t *property_data,
					      IbmlProperty *ibml_property)
{
    char *name, *value;

    name = (char *) ibml_property->name;
    if (!name || !*name)
	return;

    value = (char *) ibml_property->value;
    if (!value || !*value)
	return;

    DEBUG_printf("property_data_parser_statusbar_properties ===\n");
    if (!strcasecmp(name, "show_ime_button")) {
	property_data->show_ime_button = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->show_ime_button = 0;
	}
    } else if (!strcasecmp(name, "show_qjbj_button")) {
	property_data->show_qjbj_button = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->show_qjbj_button = 0;
	}
    } else if (!strcasecmp(name, "show_punct_button")) {
	property_data->show_punct_button = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->show_punct_button = 0;
	}
    } else if (!strcasecmp(name, "show_vkb_button")) {
	property_data->show_vkb_button = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->show_vkb_button = 0;
	}
    } else if (!strcasecmp(name, "show_utility_button")) {
	property_data->show_utility_button = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->show_utility_button = 0;
	}
    } else if (!strcasecmp(name, "show_with_vertical")) {
	property_data->show_with_vertical = 0;
	if (!strcasecmp(value, "true")) {
	    property_data->show_with_vertical = 1;
	}
    } else if (!strcasecmp(name, "beep_enabled")) {
	property_data->beep_enabled = 1;
	if (!strcasecmp(value, "false")) {
	    property_data->beep_enabled = 0;
	}
    }

    return;
}

void property_data_get_system_properties_info(property_data_t *property_data,
					      IbmlCatagory *ibml_catagory)
{
    IbmlElement *ibml_element;
    IbmlProperty *ibml_property;

    int i, j;

    char *id, *scope;

    if (ibml_catagory == NULL)
	return;
    if (ibml_catagory->num_elements <= 0)
	return;

    for (i = 0; i < ibml_catagory->num_elements; i++) {
	ibml_element = ibml_catagory->elements[i];
	if (!ibml_element)
	    continue;
	if (ibml_element->num_properties <= 0)
	    continue;

	id = (char *) ibml_element->id;
	scope = (char *) ibml_element->scope;
	DEBUG_printf("id:%s, scope:%s\n", id ? id : "NULL",
	       scope ? scope : "NULL");
	if (!id || !*id)
	    continue;
  
	if (!strcasecmp(id, IIIMSERVER_PROPERTIES_CATAGORY)) {
	    for (j = 0; j < ibml_element->num_properties; j++) {
	        ibml_property = ibml_element->properties[j];
	        if (!ibml_property)
		    continue;

	        property_data_parser_iiimserver_properties(property_data,
							   ibml_property);
	    }
	} else if (!strcasecmp(id, STATUSBAR_PROPERTIES_CATAGORY)) {
	    for (j = 0; j < ibml_element->num_properties; j++) {
	        ibml_property = ibml_element->properties[j];
	        if (!ibml_property)
		    continue;

	        property_data_parser_statusbar_properties(property_data,
							  ibml_property);
	    }
	}
    }

    return;
}

void property_data_get_ime_modules_info(property_data_t *property_data,
					IbmlCatagory *ibml_catagory)
{
    ime_module_t *ime_module;

    IbmlElement *ibml_element;
    IbmlProperty *ibml_property;

    int i, j;

    char *id, *scope;

    if (ibml_catagory == NULL)
	return;
    if (ibml_catagory->num_elements <= 0)
	return;

    for (i = 0; i < ibml_catagory->num_elements; i++) {
	ibml_element = ibml_catagory->elements[i];
	if (!ibml_element)
	    continue;
	if (ibml_element->num_properties <= 0)
	    continue;

	id = (char *) ibml_element->id;
	scope = (char *) ibml_element->scope;
	DEBUG_printf("id:%s, scope:%s\n", id ? id : "NULL",
	       scope ? scope : "NULL");
	if (!id || !*id)
	    continue;

	ime_module = (ime_module_t *) ime_module_new();
	if (ime_module == NULL)
	    continue;

	ime_module->uuid = (char *) strdup(id);

	for (j = 0; j < ibml_element->num_properties; j++) {
	    ibml_property = ibml_element->properties[j];
	    if (!ibml_property)
		continue;

	    property_data_parser_ime_property(ime_module, ibml_property);
	}

	if (ime_module->uuid == NULL ||
	    ime_module->name == NULL ||
	    ! property_data_pushback_ime_module(property_data, ime_module)) {
	    ime_module_destroy (ime_module);
	    continue;
        }
    }

    return;
}

property_data_t *property_data_get_from_xmlstr(char *xml_str, int size)
{
    property_data_t *property_data;

    IbmlData *ibml_data;
    IbmlCatagory *ibml_catagory;
    int i;

    if (xml_str == NULL || size <= 0)
	return NULL;

    ibml_data = (IbmlData *) imbean_config_new_from_memory(xml_str, size);
    if (ibml_data == NULL)
	return NULL;

    property_data = (property_data_t *) property_data_new();
    if (property_data == NULL) {
	ibml_data_free(ibml_data);
	return NULL;
    }

    for (i = 0; i < ibml_data->num_catagories; i++) {
	ibml_catagory = ibml_data->catagories[i];

	if (!ibml_catagory->scope || !*ibml_catagory->scope)
	    continue;

	if (!strcasecmp(ibml_catagory->scope, ALL_ENGINE_CATAGORY)) {
	    property_data_get_ime_modules_info(property_data, ibml_catagory);
	} else if (!strcasecmp(ibml_catagory->scope, SYSTEM_PROPERTIES_CATAGORY)) {
	    property_data_get_system_properties_info(property_data, ibml_catagory);
	}
    }

    ibml_data_free(ibml_data);
    return property_data;
}

char *property_data_composite_to_xmlstr(property_data_t *property_data, int type)
{
    xmlDocPtr doc;
    xmlAttrPtr attr;
    xmlNodePtr root;
    xmlNodePtr im_node;
    xmlNodePtr imbean_node;
    xmlNodePtr property_node;
    xmlChar *xmlbuf;
    int i, j, size;
    char *enabled_str;
    char tmp_buf[256];

    DEBUG_printf("property_data_composite_to_xmlstr:\n");

    if (property_data == NULL)
	return NULL;

    doc = xmlNewDoc("1.0");

    attr = xmlNewDocProp(doc, "encoding", "UTF-8");

    root = xmlNewNode(NULL, IBML_ELEMENT_ROOT);
    xmlSetProp(root, IBML_ELEMENT_SCOPE, LE_NAME);
    xmlDocSetRootElement(doc, root);

    doc->children = root;

    /* For "le_settings" category */
    if (type != PROPERTY_DATA_FOR_SERVER_NOTIFY_WITH_SUBSET) {
        im_node = xmlNewChild(root, NULL, IBML_ELEMENT_CATAGORY, NULL);
        xmlSetProp(im_node, IBML_ELEMENT_SCOPE, SYSTEM_PROPERTIES_CATAGORY);

        imbean_node = xmlNewChild(im_node, NULL, IBML_ELEMENT_IMBEAN, NULL);
        xmlSetProp(imbean_node, IBML_ELEMENT_IMBEAN_ID, IIIMSERVER_PROPERTIES_CATAGORY);

        property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
        xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "time_stamp");
        snprintf(tmp_buf, 256, "%d", property_data->time_stamp);
        xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, tmp_buf);

        property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
        xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "shortcutkey_vkb");
        snprintf(tmp_buf, 256, "%c", property_data->fkey_vkb);
        xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, tmp_buf);

        if (type == PROPERTY_DATA_FOR_LOCAL_PROFILE) {
            imbean_node = xmlNewChild(im_node, NULL, IBML_ELEMENT_IMBEAN, NULL);
            xmlSetProp(imbean_node, IBML_ELEMENT_IMBEAN_ID, STATUSBAR_PROPERTIES_CATAGORY);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_ime_button");
            enabled_str = "true";
            if (property_data->show_ime_button == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_qjbj_button");
            enabled_str = "true";
            if (property_data->show_qjbj_button == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_punct_button");
            enabled_str = "true";
            if (property_data->show_punct_button == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_vkb_button");
            enabled_str = "true";
            if (property_data->show_vkb_button == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_utility_button");
            enabled_str = "true";
            if (property_data->show_utility_button == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "show_with_vertical");
            enabled_str = "true";
            if (property_data->show_with_vertical == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

            property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "beep_enabled");
            enabled_str = "true";
            if (property_data->beep_enabled == 0)
                enabled_str = "false";
            xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);
        }
    }

    /* For "all_engines" category */
    im_node = xmlNewChild(root, NULL, IBML_ELEMENT_CATAGORY, NULL);
    if (type == PROPERTY_DATA_FOR_SERVER_NOTIFY_WITH_SUBSET) {
        xmlSetProp(im_node, IBML_ELEMENT_SCOPE, ENGINE_PROPERTIES_CATAGORY);
    } else {
        xmlSetProp(im_node, IBML_ELEMENT_SCOPE, ALL_ENGINE_CATAGORY);
    }

    for (i = 0; i < property_data->num_ime_modules; i++) {
	ime_module_t *ime_module;
	ImePropertyListRec *property_list;

	ime_module = (ime_module_t *) property_data->ime_modules[i];

	imbean_node = xmlNewChild(im_node, NULL, IBML_ELEMENT_IMBEAN, NULL);
	xmlSetProp(imbean_node, IBML_ELEMENT_IMBEAN_ID,
		   (char *) ime_module->uuid);

        if (type == PROPERTY_DATA_FOR_LOCAL_PROFILE) {
            if (ime_module->name) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "name");
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, ime_module->name);
            }

            if (ime_module->author) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "author");
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, ime_module->author);
            }

            if (ime_module->copyright) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "copyright");
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, ime_module->copyright);
            }

            if (ime_module->version) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "version");
                snprintf(tmp_buf, 256, "%d", ime_module->version);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, tmp_buf);
            }

            if (ime_module->hinting) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "hinting");
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, ime_module->hinting);
            }

            if (ime_module->icon_file) {
                property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "icon_file");
                xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, ime_module->icon_file);
            }
        }

	property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
	xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, "enabled");
	enabled_str = "true";
	if (ime_module->enabled == 0)
	    enabled_str = "false";
	xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, enabled_str);

	property_list = ime_module->property_list;
	if (property_list) {
	    for (j = 0; j < property_list->count; j++) {
		char buf[10];

		ImePropertyRec *ime_property = &(property_list->properties[j]);
		if (ime_property == NULL)
		    continue;

		property_node = xmlNewChild(imbean_node, NULL, IBML_ELEMENT_PROPERTY, NULL);
		xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_NAME, ime_property->key);

                if (type == PROPERTY_DATA_FOR_LOCAL_PROFILE) {
                    char options_str[1024];
                    char *options_type = "ImeProperty_Toggle";

                    options_str[0] = 0;
                    if (ime_property->type == ImeProperty_Toggle) {
                        options_type = "ImeProperty_Toggle";
                        strncat(options_str, ime_property->name, 1024);
                    } else if (ime_property->type == ImeProperty_Selection) {
                        char **value_ptr;
                        int k;

                        options_type = "ImeProperty_Selection";

                        strncat(options_str, ime_property->name, 1024);
                        value_ptr = ime_property->range.multiString_range;
                        if (value_ptr != NULL) {
                            for (k = 0; value_ptr[k] != NULL; k++) {
                                strncat(options_str, "/", 1024);
                                strncat(options_str, value_ptr[k], 1024);
                            }
                        }
                    } else if (ime_property->type == ImeProperty_Int) {
                        char value[12];

                        options_type = "ImeProperty_Int";

                        strncat(options_str, ime_property->name, 1024);
                        strncat(options_str, "/", 1024);
                        snprintf(value, 12, "%d/%d",
                                 ime_property->range.int_range.min,
                                 ime_property->range.int_range.max);
                        strncat(options_str, value, 1024);
                    }

                    xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_OPTIONS,
                               options_str);
                    xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_TYPE,
                               options_type);
                }

		snprintf(buf, 10, "%d", ime_property->value.int_value);
		xmlSetProp(property_node, IBML_ELEMENT_PROPERTY_VALUE, buf);
	    }
	}
    }

    xmlDocDumpFormatMemoryEnc(doc, &xmlbuf, &size, "UTF-8", 1);

    xmlFreeDoc(doc);

    return xmlbuf;
}

int property_data_create_local_profile_directory()
{
    int ret;
    struct stat file_stat;
    char path_name[256], tmp_name[256];
    char *locale_name;

    /* Create Path $(HOME)/.iiim */
    snprintf(path_name, 256, "%s/%s", (char *) getenv("HOME"),
	    USER_PROFILE_PATH);
    DEBUG_printf("path_name: %s\n", path_name);

    ret = stat(path_name, &file_stat);
    if (ret == -1) {
	DEBUG_printf("%s not exist\n", path_name);
	ret = mkdir(path_name, 0777);
	if (ret == -1) {
	    DEBUG_printf("can not create path %s\n", path_name);
	    return -1;
	}
    }

    /* Create Path $(HOME)/.iiim/<locale> */
    locale_name = setlocale(LC_CTYPE, NULL);
    snprintf(tmp_name, 256, "%s/%s", path_name, locale_name);
    strncpy(path_name, tmp_name, 256);

    ret = stat(path_name, &file_stat);
    if (ret == -1) {
	DEBUG_printf("%s not exist\n", path_name);
	ret = mkdir(path_name, 0777);
	if (ret == -1) {
	    DEBUG_printf("can not create path %s\n", path_name);
	    return -1;
	}
    }

    /* Create Path $(HOME)/.iiim/<locale>/$LE_NAME */
    snprintf(tmp_name, 256, "%s/%s", path_name, LE_NAME);
    strncpy(path_name, tmp_name, 256);
    DEBUG_printf("path_name: %s\n", path_name);

    ret = stat(path_name, &file_stat);
    if (ret == -1) {
	DEBUG_printf("%s not exist\n", path_name);
	ret = mkdir(path_name, 0777);
	if (ret == -1) {
	    DEBUG_printf("can not create path %s\n", path_name);
	    return -1;
	}
    }

    return 0;
}

gboolean property_data_save_to_profile(property_data_t *property_data)
{
    FILE *fd;
    int ret;
    struct stat file_stat;
    char path_name[256], tmp_name[256];
    char *locale_name;
    char *property_info_str = NULL;

    property_data->modified_flag = 0;
    property_info_str = (char *) property_data_composite_to_xmlstr(property_data,
	PROPERTY_DATA_FOR_LOCAL_PROFILE);
    if (property_info_str == NULL)
	return FALSE;

    /* Create Path $(HOME)/.iiim/<locale>/$LE_NAME */
    locale_name = setlocale(LC_CTYPE, NULL);
    snprintf(path_name, 256, "%s/%s/%s/%s", getenv("HOME"),
	    USER_PROFILE_PATH, locale_name, LE_NAME);
    DEBUG_printf("path name :%s\n", path_name);

    ret = stat(path_name, &file_stat);
    if (ret == -1) {
	DEBUG_printf("%s not exist\n", path_name);
	ret = property_data_create_local_profile_directory();
	if (ret == -1) {
	    DEBUG_printf("can not create path %s\n", path_name);
	    return FALSE;
	}
    }

    /* Create profile file: $(HOME)/.iiim/<locale>/$LE_NAME/ime_config.xml */
    snprintf(tmp_name, 256, "%s/%s", path_name, USER_PROFILE_NAME);
    strncpy(path_name, tmp_name, 256);
    DEBUG_printf("path_name: %s\n", path_name);
    fd = fopen(path_name, "wb");
    if (!fd) {
	DEBUG_printf("unable open the file: %s\n", path_name);
	return FALSE;
    }

    fprintf(fd, "%s\n", property_info_str);
    free((char *) property_info_str);

    fclose(fd);
    return TRUE;
}

int property_data_read_from_profile_to_memory(char **property_info_ptr)
{
    char file_name[256];
    struct stat file_stat;
    int ret, file_size;
    FILE *fd;
    char *locale_name;

    char *property_info_str = NULL;

    *property_info_ptr = NULL;

    /* read profile from file to memory buffer  */
    locale_name = setlocale(LC_CTYPE, NULL);
    snprintf(file_name, 256, "%s/%s/%s/%s/%s", getenv("HOME"),
	    USER_PROFILE_PATH, locale_name, LE_NAME, USER_PROFILE_NAME);
    DEBUG_printf("file name :%s\n", file_name);

    ret = stat(file_name, &file_stat);
    if (ret == -1) {
	return 0;
    }

    file_size = file_stat.st_size;
    if (file_size == 0)
	return 0;

    property_info_str = (char *) calloc(1, file_size + 1);
    fd = fopen(file_name, "r");
    if (!fd) {
	free ((char *) property_info_str);
	return 0;
    }

    fread((char *) property_info_str, file_size, 1, fd);
    property_info_str[file_size] = 0;

    fclose(fd);

    *property_info_ptr = property_info_str;
    return (file_size + 1);
}

property_data_t *property_data_read_from_profile()
{
    property_data_t *property_data = NULL;
    char *property_info_str = NULL;
    int  property_info_len;

    property_info_len = property_data_read_from_profile_to_memory(&property_info_str);

    property_data = (property_data_t *)property_data_get_from_xmlstr(property_info_str,
								     property_info_len);

    if (property_info_len > 0 && property_info_str != NULL)
	free ((char *) property_info_str);

    return property_data;
}
