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

#include "xaux_locale.h"
#include "xaux_common.h"
#include "xaux_ext_common.h"
#include "hzinput.h"
#include "encode.h"

#include "imlabel.h"

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

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

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

extern GtkWidget     *pcaux_main_window;
extern ImLabel       *pcaux_preedit_area;
extern GtkLabel      *pcaux_candidate_area;

extern void setIMLabelCaret(int caret);
extern void setIMLabelText(GtkLabel* preedit_area, const char*str, void* decoration);
extern void main_window_set_position_follow_cursor(gint pos_x, gint pos_y);
extern void PCAuxShow(void);
extern void PCAuxHide(void);

void PCAux_Proc_LE_Notify();
void PCAux_Connect_To_LE_Request();

static void GetWindowPosition(Window clientwindow, int *win_x, int *win_y)
{
    int x=0, y=0;
    Window w_none;
    XWindowAttributes attr;

    if (clientwindow && gDisplay) {
        XGetWindowAttributes(gDisplay, clientwindow, &attr);
        if (attr.root)
            XTranslateCoordinates(gDisplay, clientwindow, attr.root, -(attr.border_width), -(attr.border_width), &x, &y, &w_none );
    }
    *win_x = x;
    *win_y = y;
}

void GetDefaultPosition(int *win_x, int *win_y)
{
    GtkRequisition ws;

    gtk_widget_size_request (pcaux_main_window, &ws);

    *win_x = (gdk_screen_width () > 640)?200:100;
    *win_y = (gdk_screen_height () - ws.height - 10 - 36);
}


static void SetPCAuxPosition(aux_ext_data_t *aux_ext_data)
{
    int win_x = 0, win_y = 0;

    GetWindowPosition(aux_ext_data->clientwin, &win_x, &win_y);
    if ((win_x == 0 && win_y == 0) || (aux_ext_data->point.x == 0 && aux_ext_data->point.y == 0)) {
        GetDefaultPosition(&win_x, &win_y);
    } else {
        win_x += aux_ext_data->point.x;
        win_y += aux_ext_data->point.y + 22;
    }

    main_window_set_position_follow_cursor(win_x, win_y);
}

//int_list: protocal, caret, strlen, deco_len, deco_array
static void PCAuxShowPreedit(aux_ext_data_t *aux_ext_data)
{
    int caret = 0, len = 0, deco_len = 0;
    IMCharDecoration *deco_ptr = NULL;
    char *pretext = NULL;

    if (aux_ext_data != NULL && aux_ext_data->integer_count >= 4 &&
            (4+aux_ext_data->integer_list[3]) == aux_ext_data->integer_count) {
        DEBUG_printf("===PCAux Preedit Draw notify===>(%d integer, %d string)\n", aux_ext_data->integer_count, aux_ext_data->string_count);

        caret = aux_ext_data->integer_list[1];
        len = aux_ext_data->integer_list[2];
        deco_len = aux_ext_data->integer_list[3];
        deco_ptr = (IMCharDecoration*)(aux_ext_data->integer_list + 4);

        pretext = (char *)((aux_ext_data->string_count > 0)?(aux_ext_data->string_list[0].ptr):NULL);
        if (pretext == NULL) {
            setPreeditText(NULL, 0, NULL);
            setIMLabelCaret(0);
        } else {
            if (len <= 0) len = 1;
            pretext[len-1] = 0;
            setPreeditText(pretext, deco_len, deco_ptr);
            setIMLabelCaret(caret);
        }

        SetPCAuxPosition(aux_ext_data);
    }
}

