// XIMプロトコルのパケットを下位層から受けとり、
// それをディスパッチする。
 
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include "xim.h"

#ifndef __GNUC__
# ifdef HAVE_ALLOCA_H
#  include <alloca.h>
# endif
#endif

//色々な表
input_style input_style_tab[]={
    {XIMPreeditNothing|XIMStatusNothing, IS_ROOT_WINDOW},
    //{XIMPreeditPosition|XIMStatusArea, IS_OVER_THE_SPOT},// emacs
    {XIMPreeditPosition|XIMStatusNothing, IS_OVER_THE_SPOT},
    //{XIMPreeditCallbacks|XIMStatusCallbacks, IS_ON_THE_SPOT},// OOo
    //{XIMPreeditArea|XIMStatusArea, IS_ROOT_WINDOW},
    {XIMPreeditCallbacks|XIMStatusNothing, IS_ON_THE_SPOT},
    {0, 0},
};
// XIMPreeditArea,XIMPreeditCallbacks,XIMPreeditPosition
// XIMPreeditNothing,XIMPreeditNone
// XIMStatusArea,XIMStatusCallbacks
// XIMStatusNothing,XIMStatusNone

static char *xim_packet_name[]={
    // 0
    0,"XIM_CONNECT","XIM_CONNECT_REPLY",
    "XIM_DISCONNECT","XIM_DISCONNECT_REPLY",
    0,0,0,0,0,
    // 10
    "XIM_AUTH_REQUIRED","XIM_AUTH_REPLY","XIM_AUTH_NEXT",
    "XIM_AUTH_SETUP","XIM_AUTH_NG",0,0,0,0,0,
    // 20
    "XIM_ERROR",0,0,0,0,0,0,0,0,0,
    // 30
    "XIM_OPEN","XIM_OPEN_REPLY","XIM_CLOSE",
    "XIM_CLOSE_REPLY","XIM_REGISTER_TRIGGERKEYS",
    "XIM_TRIGGER_NOTIFY","XIM_TRIGGER_NOTIFY_REPLY",
    "XIM_SET_EVENT_MASK","XIM_ENCODING_NEGOTIATION",
    "XIM_ENCODING_NEGOTIATION_REPLY",
    // 40
    "XIM_QUERY_EXTENSION","XIM_QUERY_EXTENSION_REPLY",
    "XIM_SET_IM_VALUES","XIM_SET_IM_VALUES_REPLY",
    "XIM_GET_IM_VALUES","XIM_GET_IM_VALUES_REPLY",
    0,0,0,0,
    // 50
    "XIM_CREATE_IC","XIM_CREATE_IC_REPLY",
    "XIM_DESTROY_IC","XIM_DESTROY_IC_REPLY",
    "XIM_SET_IC_VALUES","XIM_SET_IC_VALUES_REPLY",
    "XIM_GET_IC_VALUES","XIM_GET_IC_VALUES_REPLY",
    "XIM_SET_IC_FOCUS","XIM_UNSET_FOCUS",
    // 60
    "XIM_FORWARD_EVENT","XIM_SYNC","XIM_SYNC_REPLY",
    "XIM_COMMIT","XIM_RESET_IC","XIM_RESET_IC_REPLY",
    0,0,0,0,
    // 70
    "XIM_GEOMETRY","XIM_STR_CONVERSION",
    "XIM_STR_CONVERSION_REPLY","XIM_PREEDIT_START",
    "XIM_PREEDIT_START_REPLY","XIM_PREEDIT_DRAW",
    "XIM_PREEDIT_CARET","XIM_PREEDIT_CARET_REPLY",
    "XIM_PREEDIT_DONE","XIM_STATUS_START",
    // 80
    "XIM_STATUS_DRAW","XIM_STATUS_DONE","XIM_PREEDITSTATE"
};

static struct XIMATTRIBUTE{
    XIMATTRIBUTE(char *n,int t);
    static void write_imattr_to_packet(TxPacket *p);

    char *name;
    int type;
}xim_attributes[]={
    XIMATTRIBUTE(XNQueryInputStyle,TYPE_XIMSTYLE),
};

XIMATTRIBUTE::XIMATTRIBUTE(char *n, int t)
{
    name = n;
    type = t;
}

