#ifdef KCONV

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Xmu.h>
#include <X11/Xproto.h>
#include <stdio.h>
#include <setjmp.h>
#include <ctype.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

extern void	insert_text();

extern int	_beginConversionWithAttributes();
extern void     _endConversion();
extern void	_changeConversionAttributes();

int	convCTtoCS();
int	convCStoEUC();
int	convCStoSJIS();
int	convCStoJIS();

static Atom	japanese_conversion_atom;	/* JAPANESE_CONVERSION */
static Atom	compound_text_atom;		/* COMPOUND_TEXT */
static int	atominitialized;
static int	(*originalerrorhandler)();

static Atom	converting = None;
static Window	forwardwin = None;

static XEvent	*event_queue;
static int	queue_event;
static int	queue_end;

static struct kcres {
    Boolean forwardevent;
    Boolean keypressonly;
    int queuesize;
} kcr;

static XtResource resources[] = {
    { "forwardKeyEvent", "ForwardKeyEvent", XtRBoolean, sizeof(Boolean),
      XtOffsetOf(struct kcres, forwardevent), XtRImmediate, (XtPointer)True },
    { "forwardKeyPressOnly", "ForwardKeyPressOnly", XtRBoolean, sizeof(Boolean),
      XtOffsetOf(struct kcres, keypressonly), XtRImmediate, (XtPointer)True },
    { "forwardQueueSize", "ForwardQueueSize", XtRInt, sizeof(int),
      XtOffsetOf(struct kcres, queuesize), XtRImmediate, (XtPointer)32 },
};


/* gset field */
#define CS96		0x80
#define MBCS		0x40
#define MBC2		0x7f /* second byte of a mbcs character set */
#define GSET(c)		((c) - '0')
#define TESG(i)		(((i) & ~(MBCS|CS96)) + '0')
/* code field */
#define NEEDMAP		0x80
typedef struct {
	unsigned char gset;
	unsigned char code;
} Ichr; /* char for interchanging */

#define GSET_GRAPH	GSET('0')
#define GSET_UK		GSET('A')
#define GSET_ASCII	GSET('B')
#define GSET_JISROMAN	GSET('J')
#define GSET_KANA	GSET('I')
#define GSET_LATIN1R	(CS96|GSET('A'))
#define GSET_KANJI	(MBCS|GSET('B'))
#define GSET_OLDKANJI	(MBCS|GSET('@'))

#define SS2	0x8E
#define CSI	0x9B

/* convCStoCT -- Wide Character String -> COMPOUND_TEXT */
int
convCStoCT(cs, as)
register Ichr *cs;
register unsigned char *as;
/* Convert Wide Character String cs to COMPOUND_TEXT xstr, return
 * length of xstr in bytes (not including the terminating null character).
 * If xstr is NULL, no conversion is done, but return length of xstr.
 */
{
	register int	c1, c2;
	register int	g0 = GSET_ASCII;
	register int	g1 = GSET_LATIN1R;
	register int	n = 0;

	while (c1 = cs->code) {
	    if (cs->gset & CS96
	     || cs->gset & MBCS
	     || cs->gset == GSET_KANA) {
		if (g1 != cs->gset) {
			g1 = cs->gset;
			if (as) {
				*as++ = '\033';
				if (g1 & MBCS) {
					*as++ = '$';
				}
				if (g1 & CS96) {
					*as++ = '-';
				} else {
					*as++ = ')';
				}
				*as++ = TESG(g1);
			}
			n += 3;
			if (g1 & MBCS)
				n ++;
		}
		cs++;
		if (g1 & MBCS) {
			c2 = cs++->code;
			if (as) {
				*as++ = c1 | 0x80;
				*as++ = c2 | 0x80;
			}
			n += 2;
		} else {
			if (as)
				*as++ = c1 | 0x80;
			n++;
		}
	    } else {
		if (g0 != cs->gset) {
			g0 = cs->gset;
			if (as) {
				*as++ = '\033';
				*as++ = '(';
				*as++ = TESG(g0);
			}
			n += 3;
		}
		cs++;
		if (as)
			*as++ = c1 & ~0x80;
		n++;
	    }
	}
	if (g0 != GSET_ASCII) {
		if (as) {
			*as++ = '\033';
			*as++ = '(';
			*as++ = 'B';
		}
		n += 3;
	}
	if (as)
		*as = '\0';

	return n;
}

