#include <stdio.h>
#include <X11/Xmd.h>
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>

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

#include "xaux_common.h"
#include "xaux_ext_common.h"
#include "palette_aux.h"
#include "vkb_aux.h"
#include "le_aux_protocol.h"
#include "ime.h"

/* Define Aux class */
xaux_class_t *xc_ = 0;
int im_ = 0;
int ic_ = 0;

#define ATOM_NUM_PALETTEAUX     32

/* Define Aux class */
xaux_class_t xaux_class = {
    PALETTE_AUX_CLASS_NAME, 1,
    NULL,
    (Window) 0, (Window) 0, (Window) 0,
    (Atom) 0, (Atom) 0, (Atom) 0, (Atom) 0,
    {(Atom) 0}, ATOM_NUM_PALETTEAUX, 0,
    {(Atom) 0}, ATOM_NUM_PALETTEAUX, 0,
    NULL,
};

extern palette_window_t *palette_window;

int gLocaleID = 0;
Display *gDisplay = NULL;
Window gWindow = 0;

void palette_aux_Proc_LE_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_IMEInfo_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Show_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Hide_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Change_Focus_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Switch_IME_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Switch_QjBj_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Switch_Punct_Notify(aux_ext_data_t * aux_ext_data);
void palette_aux_Proc_LE_Beep_Notify(aux_ext_data_t * aux_ext_data);

void palette_aux_Connect_To_LE_Request(int locale_id);
void palette_aux_Switch_LE_Engine_Request(char *name);
void palette_aux_Switch_LE_QjBj_Request(int qjbj);
void palette_aux_Switch_LE_Punct_Request(int punct);

/*   
	Event Handler 
*/
GdkFilterReturn xaux_ext_event_handler(GdkXEvent * gdk_xevent,
				       GdkEvent * event,
				       gpointer user_data)
{
    XEvent *xevent = (XEvent *) gdk_xevent;

    switch (xevent->type) {
    case ClientMessage:
	xaux_ext_process_client_message((Display *) gDisplay,
					(XClientMessageEvent *) xevent);
	break;
    case SelectionClear:
	break;
    }
    return GDK_FILTER_CONTINUE;
}

/*
	xaux_ext_init_classes(): Initialize the Atom of Auxiliary Window.
	It is in ../xaux_common/xaux_ext_common.c
*/
int xaux_ext_register_classes(GdkWindow * window)
{
    gDisplay = (Display *) GDK_WINDOW_XDISPLAY(window);
    gWindow = (Window) GDK_WINDOW_XWINDOW(window);

    /* Register aux class */
    if (xaux_ext_init_classes(gDisplay, (xaux_class_t *) & xaux_class, gWindow) == False)
	return False;

    return True;
}

/* 
Note:
     xaux_ext_Start(), xaux_ext_Draw(), xaux_ext_Done() 
     These three functions are all called by xaux_ext_process_client_message()

     xaux_ext_SetValue() will send auxiliary window information to language engine;
*/

/* Method - Start */
Bool xaux_ext_Start(xaux_class_t * xc, aux_ext_data_t * aux_ext_data,
		    Atom atom)
{
    DEBUG_printf("ext_Start: im:%d, ic:%d\n", aux_ext_data->im,
		 aux_ext_data->ic);
    im_ = aux_ext_data->im;
    ic_ = aux_ext_data->ic;
    xc_ = xc;

    /* inform Language Engine that PaletteAux has started. */
    /* Get Options Setting and send settings to Language engine. */
    palette_aux_Connect_To_LE_Request(gLocaleID);

    return True;
}

/* Method - Draw */
Bool xaux_ext_Draw(xaux_class_t * xc, aux_ext_data_t * aux_ext_data,
		   Atom atom)
{
    DEBUG_printf("ext_Draw == im:0x%x, ic:0x%x\n", aux_ext_data->im,
		 aux_ext_data->ic);
    im_ = aux_ext_data->im;
    ic_ = aux_ext_data->ic;
    xc_ = xc;

    palette_aux_Proc_LE_Notify(aux_ext_data);
    return True;
}

/* Method - Done */
Bool xaux_ext_Done(xaux_class_t * xc, aux_ext_data_t * aux_ext_data,
		   Atom atom)
{
    DEBUG_printf("ext_Done im:0x%x, ic_id:0x%x\n", aux_ext_data->im,
		 aux_ext_data->ic);
    return True;
}