void XIMATTRIBUTE::write_imattr_to_packet(TxPacket *p)
{
    int i,l;
    char tmp[4];
    for (i = 0; i < 4; i++) {
        tmp[i] = 0;
    }
    for (i = 0, l =0; i < (int)(sizeof(xim_attributes)/sizeof(XIMATTRIBUTE));
	 i++) {
        l += 6;
        l += strlen(xim_attributes[i].name);
        l += pad4(strlen(xim_attributes[i].name)+2);
    }
    p->pushC16(l);
    for (i = 0; i < (int)(sizeof(xim_attributes)/sizeof(XIMATTRIBUTE)); i++){
        p->pushC16((unsigned int)i);
        p->pushC16((unsigned int)xim_attributes[i].type);
        p->pushC16((unsigned int)strlen(xim_attributes[i].name));
        p->pushBytes(xim_attributes[i].name,
                     strlen(xim_attributes[i].name));
        p->pushBytes(tmp, pad4(strlen(xim_attributes[i].name))+2);
    }
}

static struct XICATTRIBUTE{
    XICATTRIBUTE(char *n, int t);
    static void write_icattr_to_packet(TxPacket *p);
    char *name;
    int type;
}xic_attributes[]={
    //この順番とximpn.hで定義している番号は一致していないといけない
    //ktermとgtkでルートウィンドウスタイルのとき
    XICATTRIBUTE(XNInputStyle, TYPE_LONG),
    XICATTRIBUTE(XNClientWindow, TYPE_WINDOW),
    XICATTRIBUTE(XNFocusWindow, TYPE_WINDOW),
    //ktermでover the spotを行うとき
    XICATTRIBUTE(XNPreeditAttributes, TYPE_NESTEDLIST),
    XICATTRIBUTE(XNForeground, TYPE_LONG),
    XICATTRIBUTE(XNBackground, TYPE_LONG),
    XICATTRIBUTE(XNSpotLocation, TYPE_POINT),
    XICATTRIBUTE(XNFontSet, TYPE_XFONTSET),
    XICATTRIBUTE(XNArea, TYPE_XRECTANGLE),
    XICATTRIBUTE(XNLineSpace, TYPE_WORD),
    //
    XICATTRIBUTE(XNStatusAttributes, TYPE_NESTEDLIST),
    XICATTRIBUTE(XNAreaNeeded, TYPE_XRECTANGLE),
    XICATTRIBUTE(XNColormap, TYPE_WORD),
    XICATTRIBUTE(XNStdColormap, TYPE_WORD),
    XICATTRIBUTE(XNBackgroundPixmap, TYPE_LONG),
    XICATTRIBUTE(XNCursor, TYPE_WORD),
    XICATTRIBUTE(XNFilterEvents, TYPE_WORD),
    XICATTRIBUTE(XNSeparatorofNestedList, TYPE_SEPARATOR),
};

XICATTRIBUTE::XICATTRIBUTE(char *n, int t)
{
    name = n;
    type = t;
}

void XICATTRIBUTE::write_icattr_to_packet(TxPacket *p)
{
    int i,l;
    char tmp[4];
    for (i = 0; i < 4; i++) {
        tmp[i] = 0;
    }
    for (i = 0, l = 0; i < (int)(sizeof(xic_attributes)/sizeof(XICATTRIBUTE)) ;
	 i++){
        l += 6;
        l += strlen(xic_attributes[i].name);
        l += pad4(strlen(xic_attributes[i].name)+2);
    }
    p->pushC16(l);
    p->pushC16(0);
    for (i = 0; i < (int)(sizeof(xic_attributes)/sizeof(XICATTRIBUTE)); i++) {
        p->pushC16((unsigned int)i);
        p->pushC16((unsigned int)xic_attributes[i].type);
        p->pushC16((unsigned int)strlen(xic_attributes[i].name));
        p->pushBytes(xic_attributes[i].name,
                     strlen(xic_attributes[i].name));
        p->pushBytes(tmp, pad4(strlen(xic_attributes[i].name)+2));
    }
}

void force_event(Window w)
{
    Window fw;
    int rev;
    XGetInputFocus(gDpy, &fw, &rev);
    XSetInputFocus(gDpy, w, rev, CurrentTime);
    XSetInputFocus(gDpy, fw, rev, CurrentTime);
    XFlush(gDpy);
}

Connection::Connection()
{
    mIsCloseWait = false;
    mByteorder = BYTEORDER_UNKNOWN;
}

