#include "SunIM.h"

#define MAXSTRINGLEN 1024

/* general functions */
IMFeedbackList *le_iml_create_feedback_list(iml_session_t * s, int size)
{
    int i;
    IMFeedbackList *feedback, *fbl;

    if (s) {
        feedback =
            (IMFeedbackList *) s->If->m->iml_new(s,
                                                 sizeof(IMFeedbackList) *
                                                 size);
        memset(feedback, 0, sizeof(IMFeedbackList) * size);
    } else {
        feedback =
            (IMFeedbackList *) calloc(1, sizeof(IMFeedbackList) * size);
    }
    for (i = 0; i < size; i++) {
        fbl = &feedback[i];
        fbl->count_feedbacks = 1;
        if (s) {
            fbl->feedbacks =
                (IMFeedback *) s->If->m->iml_new(s,
                                                 4 * sizeof(IMFeedback));
            memset(fbl->feedbacks, 0, 4 * sizeof(IMFeedback));
        } else {
            fbl->feedbacks = (IMFeedback *) calloc(4, sizeof(IMFeedback));
        }
    }
    return feedback;
}

void le_iml_free_feedback_list(iml_session_t * s,
                               IMFeedbackList * feedback, int size)
{
    int i;
    IMFeedbackList *fbl;

    if ((s != NULL) || (feedback == NULL))
        return;

    for (i = 0; i < size; i++) {
        fbl = &feedback[i];
        if (fbl->feedbacks != NULL)
            free(fbl->feedbacks);
    }
    free(feedback);
}

int le_iml_get_feedback(IMFeedbackList * fbl)
{
    IMFeedback *fb = &fbl->feedbacks[0];
    return IM_FEEDBACK_VALUE(fb);
}

void le_iml_set_feedback(IMFeedbackList * fbl, int value)
{
    IMFeedback *fb = &fbl->feedbacks[0];
    IM_FEEDBACK_TYPE(fb) = IM_DECORATION_FEEDBACK;
    IM_FEEDBACK_VALUE(fb) = value;
}

void le_iml_set_feedback_private(IMFeedbackList * fbl, int normalfeedback,
                                 int fg, int bg, int underline)
{
    int count = 0;
    IMFeedback *fb;

    fb = &fbl->feedbacks[count];
    IM_FEEDBACK_TYPE(fb) = IM_DECORATION_FEEDBACK;
    IM_FEEDBACK_VALUE(fb) = normalfeedback;
    count++;

    if (fg != -1) {
        fb = &fbl->feedbacks[count];
        IM_FEEDBACK_TYPE(fb) = IM_FOREGROUND_RGB_FEEDBACK;
        IM_FEEDBACK_VALUE(fb) = fg;
        count++;
    }
    if (bg != -1) {
        fb = &fbl->feedbacks[count];
        IM_FEEDBACK_TYPE(fb) = IM_BACKGROUND_RGB_FEEDBACK;
        IM_FEEDBACK_VALUE(fb) = bg;
        count++;
    }
    if (underline != -1) {
        fb = &fbl->feedbacks[count];
        IM_FEEDBACK_TYPE(fb) = IM_UNDERLINE_RGB_FEEDBACK;
        IM_FEEDBACK_VALUE(fb) = underline;
        count++;
    }
    IM_FEEDBACK_COUNT(fbl) = count;
}

/* UTF16 string handle functions */
UTFCHARCat(UTFCHAR * dest, UTFCHAR * str1, UTFCHAR * str2)
{
    int i;
    for (i = 0; *str1; i++) {
        *dest++ = *str1++;
    }
    for (i = 0; *str2; i++) {
        *dest++ = *str2++;
    }
    *dest = 0;
    return i;
}

int UTFCHARCpy(UTFCHAR * dest, UTFCHAR * original)
{
    int i;
    for (i = 0; *original; i++) {
        *dest++ = *original++;
    }
    *dest = 0;
    return i;
}

int UTFCHARLen(UTFCHAR * p)
{
    int i;
    for (i = 0; *p; i++)
        p++;
    return i;
}