/* CS -> SJIS */
static int
CStoSJIS(cs_p, ss_p)
Ichr		**cs_p;
unsigned char	**ss_p;
{
	int		c1, c2;
	Ichr		*cs = *cs_p;
	unsigned char	*ss = *ss_p;
	if (cs->gset == GSET_KANA) {
		c1 = cs++->code;
		if (ss) {
			*ss++ = c1 | 0x80;
		}
		*ss_p = ss;
		*cs_p = cs;
		return 1;
	} else if (cs->gset == GSET_KANJI || cs->gset == GSET_OLDKANJI) {
		c1 = cs++->code;
		c2 = cs++->code;
		if (ss) {
			*ss++ = (c1 - 0x21) / 2 + ((c1 <= 0x5e) ? 0x81 : 0xc1);
			if (c1 & 1)	/* odd */
			    *ss++ = c2 + ((c2 <= 0x5f) ? 0x1f : 0x20);
			else
			    *ss++ = c2 + 0x7e;
		}
		*ss_p = ss;
		*cs_p = cs;
		return 2;
	}
	return 0;
}

int
convCStoSJIS(cs, ss)
Ichr		*cs;
unsigned char	*ss;
{
	return convCStoANY(cs, ss, CStoSJIS);
}

/* CS -> EUC */
static int
CStoEUC(cs_p, es_p)
Ichr		**cs_p;
unsigned char	**es_p;
{
	int		c1, c2;
	Ichr		*cs = *cs_p;
	unsigned char	*es = *es_p;
	if (cs->gset == GSET_KANA) {
		c1 = cs++->code;
		if (es) {
			*es++ = SS2;
			*es++ = c1 | 0x80;
		}
		*es_p = es;
		*cs_p = cs;
		return 2;
	} else if (cs->gset == GSET_KANJI
		|| cs->gset == GSET_OLDKANJI) {
		c1 = cs++->code;
		c2 = cs++->code;
		if (es) {
			*es++ = c1 | 0x80;
			*es++ = c2 | 0x80;
		}
		*es_p = es;
		*cs_p = cs;
		return 2;
	}
	return 0;
}

int
convCStoEUC(cs, es)
Ichr		*cs;
unsigned char	*es;
{
	return convCStoANY(cs, es, CStoEUC);
}

/* CS -> JIS using ESC-$-B */
int
convCStoJIS(cs, js)
Ichr		*cs;
unsigned char	*js;
{
	return convCStoANY(cs, js, NULL);
}

/* CS -> any */
int
convCStoANY(cs, as, func)
Ichr		*cs;
unsigned char	*as;
int		(*func)();
{
	register int	c1, c2;
	register int	gset = GSET_ASCII;
	register int	n = 0, m;

	while (c1 = cs->code) {
		if (func && (m = (*func)(&cs, &as))) {
			n += m;
			continue;
		}
		if (gset != cs->gset) {
			if (gset & CS96 && !(cs->gset & CS96)) {
				if (as)
					*as++ = '\025'; /* SI */
				n++;
			} else if (!(gset & CS96) && cs->gset & CS96) {
				if (as)
					*as++ = '\024'; /* SO */
				n++;
			}
			/*
			 *	A HACK FOR JISROMAN
			 */
			if (cs->gset == GSET_ASCII
			 || cs->gset == GSET_JISROMAN) {
				if (gset != GSET_ASCII
				 && gset != GSET_JISROMAN) {
					if (as) {
						*as++ = '\033';
						*as++ = '(';
						*as++ = TESG(cs->gset);
					}
					n += 3;
				}
			} else if (cs->gset == GSET_KANJI
			/*
			 * if (cs->gset == GSET_KANJI
			 */
			 || cs->gset == GSET_OLDKANJI) {
				if (as) {
					*as++ = '\033';
					*as++ = '$';
					*as++ = TESG(cs->gset);
				}
				n += 3;
			} else {
				if (as) {
					*as++ = '\033';
					if (cs->gset & MBCS) {
						*as++ = '$';
					}
					if (cs->gset & CS96) {
						*as++ = '-';
					} else {
						*as++ = '(';
					}
					*as++ = TESG(cs->gset);
				}
				n += 3;
				if (cs->gset & MBCS)
					n ++;
			}
			gset = cs->gset;
		}
		cs++;
		if (gset & MBCS) {
			c2 = cs++->code;
			if (as) {
				*as++ = c1 & ~0x80;
				*as++ = c2 & ~0x80;
			}
			n += 2;
		} else {
			if (as)
				*as++ = c1 & ~0x80;
			n++;
		}
	}
	/*
	 *	A HACK FOR JISROMAN
	 * if (gset != GSET_ASCII) {
	 */
	if (gset != GSET_ASCII && gset != GSET_JISROMAN) {
		if (as) {
			*as++ = '\033';
			*as++ = '(';
			*as++ = 'B';
		}
		n += 3;
	}
	if (as)
		*as = '\0';

	return n;
}