Connection::~Connection()
{
    //自分で作ったIMを全てこわす。
    std::list<int>::iterator i;
    for (i = mCreatedIm.begin(); i != mCreatedIm.end(); i++) {
	close_im(*i);
    }
    //
    std::list<RxPacket *>::iterator ir;
    for (ir = mRxQ.begin(); ir != mRxQ.end(); ir ++) {
	delete *ir;
    }
    std::list<TxPacket *>::iterator it;
    for (it = mTxQ.begin(); it != mTxQ.end(); it ++) {
	delete *it;
    }
    for (it = mPTxQ.begin(); it != mPTxQ.end(); it ++) {
	delete *it;
    }
}

void Connection::terminate()
{
    mIsCloseWait = true;
}

void Connection::OnRecv()
{
    std::list<RxPacket *>::iterator i;
    i = mRxQ.begin();
    if (i != mRxQ.end()) {
	int major = (*i)->getMajor();
	//一通りそろったら、プロトコルの番号順に並びかえたい。
	RxPacket *p = *i;
	if (g_option_mask & OPT_TRACE_XIM) {
	    printf("<-: %s(%d,%d).\n", xim_packet_name[major],
		   p->getC16(), p->getC16());
	    p->rewind();
	}
	switch(major){
	case XIM_CONNECT:
	    xim_connect(p);
	    break;
	case XIM_OPEN:
	    xim_open(p);
	    break;
	case XIM_QUERY_EXTENSION:
	    xim_query_extension(p);
	    break;
	case XIM_ENCODING_NEGOTIATION:
	    xim_encoding_negotiation(p);
	    break;
	case XIM_CLOSE:
	    xim_close(p);
	    break;
	case XIM_DISCONNECT:
	    xim_disconnect(p);
	    break;
	case XIM_GET_IM_VALUES:
	    xim_get_im_values(p);
	    break;
	case XIM_SET_IC_VALUES:
	    xim_set_ic_values(p);
	    break;
	case XIM_GET_IC_VALUES:
	    xim_get_ic_values(p);
	    break;
	case XIM_CREATE_IC:
	    xim_create_ic(p);
	    break;
	case XIM_DESTROY_IC:
	    xim_destroy_ic(p);
	    break;
	case XIM_SET_IC_FOCUS:
	    xim_set_ic_focus(p);
	    break;
	case XIM_UNSET_IC_FOCUS:
	    xim_unset_ic_focus(p);
	    break;
	case XIM_TRIGGER_NOTIFY:
	    //xim_trigger_notify(*i);トリガーなんかは使わん。
	    break;
	case XIM_FORWARD_EVENT:
	    xim_forward_event(p);
	    break;
	case XIM_SYNC_REPLY:
	    //xim_sync_reply(p);
	    break;
	case XIM_RESET_IC:
	    xim_reset_ic(p);
	    break;
	case XIM_PREEDIT_START_REPLY:
	    // do nothing
	    break;
	case XIM_PREEDIT_CARET_REPLY:
	    // do nothing
	    break;
	case XIM_ERROR:
	    xim_error(p);
	    break;
	default:
	    printf("Unknown(or not implemented.) packet from xim connection.\n");
	    (*i)->dump();
	    break;
	}
	mRxQ.pop_front();
	delete p;
    }
}

void Connection::OnSend()
{
    std::list<int>::iterator i;
    for (i = mCreatedIm.begin(); i != mCreatedIm.end(); i++) {
        XimIM *im;
        im = get_im_by_id(*i);
	if (im) {
	    im->onSendPacket();
	}
    }
}

void Connection::OnClose()
{
    std::list<int>::iterator i;
    for (i = mCreatedIm.begin(); i != mCreatedIm.end(); i++) {
	close_im(*i);
    }
    mCreatedIm.erase(mCreatedIm.begin(), mCreatedIm.end());
}

void Connection::push_packet(TxPacket *p)
{
    OnPushPacket();
    mTxQ.push_back(p);
    if (g_option_mask & OPT_TRACE_XIM) {
	printf("->: %s.\n", xim_packet_name[p->get_major()]);
    }
}

void Connection::push_passive_packet(TxPacket *p)
{
    mPTxQ.push_back(p);
    if (g_option_mask & OPT_TRACE_XIM) {
	printf("(->): %s.\n", xim_packet_name[p->get_major()]);
    }
}

void Connection::push_error_packet(int imid, int icid, int er, char *str)
{
    TxPacket *t;
    t = createTxPacket(XIM_ERROR,0);
    t->pushC16(imid);
    t->pushC16(icid);
    int m = 0;
    if (imid) {
	m+=1;
    }
    if (icid) {
	m+=2;
    }
    t->pushC16(m);
    t->pushC16(er);
    int l=strlen(str);
    char tmp[4];
    t->pushC16(l);
    t->pushC16(0);
    t->pushBytes(str, l);
    t->pushBytes(tmp, pad4(l));
    push_packet(t);
}