// nStringCount == nCand
// int_list: Protocal, markType, nCand of strlen, nCand of deco_len, deco_array
static void PCAuxShowLookup(aux_ext_data_t *aux_ext_data)
{
    int i, len, nCandi, markType;
    int *deco_lens, *p;
    char *strarr[MAX_CANDIDATES_NUM];
    IMCharDecoration *deco_ptrs[MAX_CANDIDATES_NUM];

    if (aux_ext_data != NULL && aux_ext_data->integer_count >= 2) {
        markType = aux_ext_data->integer_list[1];
        DEBUG_printf("===PCAux Lookup Draw notify===>(%d integer, %d string)\n", aux_ext_data->integer_count, aux_ext_data->string_count);
        nCandi = aux_ext_data->string_count;
        if (nCandi < 0) nCandi = 0;
        if (aux_ext_data->integer_count >= 2 + 2*nCandi) {
            for (i=0; i < nCandi; ++i) {
                strarr[i] = (char*)(aux_ext_data->string_list[i].ptr);
                len = aux_ext_data->integer_list[i+2];
                if (strarr[i] != NULL) {
                    if (len <= 0) len = 1;
                    strarr[i][len-1] = 0;
                }
            }
            deco_lens = aux_ext_data->integer_list + (2 + nCandi);
            p = deco_lens + nCandi;
            len = 2+2*nCandi;
            for (i=0; i < nCandi && (len + deco_lens[i]) <= aux_ext_data->integer_count; ++i) {
                deco_ptrs[i] = (IMCharDecoration*)p;
                p += deco_lens[i];
            }
            setCandidateStrings(nCandi, markType, strarr, deco_lens, deco_ptrs);
        }
    }
}

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

        DEBUG_printf("im_:%d, ic_:%d\n", im_, ic_);

        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;

        DEBUG_printf("begin xaux_exe_setValue =============\n");
        xaux_ext_SetValue(display, xc_, &aux_ext_data);
        XFlush(display);
}

GdkFilterReturn xaux_ext_event_handler(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer user_data)
{
    if (gdk_xevent && event) {
        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;
}

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);
}

Bool xaux_ext_Start(xaux_class_t *xc, aux_ext_data_t *aux_ext_data)
{
    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 PCAux has started. */
    PCAux_Connect_To_LE_Request(gLocaleID);

    return True;
}

Bool xaux_ext_Draw(xaux_class_t *xc, aux_ext_data_t *aux_ext_data)
{
    if (xc != NULL && aux_ext_data != NULL) {
        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;

        PCAux_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)
{
    if (xc != NULL && aux_ext_data != NULL) {
        DEBUG_printf("ext_Done im:0x%x, ic_id:0x%x\n", aux_ext_data->im, aux_ext_data->ic);
    }
    return True;
}

void PCAux_Connect_To_LE_Request(int locale_id)
{
    int nIntegerCount, pIntegerList[2];

    nIntegerCount = 2;
    pIntegerList[0] = COMPOSITEAUX_CONNECT;
    pIntegerList[1] = locale_id;
    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}

/* ================================================================= */
/*       Process Notify information From Language Engine             */
/* ================================================================= */
void PCAux_Proc_LE_Notify(aux_ext_data_t *aux_ext_data)
{
    DEBUG_printf("PCAux_Proc_LE_Notify on data @%X\n", aux_ext_data);

    if (aux_ext_data != NULL && aux_ext_data->integer_count > 0) {
        switch (aux_ext_data->integer_list[0])
        {
        case COMPOSITEAUX_DRAW_PREEDIT_NOTIFY:
            PCAuxShowPreedit(aux_ext_data);
            break;
        case COMPOSITEAUX_DRAW_LOOKUP_NOTIFY:
            PCAuxShowLookup(aux_ext_data);
            break;
        case COMPOSITEAUX_HIDE_NOTIFY:
            PCAuxHide();
            break;
        case COMPOSITEAUX_SHOW_NOTIFY:
            PCAuxShow();
            break;
        }
    }
}

void PCAux_Change_LE_Position_Request(int x, int y)
{
      /*
      int nIntegerCount, pIntegerList[3];

      nIntegerCount = 3;
      pIntegerList[0] = COMPOSITEAUX_POSITION_CHANGED;
      pIntegerList[1] = x;
      pIntegerList[2] = y;
      NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
      */
}