IMText *le_iml_make_imtext(iml_session_t * s, UTFCHAR * p,
                           IMFeedbackList * feedbacks)
{
    int len;
    IMText *text = (IMText *) s->If->m->iml_new(s, sizeof(IMText));

    memset(text, 0, sizeof(IMText));

    len = UTFCHARLen(p);

    text->encoding = UTF16_CODESET;
    text->count_annotations = 0;
    text->annotations = NULL;

    text->text.utf_chars =
        (UTFCHAR *) s->If->m->iml_new(s, sizeof(UTFCHAR) * (len + 1));
    UTFCHARCpy(text->text.utf_chars, p);
    text->char_length = len;

    if (feedbacks != NULL) {
        text->feedback = feedbacks;
    } else {
        int i;

        text->feedback = le_iml_create_feedback_list(s, len);
        for (i = 0; i < len; i++) {
            le_iml_set_feedback_private(&text->feedback[i], IMReverse, -1,
                                        -1, -1);
        }
    }

    return text;
}

/* Send a Chinese string to system */
void le_iml_commit(iml_session_t * s, UTFCHAR * commit_buf)
{
    int len;
    iml_inst *lp;
    IMText *im_text;

    len = UTFCHARLen(commit_buf);
    if (len == 0)
        return;

    im_text = le_iml_make_imtext(s, commit_buf, NULL);
    lp = (iml_inst *) s->If->m->iml_make_commit_inst(s, im_text);
    s->If->m->iml_execute(s, &lp);
}

/* Send a KeyPress Event to system */
void le_iml_sendback_key(iml_session_t * s, IMKeyEventStruct * key)
{
    iml_inst *lp;

    lp = (iml_inst *) s->If->m->iml_make_keypress_inst(s, key);
    s->If->m->iml_execute(s, &lp);
}

/* Begin catch the system Key Event */
void le_iml_conversion_on(iml_session_t * s)
{
    iml_inst *lp;

    lp = (iml_inst *) s->If->m->iml_make_start_conversion_inst(s);
    lp = s->If->m->iml_execute(s, &lp);
}

/* Do not catch the system Key Event */
void le_iml_conversion_off(iml_session_t * s)
{
    iml_inst *lp;

    lp = (iml_inst *) s->If->m->iml_make_end_conversion_inst(s);
    s->If->m->iml_execute(s, &lp);
}

/* Start Status Area Information */
void le_iml_status_start(iml_session_t * s)
{
    iml_inst *lp;
    iml_inst *rrv = NULL;

    lp = (iml_inst *) s->If->m->iml_make_status_start_inst(s);
    s->If->m->iml_link_inst_tail(&rrv, lp);
}

/* Destroy Status Area Information */
void le_iml_status_enddraw(iml_session_t * s)
{
    iml_inst *lp;

    lp = (iml_inst *) s->If->m->iml_make_status_done_inst(s);
    s->If->m->iml_execute(s, &lp);
}

/* Draw Status line information */
void le_iml_status_draw(iml_session_t * s, UTFCHAR * status_str)
{
    int len;
    iml_inst *lp;
    iml_inst *rrv = NULL;
    IMText *im_text;

    le_iml_status_start(s);

    im_text = le_iml_make_imtext(s, status_str, NULL);
    lp = (iml_inst *) s->If->m->iml_make_status_draw_inst(s, im_text);
    s->If->m->iml_link_inst_tail(&rrv, lp);
    s->If->m->iml_execute(s, &rrv);
}

void le_iml_preedit_start(iml_session_t * s)
{
    iml_inst *lp;
    iml_inst *rrv = NULL;

    lp = (iml_inst *) s->If->m->iml_make_preedit_start_inst(s);
    s->If->m->iml_link_inst_tail(&rrv, lp);
}

/* Destroy Pre_edit Area Information */
void le_iml_preedit_enddraw(iml_session_t * s)
{
    iml_inst *lp;
    iml_inst *rrv = NULL;

    lp = (iml_inst *) s->If->m->iml_make_preedit_erase_inst(s);
    s->If->m->iml_link_inst_tail(&rrv, lp);
    lp = (iml_inst *) s->If->m->iml_make_preedit_done_inst(s);
    s->If->m->iml_link_inst_tail(&rrv, lp);

    s->If->m->iml_execute(s, &rrv);
}