unsigned short Connection::to_hs(unsigned short s)
{
    if (host_byte_order == mByteorder) {
	return s;
    }
    unsigned char *v;
    v = (unsigned char *)&s;
    s = (v[0]<<8) + v[1];
    return s;
}

unsigned int Connection::to_hl(unsigned int l)
{
    if (host_byte_order == mByteorder) {
	return l;
    }
    return l;
}

//
// Packet handlers
//
void Connection::xim_connect(RxPacket *p)
{
    TxPacket *t;
    
    p->rewind();
    p->getC8();
    p->getC8(); //discard
    if (p->isOverRun()) {
	push_error_packet(0, 0, 0, "Invalid Packet");
	terminate();
	return ;
    }

    t = createTxPacket(XIM_CONNECT_REPLY, 0);
    t->pushC16(1);
    t->pushC16(0);
    push_packet(t);
    if (g_option_mask & OPT_TRACE) {
	printf("accept xim connection.\n");
    }
}

void Connection::xim_disconnect(RxPacket *p)
{
    TxPacket *t;
    t = createTxPacket(XIM_DISCONNECT_REPLY,0);
    push_packet(t);

    //
    terminate();
    if (g_option_mask & OPT_TRACE) {
        printf("disconnect xim connection.\n");
    }
}

void Connection::xim_open(RxPacket *p)
{
    char buf[16];
    int l;
    for (l = 0; l < 16; l++) {
        buf[l] = 0;
    }
    l = p->getStr8Len();
    if (l > 15) {
        printf("too long locale name.\n");
        return ;
    }
    p->getStr8(buf);

    TxPacket *t;
    int imid;
    imid = unused_im_id();
    mCreatedIm.push_back(imid);//作ったやつが責任もって破棄する。
    create_im(this, imid);
    t = createTxPacket(XIM_OPEN_REPLY, 0);
    t->pushC16(imid);
    XIMATTRIBUTE::write_imattr_to_packet(t);
    XICATTRIBUTE::write_icattr_to_packet(t);
  
    push_packet(t);

    // イベントマスクの選択

    t = createTxPacket(XIM_SET_EVENT_MASK, 0);
    t->pushC16(imid);
    t->pushC16(0);
    t->pushC32(KeyPressMask);
    t->pushC32(KeyPressMask);
    push_packet(t);
}

void Connection::xim_close(RxPacket *p)
{
    int imid;
    imid = p->getC16();
    TxPacket *t;
    t = createTxPacket(XIM_CLOSE_REPLY,0);
    t->pushC16(imid);
    t->pushC16(0);
    push_packet(t);
    close_im(imid);
    std::list<int>::iterator i;
    for (i = mCreatedIm.begin(); i != mCreatedIm.end(); i++) {
	if (*i == imid) {
	    mCreatedIm.erase(i);
	    return;
	}
    }
}

void Connection::xim_query_extension(RxPacket *p)
{
    int imid;
    imid = p->getC16();

    TxPacket *t;
    t = createTxPacket(XIM_QUERY_EXTENSION_REPLY, 0);
    t->pushC16(imid);
    t->pushC16(0);
  
    push_packet(t);
}

void Connection::xim_encoding_negotiation(RxPacket *p)
{
    TxPacket *t;
    t = createTxPacket(XIM_ENCODING_NEGOTIATION_REPLY,0);
    C16 l, m, s;
    int i, idx, index;
    int imid;
    char buf[32];

    imid = p->getC16(); // m_imid
    l = p->getC16();
  
    index = 0;
    for (m = 0, s = 0, idx = 0; m < l; m += (s+1), idx ++) {
        s = p->getStr8Len ();

        for (i= 0; i < 32; i++) {
            buf[i] = 0;
        }
        p->getStr8 (buf);

        /* COMPOUND_TEXT (eucJP is Buggy) */
        if (!strcmp ("COMPOUND_TEXT", buf)) {
            index = idx;
        }
    }

    t->pushC16(imid);
    t->pushC16(0);
    t->pushC16(index);
    t->pushC16(0);
    push_packet(t);
}