/* ================================================================= */
/*       Process Notify information From Language Engine             */
/* ================================================================= */
void palette_aux_Proc_LE_Notify(aux_ext_data_t * aux_ext_data)
{
    int nIntegerCount, notifyType;

    nIntegerCount = aux_ext_data->integer_count;
    if (nIntegerCount <= 0)
	return;

    notifyType = aux_ext_data->integer_list[0];

    switch (notifyType) {
    case COMMONAUX_IME_INFO_NOTIFY:
	DEBUG_printf("COMMONAUX_IME_INFO_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_IMEInfo_Notify(aux_ext_data);
	break;
    case COMMONAUX_SHOW_NOTIFY:
	DEBUG_printf("COMMONAUX_SHOW_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Show_Notify(aux_ext_data);
	break;
    case COMMONAUX_HIDE_NOTIFY:
	DEBUG_printf("COMMONAUX_HIDE_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Hide_Notify(aux_ext_data);
	break;
    case COMMONAUX_CHANGE_FOCUS_NOTIFY:
	DEBUG_printf("COMMONAUX_CHANGE_FOCUS_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Change_Focus_Notify(aux_ext_data);
	break;
    case PALETTEAUX_SWITCH_IME_NOTIFY:
	DEBUG_printf("PALETTEAUX_SWITCH_IME_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Switch_IME_Notify(aux_ext_data);
	break;
    case PALETTEAUX_SWITCH_QJBJ_NOTIFY:
	DEBUG_printf("PALETTEAUX_SWITCH_QJBJ_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Switch_QjBj_Notify(aux_ext_data);
	break;
    case PALETTEAUX_SWITCH_PUNCT_NOTIFY:
	DEBUG_printf("PALETTEAUX_SWITCH_PUNCT_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Switch_Punct_Notify(aux_ext_data);
	break;
    case PALETTEAUX_BEEP_NOTIFY:
	DEBUG_printf("PALETTEAUX_BEEP_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Beep_Notify(aux_ext_data);
	break;
#if 0
    case PALETTEAUX_SWITCH_CONVERSION_NOTIFY:
	DEBUG_printf
	    ("PALETTEAUX_SWITCH_CONVERSION_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Switch_Conversion_Notify(aux_ext_data);
	break;
    case COMMONAUX_UPDATE_KEYMAPINFO_NOTIFY:
	DEBUG_printf
	    ("PALETTEAUX_UPDATE_KEYMAPINFO_NOTIFY Notify Received\n");
	palette_aux_Proc_LE_Update_KeymapInfo_Notify(aux_ext_data);
	break;
#endif
    }
}

void palette_aux_Set_Selection_Owner()
{
    Atom aux_atom =
	XInternAtom(gDisplay, "CH_PALETTEAUX_MODULE_ATOM", False);

    XSetSelectionOwner(gDisplay, aux_atom, gWindow, CurrentTime);
}

void palette_aux_Proc_LE_IMEInfo_Notify(aux_ext_data_t * aux_ext_data)
{
    int nIntegerCount;
    int nStringsCount;
    char *property_info_str;
    int  property_info_len;
    property_data_t *property_data;

    char *ime_uuid;
    ime_module_t *current_ime;

    nIntegerCount = aux_ext_data->integer_count;
    if (nIntegerCount <= 0)
	return;

    nStringsCount = aux_ext_data->string_count;
    if (nStringsCount <= 0)
	return;

    property_info_str = (char *)aux_ext_data->string_list[0].ptr;
    property_info_len = strlen ((char *) property_info_str);

    if (property_info_len == 0 || *property_info_str == 0)
	return;

    property_data = (property_data_t *) property_data_get_from_xmlstr(property_info_str,
								      property_info_len);
    if (property_data == NULL)
	return;

    current_ime = palette_window->current_ime;
    ime_uuid = "NoIme";
    if (current_ime != NULL)
	ime_uuid = current_ime->uuid;

    palette_window->current_ime = (ime_module_t *)property_data_get_ime_module_by_uuid(
							property_data, ime_uuid);

    property_data_merge(property_data, palette_window->property_data);
    property_data_destroy(palette_window->property_data);
    palette_window->property_data = property_data;

    if (palette_window->ime_list_menu)
        gtk_widget_destroy(palette_window->ime_list_menu);

    palette_window->ime_list_menu = (GtkWidget *)palette_window_create_ime_list_menu(palette_window, palette_window->property_data);

    palette_window_update_layout_with_property_data(palette_window, property_data);
/*
    property_data_save_to_profile(palette_window->property_data);
*/

    return;
}

void palette_aux_Proc_LE_Show_Notify(aux_ext_data_t * aux_ext_data)
{
    /* Change auxiliary window Full_Width/half_Width information */
    DEBUG_printf("%%%%%%%%%%%%%%%%%%%%% enter into palette_aux_Proc_LE_Show_Notify().");
    gtk_widget_show_all(palette_window->window);
}

void palette_aux_Proc_LE_Hide_Notify(aux_ext_data_t * aux_ext_data)
{
    /* Change auxiliary window Full_Width/half_Width information */
    if (palette_window->menu_popuped == 0) {
        palette_window_hide(palette_window);
    }
}

void palette_aux_Proc_LE_Change_Focus_Notify(aux_ext_data_t * aux_ext_data)
{
    int show_status;
    gboolean qjbj_status, punct_status;
    char *uuid;
    ime_module_t *ime_module;

    if (aux_ext_data->integer_count != 4)
	return;

    show_status = aux_ext_data->integer_list[1];
    qjbj_status = aux_ext_data->integer_list[2];
    punct_status = aux_ext_data->integer_list[3];

    DEBUG_printf("palette_aux_Proc_LE_Change_Focus_Notify: \n");
    DEBUG_printf("   show_status: %d\n", show_status);
    DEBUG_printf("   qjbj_status: 0x%x, punct_status: 0x%x\n",
		 qjbj_status, punct_status);

    if (!show_status) {
	palette_window_hide(palette_window);
	return;
    }

    if (aux_ext_data->string_count != 1)
	return;

    uuid = (char *)aux_ext_data->string_list[0].ptr;

    ime_module = (ime_module_t *)property_data_get_ime_module_by_uuid(palette_window->property_data, uuid);
    if (ime_module == NULL)
	return;

    gtk_widget_show_all(palette_window->window);
    palette_window_change_current_ime(ime_module, False);

    palette_window_set_qjbj_status(palette_window, qjbj_status);
    palette_window_set_punct_status(palette_window, punct_status);
}

void palette_aux_Proc_LE_Switch_IME_Notify(aux_ext_data_t * aux_ext_data)
{
    int show_status;
    char *uuid;
    ime_module_t *ime_module;

    if (aux_ext_data->integer_count != 2)
	return;

    show_status = aux_ext_data->integer_list[1];

    DEBUG_printf("palette_aux_Proc_LE_Switch_IME_Notify: \n");

    if (!show_status) {
        palette_window_hide(palette_window);
	return;
    }

    if (aux_ext_data->string_count != 1)
	return;

    uuid = (char *)aux_ext_data->string_list[0].ptr;
    ime_module = (ime_module_t *)property_data_get_ime_module_by_uuid(palette_window->property_data, uuid);
    if (ime_module == NULL)
	return;

    gtk_widget_show_all(palette_window->window);
    palette_window_change_current_ime(ime_module, False);
}

void palette_aux_Proc_LE_Switch_QjBj_Notify(aux_ext_data_t * aux_ext_data)
{
    int qjbj;

    if (aux_ext_data->integer_count != 2)
	return;

    /* Change auxiliary window Full_Width/half_Width information */
    qjbj = aux_ext_data->integer_list[1];
    palette_window_set_qjbj_status(palette_window, qjbj);
}

void palette_aux_Proc_LE_Switch_Punct_Notify(aux_ext_data_t * aux_ext_data)
{
    int punct;

    if (aux_ext_data->integer_count != 2)
	return;

    /* Change auxiliary window English/Chinese Punctuation information */
    punct = aux_ext_data->integer_list[1];
    palette_window_set_punct_status(palette_window, punct);
}

void palette_aux_Proc_LE_Beep_Notify(aux_ext_data_t * aux_ext_data)
{
    int beep_type;
    property_data_t *property_data;

    property_data = palette_window->property_data;

    beep_type = ImeBeepWarning;
    if (aux_ext_data->integer_count == 2)
	beep_type = aux_ext_data->integer_list[1];

    if (gDisplay == NULL)
	return;

    if (property_data != NULL && property_data->beep_enabled == 0)
	return;

    if (beep_type == ImeBeepWarning) {
        XBell(gDisplay, 0);
    } else {
	/* if is ImeBeepError, bell with some long time. */

        static XKeyboardState kbdstate;
        static XKeyboardControl kbdcontrol;
        gint save_duration;

        XGetKeyboardControl(gDisplay, &kbdstate);
        save_duration = kbdstate.bell_duration;

        kbdcontrol.bell_duration = save_duration * 2;
        XChangeKeyboardControl(gDisplay, KBBellDuration, &kbdcontrol);

        XBell(gDisplay, 0);

        kbdcontrol.bell_duration = save_duration;
        XChangeKeyboardControl(gDisplay, KBBellDuration, &kbdcontrol);
    }
}

/* ================================================================= */
/*             Send information to Language Engine                   */
/* ================================================================= */
void palette_aux_Connect_To_LE_Request(int locale_id)
{
    int nIntegerCount, pIntegerList[3];
    int nStringCount, pStringLen[1];
    char *pStringList[1];
    char *property_info_str = NULL;
    int  property_info_len = 0;

    nIntegerCount = 3;
    pIntegerList[0] = PALETTEAUX_CONNECT;
    pIntegerList[1] = locale_id;

    property_info_len = 0;
    if (palette_window->property_data == NULL) {
        palette_window->property_data = (property_data_t *)property_data_read_from_profile();
    }
    if (palette_window->property_data != NULL) {
        property_info_str = (char *)property_data_composite_to_xmlstr(palette_window->property_data,
                                        PROPERTY_DATA_FOR_SERVER_NOTIFY_WITH_ALL_INFO);
        if (property_info_str != NULL) {
            property_info_len = strlen(property_info_str);
        }
    }

    pIntegerList[2] = property_info_len;

    if (property_info_len == 0) {
        NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount,
					      pIntegerList);
	return;
    }


#if DEBUG
    printf("palette_aux_Connect_To_LE_Request: property_info_str: \n%s\nlen: %d\n", property_info_str, property_info_len);
#endif
    nStringCount = 1;
    pStringLen[0] = property_info_len + 1;
    pStringList[0] = property_info_str;
    
    NotifyStringListDataToLanguageEngine(gDisplay, nIntegerCount,
					 pIntegerList, nStringCount,
					 pStringLen, pStringList);

    free((char *)property_info_str);
}

void palette_aux_Update_LE_Properties_Request()
{
    int nIntegerCount, pIntegerList[2];
    int nStringCount, pStringLen[1];
    char *pStringList[1];
    char *property_info_str = NULL;
    int  property_info_len = 0;

    nIntegerCount = 2;
    pIntegerList[0] = PALETTEAUX_UPDATE_PROPERTY;

    property_info_len = 0;
    if (palette_window->property_data != NULL) {
        property_info_str = (char *)property_data_composite_to_xmlstr(palette_window->property_data,
                                        PROPERTY_DATA_FOR_SERVER_NOTIFY_WITH_ALL_INFO);
        if (property_info_str != NULL) {
            property_info_len = strlen(property_info_str);
        }
    }

    if (property_info_len == 0)
	return;

    pIntegerList[1] = property_info_len;

#if DEBUG
    printf("palette_aux_Connect_To_LE_Request: property_info_str: \n%s\nlen: %d\n", property_info_str, property_info_len);
#endif
    nStringCount = 1;
    pStringLen[0] = property_info_len + 1;
    pStringList[0] = property_info_str;
    
    NotifyStringListDataToLanguageEngine(gDisplay, nIntegerCount,
					 pIntegerList, nStringCount,
					 pStringLen, pStringList);

    free((char *)property_info_str);
}

void palette_aux_Switch_LE_Engine_Request(char *name)
{
    int nIntegerCount, pIntegerList[1];
    int nStringCount, pStringLen[1];
    char *pStringList[1];

    nIntegerCount = 1;
    pIntegerList[0] = PALETTEAUX_SWITCH_IME;

    nStringCount = 1;
    pStringLen[0] = strlen(name);
    pStringList[0] = name;
    
    NotifyStringListDataToLanguageEngine(gDisplay, nIntegerCount,
					 pIntegerList, nStringCount,
					 pStringLen, pStringList);
}

void palette_aux_Switch_LE_QjBj_Request(int qjbj)
{
    int nIntegerCount, pIntegerList[2];

    nIntegerCount = 2;
    pIntegerList[0] = PALETTEAUX_SWITCH_QJBJ;
    pIntegerList[1] = qjbj;
    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount,
					  pIntegerList);
}

void palette_aux_Switch_LE_Punct_Request(int punct)
{
    int nIntegerCount, pIntegerList[2];

    nIntegerCount = 2;
    pIntegerList[0] = PALETTEAUX_SWITCH_PUNCT;
    pIntegerList[1] = punct;
    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount,
					  pIntegerList);
}

#if 0
void palette_aux_Change_LE_Position_Request(int x, int y)
{
    int nIntegerCount, pIntegerList[3];

    nIntegerCount = 3;
    pIntegerList[0] = PALETTEAUX_CHANGE_POSITION;
    pIntegerList[1] = x;
    pIntegerList[2] = y;
    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount,
					  pIntegerList);
}
#endif

void palette_aux_Commit_String_Request(char *commit_str)
{
    int nIntegerCount, pIntegerList[3];
    int nStringCount, pStringLen[1];
    char *pStringList[1];

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

    DEBUG_printf("palette_aux_Commit_String_Request: encode_id:%d, commit_str:%s, len: %d\n",
                 ENCODE_UTF8,  commit_str, strlen(commit_str));

    nIntegerCount = 3;
    pIntegerList[0] = COMMONAUX_COMMIT_STRING;
    pIntegerList[1] = ENCODE_UTF8;
    pIntegerList[2] = strlen(commit_str);

    nStringCount = 1;
    pStringLen[0] = strlen(commit_str) + 1;
    pStringList[0] = commit_str;

    NotifyStringListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList,
					 nStringCount, pStringLen,
					 pStringList);
}

void palette_aux_Commit_Key_Request(int nKeyCode, int nKeyChar, int nKeyStatus)
{
    int nIntegerCount, pIntegerList[4];

    nIntegerCount = 4;
    pIntegerList[0] = COMMONAUX_COMMIT_KEY;
    pIntegerList[1] = nKeyCode;
    pIntegerList[2] = nKeyChar;
    pIntegerList[3] = nKeyStatus;
    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}

NotifyIntegerListDataToLanguageEngine(Display * display,
				      int nIntegerCount, int *pIntegerList)
{
    aux_ext_data_t aux_ext_data;

    aux_ext_data.im = im_;
    aux_ext_data.ic = ic_;
    aux_ext_data.integer_count = nIntegerCount;
    aux_ext_data.integer_list = pIntegerList;
    aux_ext_data.string_count = 0;
    aux_ext_data.string_list = NULL;
    aux_ext_data.string_ptr = NULL;
    aux_ext_data.point.x = 0;
    aux_ext_data.point.y = 0;

    xaux_ext_SetValue(display, xc_, &aux_ext_data);
    XFlush(display);
}

NotifyStringListDataToLanguageEngine(Display * display,
				     int nIntegerCount, int *pIntegerList,
				     int nStringCount, int *pStringLen,
				     char **pStringList)
{
    int i;

    aux_ext_data_t aux_ext_data;
    aux_ext_string_t *p;

    aux_ext_data.im = im_;
    aux_ext_data.ic = ic_;
    aux_ext_data.integer_count = nIntegerCount;
    aux_ext_data.integer_list = pIntegerList;
    aux_ext_data.point.x = 0;
    aux_ext_data.point.y = 0;

    aux_ext_data.string_count = nStringCount;
    aux_ext_data.string_ptr = NULL;

    if (nStringCount > 0) {
	aux_ext_data.string_list =
	    (aux_ext_string_t *) calloc(nStringCount,
					sizeof(aux_ext_string_t));
	for (i = 0; i < nStringCount; i++) {
	    aux_ext_string_t *p = &aux_ext_data.string_list[i];
	    p->length = pStringLen[i];
	    p->ptr = (unsigned char *) (pStringList[i]);
	}
    }

    xaux_ext_SetValue(display, xc_, &aux_ext_data);
    XFlush(display);

    free(aux_ext_data.string_list);
}