/* Draw Pre_edit Area Information */
void le_iml_preedit_draw(iml_session_t * s, UTFCHAR * preedit_buf,
                         int caret_pos)
{
    int i, len;
    iml_inst *lp;
    iml_inst *rrv = NULL;
    IMText *im_text;
    IMText Preedit_Buf;

    le_iml_preedit_start(s);

    len = UTFCHARLen(preedit_buf);
    DEBUG_printf("len:%d, caret_pos:%d\n", len, caret_pos);

    if (len == 0) {
        lp = (iml_inst *) s->If->m->iml_make_preedit_erase_inst(s);
        s->If->m->iml_link_inst_tail(&rrv, lp);
        s->If->m->iml_execute(s, &rrv);
        return;
    }

    im_text = le_iml_make_imtext(s, preedit_buf, NULL);

/*
        if (caret_pos>len) caret_pos = len;
        for(i = 0; i < caret_pos; i ++) {
                le_iml_set_feedback(&(im_text->feedback[i]), IMReverse);
        }
        for(i = caret_pos; i < len; i ++) {
                le_iml_set_feedback(&(im_text->feedback[i]), IMUnderline);
        }
        le_iml_set_feedback(&(im_text->feedback[caret_pos - 1]), IMNormal);
*/

    lp = (iml_inst *) s->If->m->iml_make_preedit_draw_inst(s, im_text);
    s->If->m->iml_link_inst_tail(&rrv, lp);

    if (caret_pos != -1) {
        lp = (iml_inst *) s->If->m->iml_make_preedit_caret_inst(s,
                                                                caret_pos);
        s->If->m->iml_link_inst_tail(&rrv, lp);
    }
    s->If->m->iml_execute(s, &rrv);
}

void le_iml_lookup_start(iml_session_t * s, LayoutInfo * layout)
{
    iml_inst *lp;
    IMLookupStartCallbackStruct *start;

    start =
        (IMLookupStartCallbackStruct *) s->If->m->iml_new(s,
                                                          sizeof
                                                          (IMLookupStartCallbackStruct));
    memset(start, 0, sizeof(IMLookupStartCallbackStruct));

    start->IMPreference =
        (LayoutInfo *) s->If->m->iml_new(s, sizeof(LayoutInfo));
    memset(start->IMPreference, 0, sizeof(LayoutInfo));

    start->IMPreference->choice_per_window = layout->choice_per_window;
    start->IMPreference->ncolumns = layout->ncolumns;
    start->IMPreference->nrows = layout->nrows;
    start->IMPreference->drawUpDirection = layout->drawUpDirection;
    start->IMPreference->whoOwnsLabel = IMOwnsLabel;

    start->CBPreference = NULL;
    start->whoIsMaster = IMIsMaster;

    lp = (iml_inst *) s->If->m->iml_make_lookup_start_inst(s, start);
    s->If->m->iml_execute(s, &lp);
}

/* Destroy Candidate Selection Area */
void le_iml_lookup_enddraw(iml_session_t * s)
{
    iml_inst *lp;

    lp = (iml_inst *) s->If->m->iml_make_lookup_done_inst(s);
    s->If->m->iml_execute(s, &lp);
}

