#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iiimcf.h>
#include "common.h"

IIIMCF_handle handle;

void
test_iiimf_error(
    IIIMF_status st
)
{
    fprintf(stderr, "IIIMF error:%d\n", st);
    exit(255);
}

void
check(
    IIIMF_status st
)
{
    if (st != IIIMF_STATUS_SUCCESS)
	test_iiimf_error(st);
    return;
}

void
test_assert(
    int b
)
{
    if (!b) abort();
}

char*
test_format_iiimcf_string(
    const IIIMP_card16 *pu16
)
{
    char *p, *pr, *pr2;
    int i, rest;
    int initlen = 256;

    pr = (char*) malloc(sizeof(*p) * initlen);
    rest = initlen - 1;
    for (p = pr; *pu16; pu16++) {
	if (*pu16 < 128) {
	    if (rest < 1) {
		rest += initlen;
		initlen *= 2;
		pr2 = (char*) realloc(pr, sizeof(*p) * initlen);
		p = pr2 + (p - pr);
		pr = pr2;
	    }
	    *p++ = *pu16;
	    rest--;
	} else {
	    if (rest < 16) {
		rest += initlen;
		initlen *= 2;
		pr2 = (char*) realloc(pr, sizeof(*p) * initlen);
		p = pr2 + (p - pr);
		pr = pr2;
	    }
	    i = snprintf(p, 15, " U+%.4X", *pu16);
	    p += i;
	    rest -= i ;
	}
    }
    *p = '\0';

    return pr;
}

void
test_output_iiimcf_string(
    const IIIMP_card16 *pu16
)
{
    char *str;
    str = test_format_iiimcf_string(pu16);
    if (!str) return;
    fputs(str, stderr);
    free(str);
}    

void
test_init()
{
    IIIMF_status st;
    IIIMCF_attr attr;

    st = iiimcf_initialize(IIIMCF_ATTR_NULL);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
    st = iiimcf_create_attr(&attr);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
    st = iiimcf_attr_put_string_value(attr, IIIMCF_ATTR_CLIENT_TYPE,
				      "IIIMCF basic test");
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
    st = iiimcf_create_handle(attr, &handle);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
    st = iiimcf_destroy_attr(attr);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
}

void
test_show_objects()
{
    IIIMF_status st;
    int i, n;
    const IIIMP_card16 *pu16;
    const IIIMCF_object_descriptor *pdesc;
    IIIMCF_downloaded_object objs[1];
    char *domain, *hrn, *signature, *user, *objtype, *filename;

    st = iiimcf_get_object_descriptor_list(handle, &n, &pdesc);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);

    for (i = 0; i < n; i++, pdesc++) {
	domain = test_format_iiimcf_string(pdesc->domain);
	hrn = test_format_iiimcf_string(pdesc->hrn);
	signature = test_format_iiimcf_string(pdesc->signature);
	user = test_format_iiimcf_string(pdesc->user);
	filename = NULL;

	objtype = NULL;
	switch (pdesc->predefined_id) {
	  case IIIMP_IMATTRIBUTE_CHARACTER_CONVERSION_DEFINITION_RULE:
	   objtype = "CCDEF";
	   break;

	  case IIIMP_IMATTRIBUTE_BINARY_GUI_OBJECT:
	   objtype = "Binary GUI";
	  case IIIMP_IMATTRIBUTE_BINARY_LIGHT_WEIGHT_ENGINE_OBJECT:
	   if (!objtype) objtype = "Binary LWE";
	  case IIIMP_IMATTRIBUTE_SCRIPT_GUI_OBJECT:
	   if (!objtype) objtype = "Script GUI";
	  case IIIMP_IMATTRIBUTE_SCRIPT_LIGHT_WEIGHT_ENGINE_OBJECT:
	   if (!objtype) objtype = "Script LWE";

	   st =  iiimcf_get_downloaded_objects(handle, 1, &pdesc, objs);
	   if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
	   st = iiimcf_get_downloaded_object_filename(*objs, &pu16);
	   if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
	   filename = test_format_iiimcf_string(pu16);
	   break;

	  case IIIMP_IMATTRIBUTE_JAR_GUI_OBJECT:
	   objtype = "Jar GUI";
	   break;
	  case IIIMP_IMATTRIBUTE_JAR_LIGHT_WEIGHT_ENGINE_OBJECT:
	   objtype = "Jar LWE";
	   break;
	  default:
	   objtype = "Unknown";
	   break;
	}

	fprintf(stderr,
		"Object %d ----------------------------------\n"
		"  category: %d\n"
		"  size: %d\n"
		"  objtype type: %s(%d)\n"
		"  dynamic_id: %d\n"
		"  reverse domain name: %s\n"
		"  HRN: %s\n"
		"  signature: %s\n"
		"  user: %s\n"
		"  filename: %s\n"
		"--------------------------------------------\n",
		i,
		pdesc->category,
		pdesc->size,
		objtype, pdesc->predefined_id,
		pdesc->dynamic_id,
		domain,
		hrn,
		signature,
		user,
		filename ? filename : "");

	free(domain);
	free(hrn);
	free(signature);
	free(user);
	if (filename) free(filename);
    }
}

void
test_show_input_methods()
{
    IIIMF_status st;
    int i, n, j, nlangs;
    IIIMCF_input_method *pims;
    const IIIMP_card16 *u16idname, *u16hrn, *u16domain;
    char *idname, *hrn, *domain;
    const char *langid;
    IIIMCF_language *plangs;

    st = iiimcf_get_supported_input_methods(handle, &n, &pims);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);

    if (n == 0) {
	fprintf(stderr, "The server offers no INPUTMETHOD_DESCRIPTOR.\n");
	return;
    }

    for (i = 0; i < n; i++) {
	check(iiimcf_get_input_method_desc(pims[i], &u16idname, &u16hrn, &u16domain));
	check(iiimcf_get_input_method_languages(pims[i], &nlangs, &plangs));

	idname = test_format_iiimcf_string(u16idname);
	hrn = test_format_iiimcf_string(u16hrn);
	domain = test_format_iiimcf_string(u16domain);

	fprintf(stderr,
		"Input Method %d ----------------------------------\n"
		"  idname: %s\n"
		"  HRN: %s\n"
		"  domain: %s\n"
		"  langs: ",
		i, idname, hrn, domain);

	for (j = 0; j < nlangs; j++) {
	    check(iiimcf_get_language_id(plangs[j], &langid));
	    fprintf(stderr, "%s, ", langid);
	}
	fprintf(stderr,	"\n--------------------------------------------\n");

	free(idname);
	free(hrn);
	free(domain);
    }
}


void
test_uninit()
{
    IIIMF_status st;

    st = iiimcf_destroy_handle(handle);
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
    st = iiimcf_finalize();
    if (st != IIIMF_STATUS_SUCCESS) test_iiimf_error(st);
}

void
test_forwardevent(
    IIIMCF_context c,
    IIIMCF_event ev
)
{
    IIIMF_status st;

    st = iiimcf_forward_event(c, ev);
    if (st == IIIMF_STATUS_SUCCESS) {
	fprintf(stderr, "--- successfully forwarded.\n");
    } else if (st == IIIMF_STATUS_EVENT_NOT_FORWARDED) {
	fprintf(stderr, "--- not forwarded.\n");
    } else {
	test_iiimf_error(st);
    }
}

void
test_forwardkey(
    IIIMCF_context c,
    int keycode,
    int keychar,
    int mod
)
{
    IIIMCF_event ev;
    IIIMCF_keyevent kev;

    kev.keycode = keycode;
    kev.keychar = keychar;
    kev.modifier = mod;
    kev.time_stamp = time(NULL);

    fprintf(stderr, "forward keyevent (%02X, %02X, %02X) ",
	    keycode, keychar, mod);
    check(iiimcf_create_keyevent(&kev, &ev));
    test_forwardevent(c, ev);
}

void
test_seticfocus(
    IIIMCF_context c
)
{
    IIIMCF_event ev;

    check(iiimcf_create_seticfocus_event(&ev));
    fprintf(stderr, "forward SETICFOCUS ");
    test_forwardevent(c, ev);
}