static unsigned char *
getesc(str, len)
unsigned char *str;
int len;
{
	register int	c;

	/* Find intermediate characters and final character
	 * following the escape character in an escape sequence.
	 */
	/* The intermediate character is 02/00 to 02/15 */
	while (len > 0) {
		c = *str;
		if (c < 0x20 || 0x2f < c)
			break;
		len--, str++;
	}
	/* The final character is 03/00 to 07/14 */
	if (--len < 0 || (c = *str++) < 0x30 || 0x7e < c)
		return (unsigned char *)NULL;

	return str;
}

static unsigned char *
getcsi(str, len)
unsigned char *str;
int len;
{
	register int	c;

	/* Find parameter characters, intermediate characters
	 * and final character following the CSI character
	 * in a CSI sequence.
	 */
	/* The parameter characters is 03/00 to 03/15 */
	while (len > 0) {
		c = *str;
		if (c < 0x30 || 0x3f < c)
			break;
		len--, str++;
	}
	/* The intermediate character is 02/00 to 02/15 */
	while (len > 0) {
		c = *str;
		if (c < 0x20 || 0x2f < c)
			break;
		len--, str++;
	}
	/* The final character is 04/00 to 07/14 */
	if (--len < 0 || (c = *str++) < 0x40 || 0x7e < c)
		return (unsigned char *)NULL;

	return str;
}

/* convCTtoCS -- COMPOUND_TEXT -> Wide Character String */
int
convCTtoCS(xstr, len, cs)
register unsigned char *xstr;
int len;
Ichr *cs;
/* Convert COMPOUND_TEXT xstr to Wide Character String cs, return
 * length of cs in characters (not including the terminating null character).
 * If cs is NULL, no conversion is done, but return length of cs.
 */
{
	register int	c;
	int	nskip;
	int	n = 0;
	int	g0, g1, gs;
	unsigned char	*xstr1;

	/*
	 * Compound Text can include null octet. Therefore the length
	 * of xstr is able to be specified by parameter len.
	 * But if len is zero or negative, get length by strlen() assuming
	 * that no null octet exists.
	 */
	if (len <= 0) {
		len = strlen((char *)xstr);
	}

	/* In initial state, ISO 8859/1 is designated into G0/G1 */
	g0 = GSET_ASCII;	/* ASCII -> G0 */
	g1 = GSET_LATIN1R;	/* Latin/1 right hand part -> G1 */

	while (len-- > 0) {
		switch (c = *xstr++) {
		case '\n':	/* NEWLINE */
		case '\t':	/* TAB */
			if (cs) {
				cs->code = c;
				cs->gset = g0;
				cs++;
			}
			n++;
			break;
		case 0x9b:	/* CSI */
			/*
			 * CSI sequence is generally in following form:
			 *	CSI {P} {I} F
			 *        P : 03/00 to 03/15
			 *        I : 02/00 to 02/15
			 *        F : 04/00 to 07/14
			 */
			/*
			 * Currently only directionality is definde
			 * as following:
			 *	CSI-1-]		begin left-to-right text
			 *	CSI-2-]		begin right-to-left text
			 *	CSI-]		end of string
			 * But this implementation ignores them.
			 */
			xstr1 = getcsi(xstr, len);
			if (xstr1 == NULL)
				return -1;
			len -= xstr1 - xstr;
			xstr = xstr1;
			break;
		case '\033':	/* ESC */
			/*
			 * ESC sequence is generally in following form:
			 *	ESC {I} F
			 *        I : 02/00 to 02/15
			 *        F : 03/00 to 07/14
			 */
			/*
			 * Currently, following functions are defined:
			 *   Standard character set
			 *	ESC-(-F
			 *	ESC-$-(-F
			 *	ESC-)-F
			 *	ESC---F
			 *	ESC-$-)-F
			 *   Non standard character set
			 *	ESC-%-/-[0123]
			 * Standard character set must be accepted correctly.
			 * Non standard one is ignored but must be parsed
			 * for skipping data.
			 */
			xstr1 = getesc(xstr, len);
			if (xstr1 == NULL)
				return -1;
			len -= xstr1 - xstr;
			switch (xstr1 - xstr) {
			case 2:		/* ESC - I - F */
				switch (*xstr++) {
				case '(':	/* 94chars CS -> G0 */
					g0 = GSET(*xstr);
					break;
				case ')':	/* 94chars CS -> G1 */
					g1 = GSET(*xstr);
					break;
				case '-':	/* 96chars CS -> G1 */
					g1 = GSET(*xstr) | CS96;
					break;
				default:	/* ignore */
					break;
				}
				break;
			case 3:		/* ESC - I - I - F */
				switch (*xstr++) {
				case '$':
					switch (*xstr++) {
					case '(':	/* 94chars MBCS -> G0 */
						g0 = GSET(*xstr) | MBCS;
						break;
					case ')':	/* 94chars MBCS -> G1 */
						g1 = GSET(*xstr) | MBCS;
						break;
					case '-':	/* 96chars MBCS -> G1 */
						g1 = GSET(*xstr) | CS96 | MBCS;
						break;
					default:	/* ignore */
						break;
					}
					break;
				case '%':
					if (*xstr++ != '/') {
						/* unknown sequence */
						break;
					}
					/*
					 * Private encoding is ignored.
					 * But following data must be skipped.
					 *	ESC-%-/-F-M-L
					 */
					len -= 2;
					if (len < 0)
						return -1;
					nskip = (*xstr1 & 0x7f) * 128 +
					    (*(xstr1 + 1) & 0x7f);
					if ((len -= nskip) < 0)
						return -1;
					xstr1 += nskip + 2;
					break;
				default:
					break;
				}
				break;
			default:
				break;
			}
			xstr = xstr1;
			break;
		default:
			if (!(c & 0x60)) {
				/*
				 * Non NL/TAB/ESC/CSI character in C0 or C1
				 * is an obvious error.
				 */
				return -1;
			}
			gs = (c & 0x80) ? g1 : g0;
			if (cs) {
				cs->code = c & ~0x80;
				cs->gset = gs;
				cs++;
			}
			n++;
			break;
		}
	}
	if (cs) {
		cs->code = 0;
		cs->gset = 0;
	}
	return n;
}


static void
flushEventQueue(dpy, win)
Display *dpy;
Window win;
{
    int i;

    for (i = 0; i < queue_end; i++) {
	XEvent *ev = &event_queue[i];
	ev->xkey.window = forwardwin;
	XSendEvent(dpy, win, False, 0L, ev);
    }
    queue_end = 0;
}

static void
clearEventQueue()
{
    queue_end = 0;
}

/* ARGSUSED */
static void
forwardKeyEvent(w, cldata, ev, continuep)
Widget w;
XtPointer cldata;
XEvent *ev;
Boolean *continuep;
{
    if (ev->xany.send_event) return;

    if (forwardwin != None) {
	/* send the event to IMS */
	*continuep = False;
	if (ev->type == KeyPress || !kcr.keypressonly) {
	    ev->xkey.window = forwardwin;
	    XSendEvent(XtDisplay(w), forwardwin, False, 0L, ev);
	}
    } else if (queue_event) {
	*continuep = False;
	if (ev->type == KeyPress || !kcr.keypressonly) {
	    event_queue[queue_end++] = *ev;
	    if (queue_end >= kcr.queuesize) queue_event = 0;
	}
    }
}

static int
forwardErrorHandler(dpy, error)
Display *dpy;
XErrorEvent *error;
{
    if (error->request_code == X_SendEvent &&
	error->error_code == BadWindow) {
	/* IMS dead */
	forwardwin = None;
	queue_event = 0;
    } else {
	(*originalerrorhandler)(dpy, error);
    }
}

static void
initializeForwarding(w)
Widget w;
{
    XtGetApplicationResources(w, (XtPointer)&kcr, resources,
			      XtNumber(resources), (ArgList)NULL, 0);
    if (!kcr.forwardevent) return;

    XtInsertEventHandler(w, KeyPressMask|KeyReleaseMask, False,
			 forwardKeyEvent, NULL, XtListHead);
    originalerrorhandler = XSetErrorHandler(forwardErrorHandler);

    if (kcr.queuesize < 0) kcr.queuesize = 0;
    if (kcr.queuesize > 0) {
	event_queue = (XEvent *)XtMalloc(kcr.queuesize * sizeof(XEvent));
    }
}

int csx=0;
int csy=0;
int kanjimode=0;
int isinput=0;

#define	CONV_START_X	csx+72
#define	CONV_START_Y	csy+15

void EndConversionAttributes(w)
Widget	w;
{
	Atom		catom;

	catom = japanese_conversion_atom;
	_endConversion(w,catom,True);
}

void
ClearConversionAttributes(w)
Widget	w;
{
	Atom		catom;
	Arg		arg[10];
	Cardinal	i;

	i=0;

	XtSetArg(arg[i], "spotX", CONV_START_X); i++;
	XtSetArg(arg[i], "spotY", CONV_START_Y); i++;

	catom = japanese_conversion_atom;
	_changeConversionAttributes(w,catom,arg,i);
}

/* ARGSUSED */
static void
inputString(w, selection, type, format, size, str, client_data)
Widget		w;
Atom		selection;
Atom		type;
int		format;
unsigned long	size;
unsigned char	*str;
caddr_t		client_data;
{
	unsigned char	lbuf[256 + 1];
	unsigned char	*lstr, *p, *lp;
	int		n;
	int		(*convfunc)();

	if (str == NULL)
		return;

	if (type == compound_text_atom && format == 8) {
		Ichr		cbuf[256 + 1];
		Ichr		*cs;
		n = convCTtoCS(str, size, NULL);
		cs = (n > 256) ? (Ichr *)XtMalloc((n+1) * sizeof(Ichr))
			       : cbuf;
		(void)convCTtoCS(str, size, cs);
		convfunc = convCStoSJIS;

		n = (*convfunc)(cs, NULL);
		lstr = (n > 256) ? (unsigned char *)XtMalloc(n + 1) : lbuf;
		(void)(*convfunc)(cs, lstr);
		if (cs != cbuf) XtFree((char *)cs);
	} else {
		/* ignore unknown type */
		XtFree((char *)str);
		return;
	}
	XtFree((char *)str);

	insert_text(w,(char *)str);

	if (lstr != lbuf) XtFree((char *)lstr);
}

int	ki2=0;

/* ARGSUSED */
static void
startendProc(w, selection, state, client_data, forward)
Widget w;
Atom selection;
int state;
caddr_t client_data;
Window forward;
{
    if (state == 0) {	/* start */
	ki2 = 1;
	converting = selection;
	forwardwin = forward;
	if (queue_event) flushEventQueue(XtDisplay(w), forwardwin);
    } else {		/* end or fail */
	ki2 = 1;
	converting = None;
	forwardwin = None;
    }
    queue_event = 0;
    clearEventQueue();
}

/* ARGSUSED */
void
BeginConversion(w, event, params, nparams)
Widget	w;
XEvent	*event;
String	*params;
Cardinal	*nparams;
{
	Atom		catom;
	Arg		arg[10];
	Cardinal	i;
	Position	x, y;
	int		spacing;
	XRectangle	area;
	
	if(!isinput) return;

	if (!atominitialized) {
		japanese_conversion_atom =
		    XInternAtom(XtDisplay(w), "_JAPANESE_CONVERSION", False);
		compound_text_atom =
		    XInternAtom(XtDisplay(w), "COMPOUND_TEXT", False);
		atominitialized = 1;
		initializeForwarding(w);
	}

	if (*nparams != 1) {
		catom = japanese_conversion_atom;
	} else {
		XmuInternStrings(XtDisplay(w), params, 1, &catom);
	}

	i = 0;

	XtSetArg(arg[i], "inputStyle", "over"); i++;
	XtSetArg(arg[i], "spotX", CONV_START_X); i++;
	XtSetArg(arg[i], "spotY", CONV_START_Y); i++;

	if (kcr.forwardevent) {
	    queue_event = (kcr.queuesize > 0);
	    clearEventQueue();
	    XtSetArg(arg[i], "eventCaptureMethod", "none"); i++;
	}

	if (_beginConversionWithAttributes(w, catom, compound_text_atom,
					   inputString, startendProc,
					   (XtPointer)NULL, arg, i) < 0) {
	    queue_event = 0;
	    kanjimode = 1;
	}
}
#endif