/* Draw Candidate Selection Area */
void le_iml_lookup_draw(iml_session_t * s, int lookup_num,
                        UTFCHAR ** lookup_strs,
                        IMFeedbackList ** lookup_feedbacks,
                        UTFCHAR ** label_strs,
                        IMFeedbackList ** label_feedbacks,
                        UTFCHAR * title_str,
                        IMFeedbackList * title_feedbacks,
                        int current_candidate_id, int vertical)
{
    int i, char_len;
    char begin_char;
    int max_len = 0;
    iml_inst *lp;
    IMText **candidates, **labels;
    IMLookupDrawCallbackStruct *draw;
    IMFeedbackList *feedback = NULL;

    IMLookupStartCallbackStruct *start;

    if (lookup_num <= 0)
        return;
    if (lookup_strs == NULL)
        return;
    if (label_strs == NULL)
        return;

    start =
        (IMLookupStartCallbackStruct *) s->If->m->iml_new(s,
                                                          sizeof
                                                          (IMLookupStartCallbackStruct));

    start->whoIsMaster = IMIsMaster;
    start->IMPreference =
        (LayoutInfo *) s->If->m->iml_new(s, sizeof(LayoutInfo));

    start->IMPreference->choice_per_window = lookup_num;
    start->IMPreference->ncolumns = (vertical) ? 1 : lookup_num;
    start->IMPreference->nrows = (vertical) ? lookup_num : 1;
    start->IMPreference->drawUpDirection = vertical;
    start->IMPreference->whoOwnsLabel = IMOwnsLabel;

    lp = s->If->m->iml_make_lookup_start_inst(s, start);
    s->If->m->iml_execute(s, &lp);


    /* Set candidate data */
    candidates =
        (IMText **) s->If->m->iml_new(s, lookup_num * sizeof(IMText *));
    if (candidates == NULL)
        return;
    memset(candidates, 0, lookup_num * sizeof(IMText *));
    for (i = 0; i < lookup_num; i++) {
        feedback = NULL;
        if (lookup_feedbacks != NULL)
            feedback = lookup_feedbacks[i];

        if (lookup_strs[i])
            candidates[i] = (IMText *) le_iml_make_imtext(s, lookup_strs[i], feedback);
        else
            candidates[i] = (IMText *) NULL;
    }

    labels =
        (IMText **) s->If->m->iml_new(s, lookup_num * sizeof(IMText *));
    if (labels == NULL)
        return;
    memset(labels, 0, lookup_num * sizeof(IMText *));
    for (i = 0; i < lookup_num; i++) {
        feedback = NULL;
        if (label_feedbacks != NULL)
            feedback = label_feedbacks[i];

        if (label_strs[i])
            labels[i] =
                (IMText *) le_iml_make_imtext(s, label_strs[i], feedback);
        else
            labels[i] = (IMText *) NULL;
    }

    draw =
        (IMLookupDrawCallbackStruct *) s->If->m->iml_new(s,
                                                         sizeof
                                                         (IMLookupDrawCallbackStruct));
    memset(draw, 0, sizeof(IMLookupDrawCallbackStruct));

    if (title_str == NULL)
        draw->title = NULL;
    else
        draw->title =
            (IMText *) le_iml_make_imtext(s, title_str, title_feedbacks);

    draw->n_choices = lookup_num;
    draw->index_of_first_candidate = 0;
    draw->index_of_last_candidate = lookup_num - 1;
    draw->index_of_current_candidate = current_candidate_id;

    /* Set choices data */
    draw->choices =
        (IMChoiceObject *) s->If->m->iml_new(s,
                                             lookup_num *
                                             sizeof(IMChoiceObject));
    memset(draw->choices, 0, lookup_num * sizeof(IMChoiceObject));

    for (i = 0; i < lookup_num; i++) {
        IMText *vt;		/* for value */
        IMText *lt;		/* for label */

        vt = draw->choices[i].value = candidates[i];
        lt = draw->choices[i].label = labels[i];

        if (vt && max_len < vt->char_length)
            max_len = vt->char_length;
    }

    draw->max_len = max_len;

    lp = (iml_inst *) s->If->m->iml_make_lookup_draw_inst(s, draw);
    s->If->m->iml_execute(s, &lp);
}

/* Start An Auxiliary Window */
void le_iml_aux_start(iml_session_t * s, char *classname)
{
    iml_inst *lp;
    IMAuxStartCallbackStruct *aux;

    aux =
        (IMAuxStartCallbackStruct *) s->If->m->iml_new(s,
                                                       sizeof
                                                       (IMAuxStartCallbackStruct));
    memset(aux, 0, sizeof(IMAuxStartCallbackStruct));
    aux->aux_name = classname;

    lp = (iml_inst *) s->If->m->iml_make_aux_start_inst(s, aux);
    s->If->m->iml_execute(s, &lp);

    DEBUG_printf("iml_aux_start ----- auxwin name :%s \n", classname);
}

