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

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

#include "common_aux_methods.h"
#include "gtkiiimwin.h"

extern Display *gDisplay;
extern void* composite_aux;

void composite_aux_Proc_LE_Show_Preedit_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Hide_Preedit_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Update_Preedit_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Show_Candidates_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Hide_Candidates_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Update_Candidates_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Change_Focus_Notify(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Change_Option(aux_ext_data_t * aux_ext_data);
void composite_aux_Proc_LE_Restore_Position(aux_ext_data_t * aux_ext_data);


/* ================================================================= */
/*       Process Notify information From Language Engine             */
/* ================================================================= */
void composite_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_SHOW_PREEDIT_NOTIFY:
        DEBUG_printf("COMMONAUX_SHOW_PREEDIT_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Show_Preedit_Notify(aux_ext_data);
        break;
    case COMMONAUX_HIDE_PREEDIT_NOTIFY:
        DEBUG_printf("COMMONAUX_HIDE_PREEDIT_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Hide_Preedit_Notify(aux_ext_data);
        break;
    case COMMONAUX_UPDATE_PREEDIT_NOTIFY:
        DEBUG_printf("COMMONAUX_UPDATE_PREEDIT_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Update_Preedit_Notify(aux_ext_data);
        break;
    case COMMONAUX_SHOW_CANDIDATES_NOTIFY:
        DEBUG_printf("COMMONAUX_SHOW_CANDIDATES_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Show_Candidates_Notify(aux_ext_data);
        break;
    case COMMONAUX_HIDE_CANDIDATES_NOTIFY:
        DEBUG_printf("COMMONAUX_HIDE_CANDIDATES_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Hide_Candidates_Notify(aux_ext_data);
        break;
    case COMMONAUX_UPDATE_CANDIDATES_NOTIFY:
        DEBUG_printf("COMMONAUX_UPDATE_CANDIDATES_NOTIFY Notify Received\n");
        composite_aux_Proc_LE_Update_Candidates_Notify(aux_ext_data);
        break;
    case COMPOSITE_PC_OPTION:
        DEBUG_printf("COMPOSITE_PC_OPTION Notify Received\n");
        composite_aux_Proc_LE_Change_Option(aux_ext_data);
        break;
    case COMPOSITE_PC_MOVE:
        DEBUG_printf("COMPOSITE_PC_MOVE Notify Received\n");
        composite_aux_Proc_LE_Restore_Position(aux_ext_data);
        break;
    }
}

void composite_aux_Proc_LE_Change_Option(aux_ext_data_t * aux_ext_data)
{
    int cursor_x, cursor_y, cursor_w, cursor_h;

    DEBUG_printf("--------Composite aux style change--------\n");
    int nIntegerCount = aux_ext_data->integer_count;
    if (nIntegerCount != 1 + sizeof(TPCStyleOption)/sizeof(int) + 4) {
        DEBUG_printf("    XXXXXX: wrong composite update candidates protocol data\n");
        return;
    }

    recreateWindows(composite_aux, (TPCStyleOption*)(&aux_ext_data->integer_list[1]));
    if (isPCFollowCursor(composite_aux)) {
        composite_aux_get_cursor_rect(aux_ext_data, &cursor_x, &cursor_y, &cursor_w, &cursor_h);
        movePreedit(composite_aux, cursor_x, cursor_y, cursor_w, cursor_h);
    } else {
        movePreedit(composite_aux,
            aux_ext_data->integer_list[sizeof(TPCStyleOption)/sizeof(int) + 1],
            aux_ext_data->integer_list[sizeof(TPCStyleOption)/sizeof(int) + 2],
            aux_ext_data->integer_list[sizeof(TPCStyleOption)/sizeof(int) + 3],
            aux_ext_data->integer_list[sizeof(TPCStyleOption)/sizeof(int) + 4]);
    }
}


void composite_aux_Proc_LE_Show_Preedit_Notify(aux_ext_data_t * aux_ext_data)
{
    int cursor_x, cursor_y, cursor_w, cursor_h;

    if (isPCFollowCursor(composite_aux)) {
        composite_aux_get_cursor_rect(aux_ext_data, &cursor_x, &cursor_y, &cursor_w, &cursor_h);
        movePreedit(composite_aux, cursor_x, cursor_y, cursor_w, cursor_h);
    }
    showPreedit(composite_aux);
}

void composite_aux_Proc_LE_Hide_Preedit_Notify(aux_ext_data_t * aux_ext_data)
{
    hidePreedit(composite_aux);
}

void composite_aux_Proc_LE_Restore_Position(aux_ext_data_t * aux_ext_data)
{
    if (aux_ext_data->integer_count != 5) {
        DEBUG_printf("    XXXXXX: wrong restore preedit position protocol data\n");
        return;
    }

    if (!isPCFollowCursor(composite_aux)) {
        movePreedit(composite_aux,
                    aux_ext_data->integer_list[1],
                    aux_ext_data->integer_list[2],
                    aux_ext_data->integer_list[3],
                    aux_ext_data->integer_list[4]);
    }
}

void composite_aux_Proc_LE_Update_Preedit_Notify(aux_ext_data_t * aux_ext_data)
{
    int   nIntegerCount;
    int   nStringsCount;
    char *preedit_str;
    int   preedit_len;
    int   caret, cl_start;

    int cursor_x, cursor_y, cursor_w, cursor_h;

    nIntegerCount = aux_ext_data->integer_count;
    nStringsCount = aux_ext_data->string_count;

    if (((nIntegerCount -3)/4 * 4 != nIntegerCount-3) || (nStringsCount != 1)) {
        DEBUG_printf("XXXXXX-------XXXXXXX: wrong composite update preedit protocol data\n");
        return;
    }

    preedit_str = (char *)aux_ext_data->string_list[0].ptr;
    preedit_len = strlen(preedit_str);
    caret       = aux_ext_data->integer_list[1];
    cl_start    = aux_ext_data->integer_list[2];

    DEBUG_printf("composite_aux_Proc_LE_Update_Preedit_Notify: \n");
    DEBUG_printf("   preedit_str: %s\n", preedit_str);
    DEBUG_printf("   preedit_len: %d\n", preedit_len);
    DEBUG_printf("   caret: %d\n", caret);

    int i, nfbs = (nIntegerCount-3)/4;
    /*
    ImeFeedbackRec fbs[nfbs];

    for (i=0; i < nfbs; ++i) {
        fbs[i].type     = aux_ext_data->integer_list[4*i+3+0];
        fbs[i].value    = aux_ext_data->integer_list[4*i+3+1];
        fbs[i].start    = aux_ext_data->integer_list[4*i+3+2];
        fbs[i].length   = aux_ext_data->integer_list[4*i+3+3];
    }
    */

   if (isPCFollowCursor(composite_aux)) {
        composite_aux_get_cursor_rect(aux_ext_data, &cursor_x, &cursor_y, &cursor_w, &cursor_h);
        movePreedit(composite_aux, cursor_x, cursor_y, cursor_w, cursor_h);
    }

    updatePreedit(composite_aux, preedit_str, caret, cl_start, nfbs, /*fbs*/(ImeFeedbackRec*)(aux_ext_data->integer_list+3));
    return;
}

void composite_aux_Proc_LE_Show_Candidates_Notify(aux_ext_data_t * aux_ext_data)
{
    showCandidates(composite_aux);
}

void composite_aux_Proc_LE_Hide_Candidates_Notify(aux_ext_data_t * aux_ext_data)
{
    hideCandidates(composite_aux);
}

/******************************************************************************
* IntegerList:
*     protocal_id
*     candidates_count    --->  n
*     page_states
*     focus_in_window
*     n * feedback_count for each candidates
*     feedbacks for candidates[1]
*     ......
*****************************************************************************/
void composite_aux_Proc_LE_Update_Candidates_Notify(aux_ext_data_t * aux_ext_data)
{
    int  nIntegerCount;
    int  nStringsCount;
    int  i, num_candidates, page_state, candi_current, candi_total, candi_first = 0;
    const char*candidates[126];


    nIntegerCount = aux_ext_data->integer_count;
    if (nIntegerCount < 4) {
        DEBUG_printf("    XXXXXX: wrong composite update candidates protocol data\n");
        return;
    }

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

    num_candidates = aux_ext_data->integer_list[1];
    if (num_candidates > 126)
        num_candidates = 126;

    page_state = aux_ext_data->integer_list[2];
    candi_total = candi_current = aux_ext_data->integer_list[3];
    if ((page_state & 0x1) == 0)  {// not first page
        candi_first += 10;
        candi_total += 10;
    }
    if ((page_state & 0x2) == 0) // not last page
        candi_total += 10;

    int * p_fb_counts = (aux_ext_data->integer_list + 4);
    ImeFeedbackRec * pfbs[num_candidates], *p;

    p = (ImeFeedbackRec*)(aux_ext_data->integer_list + 4 + num_candidates);
    for (i= 0; i < num_candidates; i++) {
        candidates[i] = (char *)aux_ext_data->string_list[i].ptr;
        DEBUG_printf("candidates[%d]: %s\n", i, candidates[i]);
        pfbs[i] = p;
        p += p_fb_counts[i];
    }

    DEBUG_printf("composite_aux_Proc_LE_Update_Candidates_Notify: \n");
    DEBUG_printf("   num_candidates: %d\n", num_candidates);

    updateCandidates(composite_aux, num_candidates, candidates, NULL, candi_first, candi_current, candi_total, p_fb_counts, pfbs);

    return;
}

/******************************************************************************
* Sending message to LE
******************************************************************************/
void composite_aux_send_option_change(TPCStyleOption *poptions)
{
    int nIntegerCount =  1+sizeof(TPCStyleOption)/sizeof(int);
    int pIntegerList[nIntegerCount];

    pIntegerList[0] = COMPOSITE_PC_OPTION;
    *(TPCStyleOption*)(pIntegerList+1) = *poptions;

    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}

void composite_aux_send_candidate_selection(int idx)
{
    int nIntegerCount = 2;
    int pIntegerList[nIntegerCount];

    pIntegerList[0] = COMPOSITE_PC_CANDIDATE_SELECTION;
    pIntegerList[1] = idx;

    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}

void composite_aux_send_candidate_page(int pgop)
{
    int nIntegerCount = 2;
    int pIntegerList[nIntegerCount];

    pIntegerList[0] = COMPOSITE_PC_CANDIDATE_PAGE;
    pIntegerList[1] = pgop;

    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}


void composite_aux_send_move(int cursor_x, int cursor_y, int cursor_w, int cursor_h)
{
    int nIntegerCount = 5;
    int pIntegerList[nIntegerCount];

    pIntegerList[0] = COMPOSITE_PC_MOVE;
    pIntegerList[1] = cursor_x;
    pIntegerList[2] = cursor_y;
    pIntegerList[3] = cursor_w;
    pIntegerList[4] = cursor_h;

    NotifyIntegerListDataToLanguageEngine(gDisplay, nIntegerCount, pIntegerList);
}