void Connection::xim_get_im_values(RxPacket *p)
{
    int l,i;
    int imid;
    TxPacket *t;
    t = createTxPacket(XIM_GET_IM_VALUES_REPLY, 0);
    imid = p->getC16(); // input-method id
    l = p->getC16() / 2; //個数

    int *ra = (int *)alloca(sizeof(int)*l);
    int rlen = 0;
    for (i = 0; i < l; i++) {
        ra[i] = p->getC16();
        rlen += 4;
    }

    // どうせIMAttributeは1種類しかないんだし、、
    t->pushC16(imid);
    t->pushC16(16); // length

    // XIMATTRIBUTE
    int nr_style;
    for (nr_style = 0; input_style_tab[nr_style].style; nr_style ++);

    t->pushC16(0); // attribute id
    t->pushC16(nr_style*4); //length

    t->pushC16(nr_style); // 個数
    t->pushC16(0);
    for (i = 0; i < nr_style; i++) {
	t->pushC32(input_style_tab[i].x_style);
    }

    push_packet(t);
}

void Connection::xim_set_ic_values(RxPacket *p)
{
    int imid, icid;
    imid = p->getC16();
    icid = p->getC16();
    p->rewind();

    XimIM *im = get_im_by_id(imid);
    if (im) {
	im->set_ic_values(p, icid);
    }

    TxPacket *t=createTxPacket(XIM_SET_IC_VALUES_REPLY,0);
    t->pushC16(imid);
    t->pushC16(icid);
    push_packet(t);
}

void Connection::xim_get_ic_values(RxPacket *p)
{
    int imid;
    imid = p->getC16();
    p->rewind();
    XimIM *im;
    im = get_im_by_id(imid);
    if (im) {
	im->get_ic_values(p);
    }
}

void Connection::xim_create_ic(RxPacket *p)
{
    XimIM *im;
    int imid;
    imid = p->getC16();
    p->rewind();
    im = get_im_by_id(imid);
    if (im) {
	im->create_ic(p);
    }
}

void Connection::xim_destroy_ic(RxPacket *p)
{
    int imid;
    int icid;
    imid = p->getC16();
    icid = p->getC16();    
    XimIM *im = get_im_by_id(imid);
    if (im) {
	im->destroy_ic(icid);
    }
    TxPacket *t = createTxPacket(XIM_DESTROY_IC_REPLY,0);
    t->pushC16(imid);
    t->pushC16(icid);
    push_packet(t);
}

void Connection::xim_set_ic_focus(RxPacket *p)
{
    int imid;
    XimIM *im;
    imid = p->getC16();
    im = get_im_by_id(imid);
    if (im) {
	im->set_ic_focus(p->getC16());
    }
}

void Connection::xim_unset_ic_focus(RxPacket *p)
{
    int imid;
    XimIM *im;
    imid = p->getC16();
    im = get_im_by_id(imid);
    if (im) {
	im->unset_ic_focus(p->getC16());
    }
}

void Connection::xim_forward_event(RxPacket *p)
{
    int imid,icid;
    
    imid = p->getC16();
    icid = p->getC16();
    p->rewind();
    XimIM *im = get_im_by_id(imid);
    if (im) {
	im->forward_event(p);
    }
    TxPacket *t = createTxPacket(XIM_SYNC_REPLY,0);
    t->pushC16(imid);
    t->pushC16(icid);
    push_packet(t);
}

void Connection::xim_reset_ic(RxPacket *p)
{
    XimIC *ic = get_ic(p);
    if (ic) {
	ic->reset_ic();
    }
}

void Connection::xim_error(RxPacket *p)
{
    int imid,icid,mask,ecode,len;
    char *buf;

    imid = p->getC16();
    icid = p->getC16();
    mask = p->getC16();
    ecode = p->getC16();
    len = p->getStrLen();
    buf = (char *)alloca(len+1);
    buf[len] = 0;
    p->getStr(buf);
  
    printf("XIM_ERROR received.\n");
    if (mask & 1) {
	printf(" imid = %d\n", imid);
    }
    if (mask & 2) {
	printf(" icid = %d\n", icid);
    }
    printf(" error_code = %d\n", ecode);
    printf(" message=(%s)\n", buf);
}

XimIC *Connection::get_ic(RxPacket *p)
{
    int imid,icid;
    XimIM *im;
    imid = p->getC16();
    icid = p->getC16();
    p->rewind();
    im = get_im_by_id(imid);
    if (im) {
	return im->get_ic_by_id(icid);
    }
    return 0;
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */
