/*
###
### This file is part of
###
###                        TurboLinux  ZWinPro
###
###                 Copyright (C) 1999-2000 TurboLinux, Inc.
###                        All Rights Reserved
### Distributed under the terms of the GNU General Public License (GPL)
###
###
### Authors:     TurboLinux Chinese Development Team:
###              Justin Yu   <justiny@turbolinux.com.cn>
###              Sean Chen   <seanc@turbolinux.com.cn>
###              Daniel Fang <danf@turbolinux.com.cn>
###
*/

//Why onthespot:
//onthespot provide a close interaction between the applications
//and the user. In onthespot style, the user's feeling is to edit
//on the insertion place. 

//Important:
//onthespot style is not a good choice for many input methods, such
//as pinyin, which has multi-selection for one PinYin.

//Important:
//The current IMdkit has bugs in on-the-spot implementation.
//Please make sure you use the modified version from
//http://www.turbolinux.com.cn/~justiny/project-chinput.html


//OnTheSpot Style works for Java JDK, Mozilla(default style) and
//some simple demos.  Motif's internal TextField/Text widgets can 
//work but still has some problems in IC focus.
//
//OnTheSpot is the best style for some input methods, but don't
//expect it work on any applications, because the programmers are
//too lazy to add on-the-spot support on their programs :)
//


//The order for preedit callback and commit string is:
//	(1). Commit String
//	(2). Update Preedit Callbacks
//which is expected by most applications.
//it maybe in the reverse order to some other IM server
//so in such case, the application will do the following
//      (1) when composed text exits, clear the text
//      (2) Commit text
//      (3) when composed text exits, redraw the existing preedit
//as described in Mozilla.

//FIXME or FIXMOZILLA
//mozilla's another bug:
//In Chinput's onthespot style, status area and auxiliary
//area(candidate area) are all necessary. If using the status area as both
//status and candidate window, the user cannot input conveniently. So it is
//better to popup the candidate window. candidate window and status window all
//want to floating up, so it cause the application crashing.
//This happened on the recent snapshot of mozilla, but M16 is fine.

//Suggestion:
//To extend the XIM protocol to support spot location in the
//onthespot style!!!

#include "all.h"

static char *chinput_onspot_mbstocts (char *s)
{
	XTextProperty tp;
	XmbTextListToTextProperty (display, &s, 1, XCompoundTextStyle, &tp);
	return tp.value;
}

#if 0
// Nothing else call it??
static void chinput_onspot_preedit_start (void)
{
	// Client usually does not support this callback
	IMPreeditCBStruct data;
	IC *ic = (IC *)chinput_find_ic (last_icid);
	if (!ic)
		return;

	data.major_code = XIM_PREEDIT_START;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;
	IMCallCallback (this_xims, (XPointer)&data);
}

// Nothing else call it??
static void chinput_onspot_preedit_done (void)
{
	//client usually does not support this callback
	IMPreeditCBStruct data;
	IC *ic = (IC *)chinput_find_ic (last_icid);
	if (!ic)
		return;
	data.major_code = XIM_PREEDIT_DONE;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;
	IMCallCallback (this_xims, (XPointer)&data);
}

// Nothing else call it??
static void chinput_onspot_preedit_caret (void)
{
	//Chinput does not need caret operation for the time being
	//because it extracts preedit string and status/candidate string
	//from IME, and replace the whole client string
}

// Nothing else call it??
static void chinput_onspot_status_start (void)
{
	//client usually does not support this
}

// Nothing else call it??
static void chinput_onspot_status_done (void)
{
	//client usually does not support this
}
#endif

static void chinput_onspot_preedit_draw (char *s, int len)
{
	int i;
	char *buf;
	IC *ic = (IC *)chinput_find_ic (last_icid);
	IMPreeditCBStruct data;
	XIMText text;
	XIMFeedback feedback[128] = {0};
	char *convtext = s;
	char tbuf[80];

	if (!ic)
		return;

	//trim spaces
	while (len > 0 && s[len-1] == ' ')
		len--;
	s[len] = '\0';

	data.major_code = XIM_PREEDIT_DRAW;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;

	for (i=0; i<128; i++)
		feedback[i] = XIMUnderline;
	feedback[len] = 0;

	//data.todo.draw.caret = ic->len+1;
	data.todo.draw.caret = 0;
	data.todo.draw.chg_first = 0;
	data.todo.draw.chg_length = ic->len;	//the previous length for clear
	data.todo.draw.text = &text;
	
	//the input server only support multibyte string.
	//so the client should support both multibyte and widechar
	text.encoding_is_wchar = False;

	//flag_encoding = chinput_find_enc_by_ic (ic->id);
	flag_encoding = ic->encoding;

	if ( ( (HZServer.encoding == HZSERVER_ENCODING_GB2312)
					|| (HZServer.encoding == HZSERVER_ENCODING_GB18030) )
				&& ( (flag_encoding == HZSERVER_ENCODING_BIG5)
					|| (flag_encoding == HZSERVER_ENCODING_BIG5HKSCS)
					|| (flag_lock == LOCK_BIG5) ) )
	{
		// Convert to Big5 encoding
		chinput_gbmixstring_to_big5mixstring (convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}
	if ( ( (HZServer.encoding == HZSERVER_ENCODING_BIG5)
					|| (HZServer.encoding == HZSERVER_ENCODING_BIG5HKSCS) )
				&& ( (flag_encoding == HZSERVER_ENCODING_GB2312)
					|| (flag_encoding == HZSERVER_ENCODING_GB18030)
					|| (flag_lock == LOCK_GB) ) )
	{
		// Convert to gb encoding
		chinput_big5mixstring_to_gbmixstring (convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}

	convtext[len] = 0;
	if(flag_encoding == HZSERVER_ENCODING_GB2312)
		setlocale(LC_CTYPE, gb2312locale);
	else if(flag_encoding == HZSERVER_ENCODING_GB18030)
		setlocale(LC_CTYPE, gb18030locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5 )
		setlocale(LC_CTYPE, big5locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5HKSCS )
		setlocale(LC_CTYPE, big5hkscslocale);

	buf = chinput_onspot_mbstocts(convtext);
	text.string.multi_byte = buf;
	text.length = strlen(buf);
	text.feedback = feedback;
	
	IMCallCallback(this_xims, (XPointer)&data);
	XFree(buf);
	ic->len = chinput_util_mb_strlen(s, -1);
}

static void chinput_onspot_status_draw (char *s, int len)
{
	int i;
	IC *ic = (IC *)chinput_find_ic (last_icid);
	IMStatusCBStruct data;
	XIMFeedback feedback[128]={0};
	char tmp[256];
	char tbuf[256];
	XIMText text;
	char *convtext;
	char *buf;

	//if no status area, popup the candidate window
	if (!ic)
		return;
	//if(!(ic->input_style & XIMStatusCallbacks)) return;

	chinput_util_candidate_preprocess(s);

	if ( (HZServer.encoding == HZSERVER_ENCODING_GB2312)
			|| (HZServer.encoding == HZSERVER_ENCODING_GB18030) )
	{
		strcpy (tmp, "");
		if (flag_corner)
			strcat(tmp, "¡¾È«");
		else
			strcat(tmp, "¡¾°ë");
		if (flag_punct)
			strcat(tmp, "£ï¡¿");
		else
			strcat(tmp, " .¡¿");
		strcat (tmp, chinputime[cur_inputmethod].namegb);
	}
	else
	{
		strcpy (tmp, "");
		if (flag_corner)
			strcat(tmp, "¡i¥þ");
		else
			strcat(tmp, "¡i¥b");
		if (flag_punct)
			strcat(tmp, "¡³¡j");
		else
			strcat(tmp, "¡E¡j");
		strcat (tmp, chinputime[cur_inputmethod].namebig5);
	}

	//if popup candidate window, then we need not put selection on 
	//the status window
	if(cmode != HZSERVER_CMODE_AUTO) {
		strcat(tmp, ":");
		strcat(tmp, s);
	}
	len = strlen(tmp);
	//buf = chinput_onspot_mbstocts(tmp);

	convtext =  tmp;

	if ( ( (HZServer.encoding == HZSERVER_ENCODING_GB2312)
					|| (HZServer.encoding == HZSERVER_ENCODING_GB18030) )
				&& ( (flag_encoding == HZSERVER_ENCODING_BIG5)
					|| (flag_encoding == HZSERVER_ENCODING_BIG5HKSCS)
					|| (flag_lock == LOCK_BIG5) ) )
	{
		// Convert to Big5 encoding
		chinput_gbmixstring_to_big5mixstring(convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}
	if ( ( (HZServer.encoding == HZSERVER_ENCODING_BIG5)
					|| (HZServer.encoding == HZSERVER_ENCODING_BIG5HKSCS) )
				&& ( (flag_encoding == HZSERVER_ENCODING_GB2312)
					|| (flag_encoding == HZSERVER_ENCODING_GB18030)
					|| (flag_lock == LOCK_GB) ) )
	{
		// Convert to gb encoding
		chinput_big5mixstring_to_gbmixstring(convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}

	convtext[len] = 0;
	if(flag_encoding == HZSERVER_ENCODING_GB2312)
		setlocale(LC_CTYPE, gb2312locale);
	else if ( flag_encoding == HZSERVER_ENCODING_GB18030 )
		setlocale(LC_CTYPE, gb18030locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5 )
		setlocale(LC_CTYPE, big5locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5HKSCS )
		setlocale(LC_CTYPE, big5hkscslocale);

	buf = chinput_onspot_mbstocts(convtext);
	
	data.todo.draw.type = XIMTextType;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;
	data.major_code = XIM_STATUS_DRAW;
	data.todo.draw.data.text = &text;
	
	for (i=0; i<128; i++)
		feedback[i] = XIMUnderline;
	feedback[len] = 0;
	text.string.multi_byte = buf;
	text.length = strlen(buf);
	text.feedback = feedback;
	
	IMCallCallback(this_xims, (XPointer)&data);
	XFree(buf);
	//If no callbacks, then we have no way to draw the candidate
	//area for Chinese input. Thus, I provide a root window
	//to let the users choose the word/phrases.

	//You can moving the root window anywhere within the screen.
	//kinput2 popup a root window around the lower left corner
	//of the screen, but it is not the best way.
	if(cmode == HZSERVER_CMODE_AUTO)
		chinput_overspot_draw_caret (s, len);
}

void chinput_onspot_preedit_clear (void)
{
	IC *ic = (IC *)chinput_find_ic (last_icid);
	//char *buf;
	IMPreeditCBStruct data;
	XIMText text;
	XIMFeedback feedback[1] = {0};

	if (!ic)
		return;
	if (ic->len == 0)
		return;
	data.major_code = XIM_PREEDIT_DRAW;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;
	data.todo.draw.caret = 0;
	data.todo.draw.chg_first = 0;
	data.todo.draw.chg_length = ic->len;	//clear length
	data.todo.draw.text = &text;

	//replace string to NULL
	text.feedback = feedback;
	text.length = 0;
	text.string.multi_byte = "";
	IMCallCallback(this_xims, (XPointer)&data);
	ic->len = 0;
}

void chinput_onspot_clear (void)
{
	int i;
	IC *ic = (IC *)chinput_find_ic (last_icid);
	IMStatusCBStruct data;
	XIMFeedback feedback[256]={0};
	char tmp[256];
	XIMText text;
	char *convtext;
	char *buf;
	int len;

	//if no status area, popup the candidate window
	if (!ic)
		return;
	if (!(ic->input_style & XIMStatusCallbacks))
		return;

	if ( (HZServer.encoding == HZSERVER_ENCODING_GB2312)
			|| (HZServer.encoding == HZSERVER_ENCODING_GB18030) )

	{
		strcpy(tmp, "");
		if (flag_corner)
			strcat(tmp, "¡¾È«");
		else
			strcat(tmp, "¡¾°ë");
		if (flag_punct)
			strcat(tmp, "£ï¡¿");
		else
			strcat(tmp, " .¡¿");
		strcat(tmp, chinputime[cur_inputmethod].namegb);
	}
	else
	{
		strcpy (tmp, "");
		if (flag_corner)
			strcat(tmp, "¡i¥þ");
		else
			strcat(tmp, "¡i¥b");
		if (flag_punct)
			strcat(tmp, "¡³¡j");
		else
			strcat(tmp, "¡E¡j");
		strcat (tmp, chinputime[cur_inputmethod].namebig5);
	}
	//strcat(tmp, "                                                       ");	
	len = strlen(tmp);
	convtext =  tmp;

	if ( ( (HZServer.encoding == HZSERVER_ENCODING_GB2312)
					|| (HZServer.encoding == HZSERVER_ENCODING_GB18030) )
				&& ( (flag_encoding == HZSERVER_ENCODING_BIG5)
					|| (flag_encoding == HZSERVER_ENCODING_BIG5HKSCS)
					|| (flag_lock == LOCK_BIG5) ) )
	{
		char tbuf[256];
		// Convert to Big5 encoding
		chinput_gbmixstring_to_big5mixstring (convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}
	if ( ( (HZServer.encoding == HZSERVER_ENCODING_BIG5)
					|| (HZServer.encoding == HZSERVER_ENCODING_BIG5HKSCS) )
				&& ( (flag_encoding == HZSERVER_ENCODING_GB2312)
					|| (flag_encoding == HZSERVER_ENCODING_GB18030)
					|| (flag_lock == LOCK_GB) ) )
	{
		char tbuf[256];
		// Convert to gb encoding
		chinput_big5mixstring_to_gbmixstring (convtext, tbuf, len);
		strncpy(convtext, tbuf, len);
	}

	convtext[len] = 0;
	if(flag_encoding == HZSERVER_ENCODING_GB2312)
		setlocale(LC_CTYPE, gb2312locale);
	else if(flag_encoding == HZSERVER_ENCODING_GB18030)
		setlocale(LC_CTYPE, gb18030locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5 )
		setlocale(LC_CTYPE, big5locale);
	else if ( flag_encoding == HZSERVER_ENCODING_BIG5HKSCS )
		setlocale(LC_CTYPE, big5hkscslocale);

	buf = chinput_onspot_mbstocts(convtext);
	
	data.todo.draw.type = XIMTextType;
	data.connect_id = ic->connect_id;
	data.icid = ic->id;
	data.major_code = XIM_STATUS_DRAW;
	data.todo.draw.data.text = &text;

	for (i=0; i<256; i++)
		feedback[i] = XIMUnderline;
	feedback[len] = 0;
	text.string.multi_byte = buf;
	text.length = strlen (buf);
	text.feedback = feedback;

	IMCallCallback (this_xims, (XPointer)&data);
	XFree (buf);
}

void chinput_onspot_flush (void)
{
	char mbuf[256];
	//if(flag_refresh)return;
	bzero (mbuf, 256);
	if (flag_english)
		strcpy (mbuf, input_buf);
	else
		IMM_GetInputDisplay (chinput_imm, mbuf, sizeof (mbuf));

	chinput_onspot_preedit_draw (mbuf, strlen(mbuf));

	//in general, status window only display current status
	//of input method. but if we have no way to display the
	//candidate window(e.g., the use disabled the candidate window)
	//we have to display the candidate list in status window
	IMM_GetSelectDisplay (chinput_imm, mbuf, sizeof (mbuf));
	chinput_onspot_status_draw (mbuf, strlen(mbuf));
}