void
test_unseticfocus(
    IIIMCF_context c
)
{
    IIIMCF_event ev;

    check(iiimcf_create_unseticfocus_event(&ev));
    fprintf(stderr, "forward UNSETICFOCUS ");
    test_forwardevent(c, ev);
}

void
test_trigger(
    IIIMCF_context c,
    int flag
)
{
    IIIMF_status st;
    IIIMCF_event ev;

    fprintf(stderr, "forward trigger notify event (%d) ", flag);
    check(iiimcf_create_trigger_notify_event(flag, &ev));
    st = iiimcf_forward_event(c, ev);
    if (st == IIIMF_STATUS_SUCCESS) {
	fprintf(stderr, "--- successfully forwarded.\n");
    } else if (st == IIIMF_STATUS_EVENT_NOT_FORWARDED) {
	fprintf(stderr, "--- not forwarded.\n");
    } else {
	test_iiimf_error(st);
    }
}

char*
test_format_iiimcf_text(
    IIIMCF_text t
)
{
    char *p, *pr, *pr2;
    int i, j, n, l, rest;
    int initlen = 256;
    IIIMP_card16 ch;
    int nfb;
    const IIIMP_card32 *pids, *pfbs;

    pr = p = (char*) malloc(sizeof(*p) * initlen);
    rest = initlen - 1;

    check(iiimcf_get_text_length(t, &n));

    for (i = 0; i < n; i++) {
	check(iiimcf_get_char_with_feedback(t, i, &ch, &nfb, &pids, &pfbs));
	if (rest <= (16 + 2 + 20 * nfb)) {
	    rest += initlen;
	    initlen *= 2;
	    pr2 = (char*) realloc(pr, sizeof(*p) * initlen);
	    p = pr2 + (p - pr);
	    pr = pr2;
	}
	if (ch < 128) {
	    *p++ = ch;
	    rest--;
	} else {
	    l = snprintf(p, 15, " U+%04X", ch);
	    p += l;
	    rest -= l;
	}
	if (nfb > 0) {
	    *p++ = '[';
	    rest--;
	    for (j = 0; j < nfb; j++, pids++, pfbs++) {
		l = snprintf(p, 20, "(%d,%d)", *pids, *pfbs);
		p += l;
		rest -= l;
	    }
	    rest--;
	    *p++ = ']';
	}
    }
    *p = '\0';

    return pr;
}

void
test_show_preedit_info(
    IIIMCF_context c
)
{
    int cpos;
    IIIMF_status st;
    IIIMCF_text text;
    
    st = iiimcf_get_preedit_text(c, &text, &cpos);
    if (st == IIIMF_STATUS_SUCCESS) {
	char *str;

	str = test_format_iiimcf_text(text);
	fprintf(stderr, "Preedit(%d):%s\n", cpos, str);
	free(str);
    } else if (st == IIIMF_STATUS_NO_PREEDIT) {
	fprintf(stderr, "Preedit is disabled.\n");
    } else {
	test_iiimf_error(st);
    }
}

void
test_show_status_info(
    IIIMCF_context c
)
{
    IIIMF_status st;
    IIIMCF_text text;
    
    st = iiimcf_get_status_text(c, &text);
    if (st == IIIMF_STATUS_SUCCESS) {
	char *str;

	str = test_format_iiimcf_text(text);
	fprintf(stderr, "Status:%s\n", str);
	free(str);
    } else if (st == IIIMF_STATUS_NO_STATUS_TEXT) {
	fprintf(stderr, "Status is disabled.\n");
    } else {
	test_iiimf_error(st);
    }
}