/* Close the Auxiliary Window */
void le_iml_aux_done(iml_session_t * s, char *classname)
{
    iml_inst *lp;
    IMAuxDoneCallbackStruct *aux;

    aux =
        (IMAuxDoneCallbackStruct *) s->If->m->iml_new(s,
                                                      sizeof
                                                      (IMAuxDoneCallbackStruct));
    memset(aux, 0, sizeof(IMAuxDoneCallbackStruct));
    aux->aux_name = classname;

    lp = (iml_inst *) s->If->m->iml_make_aux_done_inst(s, aux);
    s->If->m->iml_execute(s, &lp);

    DEBUG_printf("iml_aux_done -------------------------------- end \n");
}


/* Draw the Auxiliary Window Items */
void le_iml_aux_draw(iml_session_t * s, char *classname,
                     int count_integers, int *integers,
                     int count_strings, int *string_char_lens,
                     UTFCHAR **strings)
{
    iml_inst *lp;
    IMText *lt;
    IMAuxDrawCallbackStruct *aux;
    int i, len;

    aux = (IMAuxDrawCallbackStruct *) s->If->m->iml_new(s, sizeof(IMAuxDrawCallbackStruct));
    memset(aux, 0, sizeof(IMAuxDrawCallbackStruct));
    aux->aux_name = classname;

    aux->count_integer_values = count_integers;
    if (count_integers) {
        aux->integer_values = (int *)s->If->m->iml_new(s, sizeof(int) * count_integers);
        memset(aux->integer_values, 0, sizeof(int) * count_integers);
        for (i = 0; i < count_integers; i++) {
            aux->integer_values[i] = integers[i];
        }
    }

    aux->count_string_values = count_strings;
    if (count_strings) {
        aux->string_values = (IMText *) s->If->m->iml_new(s, sizeof(IMText) * count_strings);
        memset(aux->string_values, 0, sizeof(IMText) * count_strings);

        aux->string_values->encoding = UTF16_CODESET;
        for (i = 0, lt = aux->string_values; i < count_strings; i++, lt++) {
            len = (strings[i])?(UTFCHARLen(strings[i])):(0);
            lt->text.utf_chars = (UTFCHAR *)s->If->m->iml_new(s, (len+1)*sizeof(UTFCHAR));
            memset(lt->text.utf_chars, 0,(len+1)*sizeof(UTFCHAR));
            lt->char_length = len+1;
            if (strings[i])
                memcpy(lt->text.utf_chars, strings[i], len*sizeof(UTFCHAR));
        }
    }

    lp = (iml_inst *) s->If->m->iml_make_aux_draw_inst(s, aux);
    s->If->m->iml_execute(s, &lp);

    DEBUG_printf("iml_aux_draw -------------------------------- end \n");
}

static char* dummy="";

/* Draw the Auxiliary Window Items */
void le_iml_aux_draw_native(iml_session_t * s, char *classname,
                             int count_integers, int *integers,
                             int encoding_id, int count_strings,  char **strings)
{
    int i, from_len, to_left;
    char* src, *tmp_ptr;

    if (encoding_id != UTF16_CODESET) {
        UTFCHAR * utf16strings[count_strings+1]; // to avoid count_strings == 0
        for (i=0; i < count_strings; ++i) {
            src = (strings[i])?(strings[i]):(dummy);
            from_len = strlen(src) + 1;

            utf16strings[i] = (UTFCHAR*)calloc(from_len+1, sizeof(UTFCHAR));
            utf16strings[i][0] = 0;
            utf16strings[i][from_len-1] = 0;
            utf16strings[i][from_len] = 0;

            tmp_ptr = (char*)utf16strings[i];
            to_left = (from_len)*sizeof(UTFCHAR);
            Convert_Native_To_UTF16(encoding_id, strings[i], from_len, (char **)&tmp_ptr, (size_t *) & to_left);
        }

        le_iml_aux_draw(s, classname, count_integers, integers, count_strings, NULL, utf16strings);

        for (i=0; i < count_strings; ++i)
            free(utf16strings[i]);
    } else {
        le_iml_aux_draw(s, classname, count_integers, integers, count_strings, NULL, (UTFCHAR**)strings);
    }
}