void
test_show_lookup_choice_info(
    IIIMCF_context c
)
{
    IIIMF_status st;
    IIIMCF_lookup_choice ilc;

    st = iiimcf_get_lookup_choice(c, &ilc);
    if (st == IIIMF_STATUS_SUCCESS) {
	char *itemstr, *labelstr;
	int i, flag;
	int size, idx_first, idx_last, idx_current;
	IIIMCF_text cand, label;

	check(iiimcf_get_lookup_choice_size(ilc, &size,
					    &idx_first,
					    &idx_last,
					    &idx_current));

	for (i = 0; i < size; i++) {
	    check(iiimcf_get_lookup_choice_item(ilc, i, &cand, &label, &flag));
	    if (flag & IIIMCF_LOOKUP_CHOICE_ITEM_ENABLED) {
		itemstr = test_format_iiimcf_text(cand);
		labelstr = test_format_iiimcf_text(label);
		fprintf(stderr, "LC[%d](%s): %s\n", i, labelstr, itemstr);
		free(labelstr);
		free(itemstr);
	    }
	}
    } else if (st == IIIMF_STATUS_NO_LOOKUP_CHOICE) {
	fprintf(stderr, "Lookup choice is disabled.\n");
    } else {
	test_iiimf_error(st);
    }
}

void
test_show_commit_info(
    IIIMCF_context c
)
{
    IIIMF_status st;
    IIIMCF_text text;
    
    st = iiimcf_get_committed_text(c, &text);
    if (st == IIIMF_STATUS_SUCCESS) {
	char *str;

	str = test_format_iiimcf_text(text);
	fprintf(stderr, "Committed text:%s\n", str);
	free(str);
    } else if (st == IIIMF_STATUS_NO_COMMITTED_TEXT) {
	fprintf(stderr, "No committed text.\n");
    } else {
	test_iiimf_error(st);
    }
}

void
test_show_version_info(
    IIIMCF_handle h
)
{
    IIIMF_status st;
    int v, lib_major, lib_minor, proto_major, proto_minor;

    check(iiimcf_get_version_number(h, IIIMCF_LIBRARY_VERSION | IIIMCF_MAJOR_VERSION,
				    &lib_major));
    check(iiimcf_get_version_number(h, IIIMCF_LIBRARY_VERSION | IIIMCF_MINOR_VERSION,
				    &lib_minor));

    check(iiimcf_get_version_number(h, IIIMCF_PROTOCOL_VERSION | IIIMCF_MAJOR_VERSION,
				    &proto_major));
    check(iiimcf_get_version_number(h, IIIMCF_PROTOCOL_VERSION | IIIMCF_MINOR_VERSION,
				    &proto_minor));

    fprintf(stderr, "Version: LIB:%d.%d, PROTOCOL:%d.%d\n",
	    lib_major, lib_minor,
	    proto_major, proto_minor);

    st = iiimcf_get_version_number(h, IIIMCF_PROTOCOL_VERSION, &v);
    test_assert(st == IIIMF_STATUS_ARGUMENT);
    st = iiimcf_get_version_number(h, IIIMCF_LIBRARY_VERSION, &v);
    test_assert(st == IIIMF_STATUS_ARGUMENT);
    st = iiimcf_get_version_number(h, IIIMCF_MAJOR_VERSION, &v);
    test_assert(st == IIIMF_STATUS_ARGUMENT);
    st = iiimcf_get_version_number(h, IIIMCF_MINOR_VERSION, &v);
    test_assert(st == IIIMF_STATUS_ARGUMENT);
}

static void
show_aux(
    const char *name,
    IIIMCF_event ev
)
{
    int i;
    char *str;
    const IIIMP_card16 *aux_name;
    IIIMP_card32 idx;
    int num_intvals, num_strvals;
    const IIIMP_card32 *pints;
    const IIIMP_card16 **pstrs;
	
    check(iiimcf_get_aux_event_value(ev, &aux_name, &idx,
				     &num_intvals, &pints,
				     &num_strvals, &pstrs));
    str = test_format_iiimcf_string(aux_name);
    fprintf(stderr, "Event[%s/%s:%d]: ", name, str, idx);
    free(str);
    if (num_intvals > 0) {
	fprintf(stderr, "(%d", *pints);
	for (i = 1; i < num_intvals; i++) {
	    fprintf(stderr, ",%d", pints[i]);
	}
	fprintf(stderr, ")");
    }
    if (num_strvals > 0) {
	str = test_format_iiimcf_string(*pstrs);
	fprintf(stderr, "{%s", str);
	free(str);
	for (i = 1; i < num_strvals; i++) {
	    str = test_format_iiimcf_string(pstrs[i]);
	    fprintf(stderr, ",%s", str);
	    free(str);
	}
	fprintf(stderr, "}");
    }
    fprintf(stderr, "\n");
}

void
test_show_incoming_events(
    IIIMCF_context c,
    int dispatchp
)
{
    IIIMF_status st;
    IIIMCF_event ev;
    IIIMCF_event_type et;

    while ((st = iiimcf_get_next_event(c, &ev))
	   == IIIMF_STATUS_SUCCESS) {
	check(iiimcf_get_event_type(ev, &et));
	switch (et) {
	  case IIIMCF_EVENT_TYPE_KEYEVENT:
	  {
	      IIIMCF_keyevent kev;

	      check(iiimcf_get_keyevent_value(ev, &kev));
	      fprintf(stderr, "Event[KEY]: %d, %d, %d, %d\n",
		      kev.keycode, kev.keychar, kev.modifier, kev.time_stamp);
	      break;
	  }
	  case IIIMCF_EVENT_TYPE_TRIGGER_NOTIFY:
	  {
	      int flag;

	      check(iiimcf_get_trigger_notify_flag(ev, &flag));
	      fprintf(stderr, "Event[TRIGGER-NOTIFY]: %d\n", flag);
	      break;
	  }
	  case IIIMCF_EVENT_TYPE_UI_PREEDIT_START:
	   fprintf(stderr, "Event[PREEDIT-START]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE:
	   fprintf(stderr, "Event[PREEDIT-CHANGE]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE:
	   fprintf(stderr, "Event[PREEDIT-DONE]\n");
	   break;

	  case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_START:
	   fprintf(stderr, "Event[LOOKUP-CHOICE-START]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_CHANGE:
	   fprintf(stderr, "Event[LOOKUP-CHOICE-CHANGE]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_DONE:
	   fprintf(stderr, "Event[LOOKUP-CHOICE-DONE]\n");
	   break;

	  case IIIMCF_EVENT_TYPE_UI_STATUS_START:
	   fprintf(stderr, "Event[STATUS-START]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_STATUS_CHANGE:
	   fprintf(stderr, "Event[STATUS-CHANGE]\n");
	   break;
	  case IIIMCF_EVENT_TYPE_UI_STATUS_DONE:
	   fprintf(stderr, "Event[STATUS-DONE]\n");
	   break;

	  case IIIMCF_EVENT_TYPE_UI_COMMIT:
	   fprintf(stderr, "Event[COMMIT]\n");
	   break;

	  case IIIMCF_EVENT_TYPE_AUX_START:
	   show_aux("AUX-START", ev);
	   break;

	  case IIIMCF_EVENT_TYPE_AUX_DRAW:
	   show_aux("AUX-DRAW", ev);
	   break;
	  case IIIMCF_EVENT_TYPE_AUX_SETVALUES:
	   show_aux("AUX-SETVALUES", ev);
	   break;
	  case IIIMCF_EVENT_TYPE_AUX_DONE:
	   show_aux("AUX-DONE", ev);
	   break;
	   
	  default:
	   fprintf(stderr, "Event[Unknown:%d]\n", et);
	   break;
	}
	if (dispatchp) {
	    st = iiimcf_dispatch_event(c, ev);
	    if (st != IIIMF_STATUS_SUCCESS) {
		if (st == IIIMF_STATUS_COMPONENT_FAIL) {
		    fprintf(stderr, "at least one component reported failure.\n");
		} else if (st == IIIMF_STATUS_COMPONENT_INDIFFERENT) {
		    fprintf(stderr, "none of the components deal with the event.\n");
		} else {
		    fprintf(stderr, "fail to dispatch");
		    test_iiimf_error(st);
		}
	    }
	}
	check(iiimcf_ignore_event(ev));
    }
    if (st != IIIMF_STATUS_NO_EVENT) {
	test_iiimf_error(st);
    }

}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */

