#include "inputmethod.h"
#include "ximethod.h"
#include "kanjiconvert.h"
#include "config.h"
#include <qapplication.h>
#include <qdatastream.h>
#include <qevent.h>
#include <qmessagebox.h>
#include <qkeysequence.h>
#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>

enum {
  XKeyPress   = KeyPress,
  XKeyRelease = KeyRelease
};

#undef KeyPress
#undef KeyRelease

void xEvent2XEvent(Display* dis, const void* src, XEvent& e);


InputMethod::InputMethod()
{
  _curwin = 0;
  _curimid = 0;
  _curicid = 0;
  _xim_servers = 0; 
  _server_atom = 0;
  _xim_xconnect = 0; 
  _xim_protocol = 0; 
  _xim_moredata = 0; 
  _locales = 0;
  _transport = 0;
  _compound_text = 0;
  _kimera_atom = 0;
  //_stat = NONE_YOMI;
  //_byteorder = QDataStream::LittleEndian;      // temporary  

  _xim = new XIMethod();

  _buffer = new QBuffer();
  _buffer->open( IO_WriteOnly | IO_Append );

  // Create instance KanjiConvert class
  _kanjiconvt = new KanjiConvert();

  connect(_kanjiconvt, SIGNAL(decidePhases(const QString&)),
	  this,  SLOT(slotDecidePhases(const QString&)));

  connect(this, SIGNAL(setInputMode(const InputMode&)), 
	  _kanjiconvt,  SLOT(setInputMode(const InputMode&)));
}


InputMethod::~InputMethod()
{
  if ( _xim ) delete _xim;
  delete _kanjiconvt;
  _buffer->close();
  delete _buffer;
}


void 
InputMethod::init()
{
  _kanjiconvt->init();

  Q_CHECK_PTR(qt_xdisplay());
  qDebug("IMS Window ID: %d", winId());

  // Create Atom
  _xim_servers = XInternAtom(qt_xdisplay(), "XIM_SERVERS", False);
  qDebug("XIM_SERVERS: %d", _xim_servers);
  _server_atom = XInternAtom(qt_xdisplay(), "@server=kimera", False);
  qDebug("@server=kimera: %d", _server_atom);
  _xim_xconnect = XInternAtom(qt_xdisplay(), "_XIM_XCONNECT", False);
  qDebug("_XIM_XCONNECT: %d", _xim_xconnect);
  _xim_protocol = XInternAtom(qt_xdisplay(), "_XIM_PROTOCOL", False);
  qDebug("_XIM_PROTOCOL: %d", _xim_protocol);
  _xim_moredata = XInternAtom(qt_xdisplay(), "_XIM_MOREDATA", False);
  qDebug("_XIM_MOREDATA: %d", _xim_moredata);
  _locales = XInternAtom(qt_xdisplay(), "LOCALES", False);
  qDebug("LOCALES: %d", _locales);
  _transport = XInternAtom(qt_xdisplay(), "TRANSPORT", False);
  qDebug("TRANSPORT: %d", _transport);
  _compound_text = XInternAtom(qt_xdisplay(), "COMPOUND_TEXT", False);
  qDebug("COMPOUND_TEXT: %d", _compound_text);
  _kimera_atom = XInternAtom(qt_xdisplay(), "KIMERA_ATOM", False);
  qDebug("KIMERA_ATOM: %d", _kimera_atom);
 
  Atom type;
  int format;
  ulong nitems;
  ulong bytes_after;
  uchar* value = 0;

  // Set Selection XIM_SERVER Atom (@im=kimera)  
  XSetSelectionOwner(qt_xdisplay(), _server_atom, winId(), 0); 
  Q_ASSERT(XGetSelectionOwner(qt_xdisplay(), _server_atom) == winId());

  // Get the Atom contained in XIM_SERVERS Property
  int res = XGetWindowProperty(qt_xdisplay(), qt_xrootwin(), _xim_servers,
			       0, 1024, False, XA_ATOM, &type, 
			       &format, &nitems, &bytes_after, &value); 
  Q_ASSERT(res == Success);
  Q_ASSERT(bytes_after == 0);
  
  // Compare the gotten Atom and XIM_SERVER Atom 
  if (res != Success || type != XA_ATOM || format != 32 || !value ) {
    // Register XIM_SERVER Atom 
    res = XChangeProperty(qt_xdisplay(), qt_xrootwin(), _xim_servers, 
			  XA_ATOM, 32, PropModeReplace, 
			  (uchar *)&_server_atom, 1);
    //Q_ASSERT(res == Success);
    qDebug("No XIM_SERVER. Regiter IM server");
    
  } else {
    ulong* atoms = (ulong*)value;
    int i;
    for (i = 0; i < nitems; i++) {
      if (atoms[i] == _server_atom) {  
	// Already registered
	qDebug("IM server registered already. No change.");
	break;
      }
    }
    
    if (i == nitems) {
      // Register XIM_SERVER Atom
      res = XChangeProperty(qt_xdisplay(), qt_xrootwin(), _xim_servers, 
			    XA_ATOM, 32, PropModeAppend,  
			    (uchar *)&_server_atom, 1);
      //Q_ASSERT(res == Success);
      qDebug("No Kimera. Register IM server");
    }
  }

  if ( value ) XFree((char*)value);
}


void
InputMethod::recvXIMError()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  char unused[4];
  ushort flag, errcode;
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid >> flag >> errcode;
  qDebug("XIM Error received. IM:%d IC:%d flag:%d Error Code:%d", 
	 _curimid, _curicid, flag, errcode);
}


void
InputMethod::recvXIMGetIMValues()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  char unused[4];
  ushort n;
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> n;
  
  QByteArray listximattr;   // reply LISTofXICATTRIBUTE
  QDataStream dsxim(listximattr, IO_WriteOnly);
  dsxim.setByteOrder(_xim->byteOrder(_curimid));
  
  //
  ushort id;
  dsbuf >> id;
  Q_ASSERT(n == 2 && id == 0);
  qDebug("Get IM values request IM:%d id:%d", _curimid, id);
  
  dsxim << id << (ushort)8;
  dsxim << 0x04 << 0x00;
  //dsxim << (ulong)XIMethod::OverTheSpotStyle;   // OverTheSpot style
  dsxim << _xim->getInputStyle(_curimid);    // OverTheSpot style
  for (int j = 0; j < pad(8); j++) {
    dsxim << (uchar)0;       // unused
  }
  
  QByteArray data;         // reply data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid)); 

  ds << (uchar)XIM_GET_IM_VALUES_REPLY << (uchar)0;
  ds << numberElements(4 + listximattr.size());
  ds << _curimid << (ushort)listximattr.size();
  ds.writeRawBytes(listximattr.data(), listximattr.size());

  sendClientMessage(data);
  qDebug("XSendEvent XIM_GET_IM_VALUES_REPLY");
}


void
InputMethod::recvXIMSetICFocus()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  char unused[4];
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;
  qDebug("SetICFocus IM: %d IC: %d", _curimid, _curicid);
  
  // Set font of preedit
  QFont   ft = QFont();      // Default font
  QString ftname;
  QString ftnamelist = _xim->fontPreedit(_curimid, _curicid);
  ftname = ftnamelist.section(',', 1, 1);
  
  // set font size
  int size = 0;
  QString strsize;
  strsize = ftname.section('-', 7, 7);
  if (!strsize.isEmpty() && strsize != '*') {
    size = strsize.toInt();
    ft.setPointSize( size );
  }

  qDebug("set font: %s", ft.rawName().data());
  qDebug("set font pixel size:%d  point size:%d", ft.pixelSize(), ft.pointSize());  
  _kanjiconvt->setFont(ft);
  
  // check trigger flag
  if (_xim->triggerFlag(_curimid, _curicid) == TRUE) {   // if on-key
    // calculate preedit point and set im-ID and ic-ID of focus window
    _kanjiconvt->setPreeditPoint(calcPreeditPoint(_curimid, _curicid));
    emit triggerNotify(TRUE);
  } else {
    emit triggerNotify(FALSE);
  }
}


void
InputMethod::recvXIMUnsetICFocus()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  char unused[4];
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;
  qDebug("UnsetICFocus IM: %d IC: %d", _curimid, _curicid);

  _kanjiconvt->clear();
}


void
InputMethod::recvXIMSetICValues()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  char unused[4];
  ushort n;
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid >> n;
  dsbuf.readRawBytes((char*)unused, 2);  // unused

  qDebug("XIMSetICValues Request IM:%d IC:%d data size:%d", _curimid, _curicid, buf.size());
  
  int pos = dsbuf.device()->at();
  QByteArray xicattr;
  while ( !dsbuf.atEnd() && dsbuf.device()->at() < pos + n) {
    ushort id, len;
    dsbuf >> id >> len;
    xicattr.resize(len);
    dsbuf.readRawBytes(xicattr.data(), len);
    dsbuf.readRawBytes((char*)unused, pad(len));         // unused
    _xim->setICValue(_curimid, _curicid, id, xicattr);   // set IC value
    //qDebug("current pos:%d", dsbuf.device()->at());
  }

  QByteArray data;         // reply data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));  
  ds << (uchar)XIM_SET_IC_VALUES_REPLY << (uchar)0;
  ds << numberElements( 4 );
  ds << _curimid << _curicid;
  
  sendClientMessage(data);
  qDebug("XSendEvent XIM_SET_IC_VALUES_REPLY: IM:%d IC:%d", _curimid, _curicid);

  // check trigger flag
  if (_xim->triggerFlag(_curimid, _curicid) == TRUE) {
    // It's unknown that this is focus window now,
    // therfore don't specify IM-ID and IC-ID.
    _kanjiconvt->setPreeditPoint(calcPreeditPoint());   // set preedit point
    
    emit triggerNotify(TRUE);
  } else {
    emit triggerNotify(FALSE);
  }
}


void 
InputMethod::recvXIMGetICValues()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
 
  char unused[4];
  ushort n;
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid >> n;

  qDebug("XIMGetICValues Request IM:%d IC:%d", _curimid, _curicid);

  QByteArray listxicattr;  // reply LISTofXICATTRIBUTE
  QDataStream dsxic(listxicattr, IO_WriteOnly);
  dsxic.setByteOrder(_xim->byteOrder(_curimid)); 

  for (int i = 0; i < n/2; i++) {
    ushort id;
    dsbuf >> id;
    
    QByteArray value = _xim->getICValue(_curimid, _curicid, id);
    dsxic << id << (ushort)value.size();
    dsxic.writeRawBytes(value.data(), value.size());
    for (int j = 0; j < pad(value.size()); j++)  dsxic << (uchar)0; // unused
  }
  
  QByteArray data;         // reply data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  
  ds << (uchar)XIM_GET_IC_VALUES_REPLY << (uchar)0;
  ds << numberElements(8 + listxicattr.size());
  ds << _curimid << _curicid << (ushort)listxicattr.size();
  ds << (ushort)0;       // unused
  ds.writeRawBytes(listxicattr.data(), listxicattr.size());
  
  sendClientMessage(data);
  qDebug("XSendEvent XIM_GET_IC_VALUES_REPLY");
}


void 
InputMethod::recvXIMCreateIC()
{
  char unused[4];
  ushort n;
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> n;
 
  _curicid = _xim->createXIC(_curimid);   // create IC
  qDebug("IM:%d  Create IC id:%d data size:%d", _curimid, _curicid, buf.size());

  QByteArray listofxic(n), data;
  QDataStream dslistxic(listofxic, IO_ReadOnly);
  dslistxic.setByteOrder(_xim->byteOrder(_curimid));
  dsbuf.readRawBytes(listofxic.data(), n);

  while ( !dslistxic.atEnd() ) {
    ushort id, len;
    dslistxic >> id >> len;
    data.resize(len);
    dslistxic.readRawBytes(data.data(), len);
    dslistxic.readRawBytes((char*)unused, pad(len));  // unused
    _xim->setICValue(_curimid, _curicid, id, data);   // set IC value
  }

  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.data.b[0] = (uchar)XIM_CREATE_IC_REPLY;
  cm.data.s[1] = 1;   // length
  cm.data.s[2] = _curimid;  // set input-method-ID
  cm.data.s[3] = _curicid;  // set input-context-ID

  sendClientMessageProtocol(_xim->commWin(_curimid), cm);
  qDebug("XSendEvent XIM_CREATE_IC_REPLY");
}


void 
InputMethod::recvXIMDestroyIC()
{
  char unused[4];
  ushort n;
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;

  qDebug("IM:%d  Destory IC id:%d", _curimid, _curicid);
  _xim->removeXIC(_curimid, _curicid);

  QByteArray data;         // reply data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  
  ds << (uchar)XIM_DESTROY_IC_REPLY << (uchar)0;
  ds << numberElements(4);
  ds << _curimid << _curicid;
  
  sendClientMessage(data);
  qDebug("XSendEvent XIM_DESTROY_IC_REPLY");
}


void
InputMethod::recvXIMSync()
{
  char unused[4];
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;

  qDebug("Synchronuze IM:%d  IC:%d", _curimid, _curicid);

  QByteArray data;         // reply data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));

  ds << (uchar)XIM_SYNC_REPLY << (uchar)0;
  ds << numberElements(4);
  ds << _curimid << _curicid;

  sendClientMessage(data);
  qDebug("XSendEvent XIM_SYNC_REPLY");
}


void 
InputMethod::recvXIMSyncReply()
{
  char unused[4];
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;

  qDebug("Receive XIM_SYNC_REPLY");
}


void 
InputMethod::recvXIMTriggerNotify()
{
  char unused[4];
  ulong  flag, index, mask;
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid)); 

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid >> flag >> index >> mask;
  qDebug("XIMTriggerNotify Request IM: %d IC:%d", _curimid, _curicid);
  qDebug("flag:%d mask:%d", flag, mask);

  if ( flag ) {
    // if off-key
    _kanjiconvt->clear();   // preedit clear
    _xim->setTriggerFlag(_curimid, _curicid, FALSE);
    sendXIMSetEventMask(_curimid, _curicid);  // XIM_SET_EVENT_MASK
    emit triggerNotify(FALSE);  // trigger signal
  } else {
    // if on-key
    _xim->setTriggerFlag(_curimid, _curicid, TRUE);
    sendXIMSetEventMask(_curimid, _curicid, KeyPressMask); // XIM_SET_EVENT_MASK
    _kanjiconvt->setPreeditPoint(calcPreeditPoint(_curimid, _curicid));
    emit triggerNotify(TRUE);   // trigger signal
  }

  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.data.b[0] = (uchar)XIM_TRIGGER_NOTIFY_REPLY;
  cm.data.s[1] = 1;   // length
  cm.data.s[2] = _curimid;
  cm.data.s[3] = _curicid;

  sendClientMessageProtocol(_xim->commWin(_curimid), cm);
  qDebug("XSendEvent XIM_TRIGGER_NOTIFY_REPLY");
}


void 
InputMethod::recvXIMEncodingNegotiation()
{
  char unused[4];
  ushort n, m;
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> n;
  qDebug("XIMEncodingNegotiation Request IM: %d", _curimid);
  qDebug("byte length of encoding list: %d", n);

  int index = 0;
  QCString encode;
  while ( !dsbuf.atEnd() ) {
    uchar len;
    dsbuf >> len;
    QCString str(len + 1);
    dsbuf.readRawBytes(str.data(), len);
    qDebug("client supported encoding:%s", str.data());
    //if ((encode = str) == QCString("eucJP")) break;
    if ((encode = str) == QCString("COMPOUND_TEXT")) break;
    index++;
  }
  //Q_ASSERT(encode == QCString("eucJP"));
  Q_ASSERT(encode == QCString("COMPOUND_TEXT"));

  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.data.b[0] = (uchar)XIM_ENCODING_NEGOTIATION_REPLY;
  cm.data.s[1] = 2;  // length
  cm.data.s[2] = _curimid;  // set input-method-ID
  cm.data.s[3] = 0;
  cm.data.s[4] = index;
  
  sendClientMessageProtocol(_xim->commWin(_curimid), cm);
  qDebug("XSendEvent XIM_ENCODING_NEGOTIATION_REPLY");
}


void
InputMethod::recvXIMQureyExtension()
{
  char unused[4];
  ushort n;
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> n;
  qDebug("XIMQureyExtension Request IM:%d len:%d", _curimid, n);
 
  //QByteArray listofext;
  //QDataStream dsext(listofext, IO_WriteOnly);
  //dsext.setByteOrder(_xim->byteOrder(_curimid));
  if ( n ) {
    //buf.resize(8 + n);
    int pos = dsbuf.device()->at();
    while ( !dsbuf.atEnd() &&  dsbuf.device()->at() < pos + n) {
      uchar len;
      dsbuf >> len;
      QCString str(len + 1);
      dsbuf.readRawBytes(str.data(), len);
      qDebug("extensions supported(IM lib):%s", str.data());
    }
  }
  
  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));

  ds << (uchar)XIM_QUERY_EXTENSION_REPLY << (uchar)0;
  ds << (ushort)1;
  ds << _curimid << (ushort)0;
  //ds.writeRawBytes(listofext.data(), listofext.size());
  sendClientMessage(data);
  qDebug("XSendEvent XIM_QUERY_EXTENSION_REPLY");
}


void 
InputMethod::recvSelectionRequest(const XEvent& e) const
{
  if (e.xselectionrequest.property != _locales && 
      e.xselectionrequest.property != _transport) return;
  
  XSelectionEvent se;
  se.type = SelectionNotify;
  se.requestor = e.xselectionrequest.requestor;
  se.selection = e.xselectionrequest.selection;
  se.target = e.xselectionrequest.target;
  se.time = e.xselectionrequest.time;
  se.property = e.xselectionrequest.property;
  qDebug("selection: %d", se.selection);
  qDebug("target: %d", e.xselectionrequest.target);
  qDebug("property: %d", se.property);
  
  if (se.property == _locales) {
    qDebug("Convert locales");
    char strlocales[] = "@locale=ja_JP.eucJP,ja_JP,japanese,japan,ja";  // ja_JP.SJIS ...
    XChangeProperty(qt_xdisplay(), se.requestor, se.property, se.target, 8,
		    PropModeAppend, (uchar*)strlocales, 
		    strlen(strlocales));
    XSendEvent(qt_xdisplay(), se.requestor, False, 0, (XEvent*)&se);

  } else if (se.property == _transport ){
    qDebug("Convert transport");
    char strtransport[] = "@transport=X/";
    XChangeProperty(qt_xdisplay(), se.requestor, se.property, se.target, 8,
		    PropModeAppend, (uchar*)strtransport, 
		    strlen(strtransport));
    XSendEvent(qt_xdisplay(), se.requestor, False, 0, (XEvent*)&se);
  }
}


void
InputMethod::recvSelectionClear()
{
  if ( _xim ) {
    delete _xim;
    _xim = 0;
  }

  QMessageBox::critical(0, QString("Critical Message"), QString("Received Selection Clear event.\nKimera stops service."), QMessageBox::Ok | QMessageBox::Default, 0);
}


void 
InputMethod::recvXIMOpen(const XEvent& e)
{
  qDebug("length(%d): %s", e.xclient.data.b[4], e.xclient.data.b + 5);

  QByteArray listofximattr;   // LISTofXIMATTR
  QDataStream dsxim(listofximattr, IO_WriteOnly);
  dsxim.setByteOrder(_xim->byteOrder(_curimid));
  
  //Q_ASSERT(XIMAttribute::im_attr_list.count() != 0);
  QValueList<SupportAttr> imattr = XIMethod::getSuppIMAttr();
  Q_ASSERT(imattr.count() != 0);
 
  for (QValueList<SupportAttr>::const_iterator it = imattr.begin(); it != imattr.end(); ++it) {
    dsxim << (ushort)(*it).id();
    dsxim << (ushort)(*it).type();
    dsxim << (ushort)(*it).attribute().length(); 
    dsxim.writeRawBytes((*it).attribute().data(), (*it).attribute().length());
    for (int i = 0; i < pad(2 + (*it).attribute().length()); i++) {
      dsxim << (uchar)0;   // unused
    }
  } 
  
  QByteArray listofxicattr;   // LISTofXICATTR
  QDataStream dsxic(listofxicattr, IO_WriteOnly);
  dsxic.setByteOrder(_xim->byteOrder(_curimid));
  
  //Q_ASSERT(XICAttribute::ic_attr_list.count() != 0);
  QValueList<SupportAttr> icattr = XIMethod::getSuppICAttr();
  Q_ASSERT(icattr.count() != 0);

  //for (SupportICAttrList::const_iterator it = XICAttribute::ic_attr_list.begin(); it != XICAttribute::ic_attr_list.end(); ++it) {
  for (QValueList<SupportAttr>::const_iterator it = icattr.begin(); it != icattr.end(); ++it) {
    dsxic << (ushort)(*it).id();
    dsxic << (ushort)(*it).type();
    dsxic << (ushort)(*it).attribute().length();
    dsxic.writeRawBytes((*it).attribute().data(), (*it).attribute().length());
    for (int i = 0; i < pad(2 + (*it).attribute().length()); i++) {
      dsxic << (uchar)0;   // unused
    }
  }

  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  ds << (uchar)XIM_OPEN_REPLY << (uchar)0;  // set opcode
  ds << (ushort)numberElements(8 + listofximattr.size() + 
			       listofxicattr.size()); // length

  Q_ASSERT( _curwin );
  _curimid = _xim->createIM(_curwin);
  _curwin = 0;
  
  //XIMAttribute* pxim = new XIMAttribute(_curwin, _xim->byteOrder(_curimid));
  //_curwin = 0;
  //pxim->init();
  //_xim_list.append(pxim);
  //_curimid = pxim->im();

  ds << _curimid << (ushort)listofximattr.size();
  ds.writeRawBytes(listofximattr.data(), listofximattr.size());

  ds << (ushort)listofxicattr.size();
  ds << (ushort)0;   // unused
  
  ds.writeRawBytes(listofxicattr.data(), listofxicattr.size());

  //sendXIMRegisterTriggerkeys(pxim->im()); // XIM_REGISTER_TRIGGERKEYS
  sendXIMRegisterTriggerkeys(_curimid); // XIM_REGISTER_TRIGGERKEY

  sendClientMessage(data);
  qDebug("XSendEvent XIM_OPEN_REPLY");
}


void
InputMethod::recvXIMClose()
{
  char unused[4];
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid;
  qDebug("Close request input-method-ID:%d", _curimid);
  
  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  ds << (uchar)XIM_CLOSE_REPLY << (uchar)0;  // set opcode
  ds << (ushort)numberElements( 4 );
  ds << (ushort)_curimid << (ushort)0;   // unused
  
  sendClientMessage(data);
  qDebug("XSendEvent XIM_CLOSE_REPLY");

  _curwin = _xim->commWin(_curimid);
  _xim->removeIM(_curimid);
}


void 
InputMethod::recvXIMConnect(const XEvent& e)
{
  // Check byte order and connect to XIM
  if (e.xclient.data.b[4] == 0x42) { 
    qDebug("byte order: BigEndian");
    _xim->connectIM(_curwin, QDataStream::BigEndian);
    //_byteorder = QDataStream::BigEndian;
  } else if (e.xclient.data.b[4] == 0x6c) {
    qDebug("byte order: LittleEndian");
    _xim->connectIM(_curwin, QDataStream::LittleEndian);
    //_byteorder = QDataStream::LittleEndian;
  } else {
    qDebug("byte order: format incorrect");
  }
    
  qDebug("client-major-protocol-version: %d", e.xclient.data.s[3]);
  qDebug("client-minor-protocol-version: %d", e.xclient.data.s[4]);
  qDebug("number of client-auth-protocol-names: %d", e.xclient.data.s[5]);
 
  Q_ASSERT(e.xclient.data.s[3] == 1);  // check protocol version
  Q_ASSERT(e.xclient.data.s[4] == 0);  // check protocol version
  Q_ASSERT( !e.xclient.data.s[5] );    // unsupported authorization

  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.data.b[0] = (uchar)XIM_CONNECT_REPLY;
  cm.data.s[1] = 1;  // length
  cm.data.s[2] = 1;  // set protocol version

  sendClientMessageProtocol(_curwin, cm);
  qDebug("XSendEvent XIM_CONNECT_REPLY");
}


void
InputMethod::recvXIMDisconnect(const XEvent& e)
{
  _xim->disconnectIM();
  /*
  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  ds << (uchar)XIM_DISCONNECT_REPLY << (uchar)0;  // set opcode
  ds << (ushort)numberElements( 0 );
  */

  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.data.b[0] = (uchar)XIM_DISCONNECT_REPLY;
  sendClientMessageProtocol(_curwin, cm);
  qDebug("XSendEvent XIM_DISCONNECT_REPLY");

  _curimid = 0;
  _curicid = 0;
  _curwin = 0;
}


void 
InputMethod::recvProperty(const XEvent& e)
{
  Q_ASSERT(_buffer->size() == 0);

  Atom type;
  int format;
  ulong nitems;
  ulong bytes_after;
  uchar* value = 0;

  // Get the Atom contained in XIM_SERVERS Property
  int res = XGetWindowProperty(qt_xdisplay(), winId(), e.xclient.data.l[1],
                               0, e.xclient.data.l[0], True, XA_STRING, 
			       &type, &format, &nitems, &bytes_after, &value); 
  Q_ASSERT(res == Success);
  Q_ASSERT(nitems == e.xclient.data.l[0]);
  Q_ASSERT(bytes_after == 0);
  Q_CHECK_PTR(value);
  
  // Write the receved data to buffer
  Q_ASSERT(_buffer->isOpen());
  _buffer->writeBlock((char*)value, e.xclient.data.l[0]);

  if ( value ) XFree((char*)value);
}


void 
InputMethod::recvXIMForwardEvent()
{
  QByteArray buf = _buffer->buffer();   // received data
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));

  char unused[4];
  ushort flag, serial;
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid >> flag >> serial;

  qDebug("Forward event im:%d ic:%d, flag:%d serial:%d", _curimid, _curicid, flag, serial);
  //qDebug("size:%d (%d)", buf.size(), sizeof(xEvent));

  XEvent event;
  char* pe = new char[buf.size() - 12];
  dsbuf.readRawBytes(pe, buf.size() - 12);
  xEvent2XEvent(qt_xdisplay(), pe, event);
  delete[] pe;
  //qDebug("key:%d detail:%d", xkey.state, xkey.keycode);
  
  event.xkey.window = winId();     // target for this widget
  qApp->x11ProcessEvent(&event);   // To translate button event
                                   //  see keyPressEvent function
}


void 
InputMethod::keyPressEvent(QKeyEvent* e)
{
  //QString ss = QKeySequence(e->key());
  //qDebug("keyPressEvent :%s", ss.latin1()); 
  if (_kanjiconvt->processKeyEvent(*e) == FALSE) {
    qDebug("Send XIM_FORWARD_EVENT because of no function for the key");
    // XIM FORWARD EVENT
    QByteArray buf = _buffer->buffer();   // received data
    Q_ASSERT(buf.size() > 0);
    sendClientMessage(buf);
  }
} 

void 
InputMethod::recvXIMResetIC()
{
  char unused[4];
  QByteArray buf = _buffer->buffer();
  QDataStream dsbuf(buf, IO_ReadOnly);
  dsbuf.setByteOrder(_xim->byteOrder(_curimid));
  
  dsbuf.readRawBytes((char*)unused, 4);   // header
  dsbuf >> _curimid >> _curicid;
  qDebug("XIMResetIC request IM:%d IC:%d", _curimid, _curicid);

  _kanjiconvt->clear();   // preedit clear
  
  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  ds << (uchar)XIM_RESET_IC_REPLY << (uchar)0;  
  ds << (ushort)numberElements( 4 );
  ds << _curimid << _curicid;
  ds << (ushort)0;
  for (int i = 0; i < pad(2); i++) {
    ds << (uchar)0;   // unused
  }
  
  sendClientMessage(data);
  qDebug("XSendEvent XIM_RESET_IC_REPLAY");
}


void
InputMethod::slotDecidePhases(const QString& str)
{
  uint len = str.local8Bit().length();
  qDebug("Decide phases:%s (%d)", str.local8Bit().data(), str.length());

  char* pstr = (char*)alloca(len + 1);
  memcpy(pstr, str.local8Bit().data(), len + 1);
  
  XTextProperty tp;
  memset(&tp, 0, sizeof(tp));
  int res;
  res = XmbTextListToTextProperty(qt_xdisplay(), &pstr, 1, XCompoundTextStyle, &tp);
  Q_ASSERT(res == Success);
  
  QByteArray data;         // send data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  ds << (uchar)XIM_COMMIT << (uchar)0;
  ds << numberElements(8 + tp.nitems);
  ds << _curimid << _curicid << (ushort)0x0003;
  ds << (ushort)tp.nitems;
  ds.writeRawBytes((char*)tp.value, tp.nitems);
  for (int i = 0; i < pad(tp.nitems); i++) {
    ds << (uchar)0;  // unused
  }
  
  sendClientMessage(data);
  qDebug("IM:%d IC:%d XSendEvent XIM_COMMIT", _curimid, _curicid);
}


void
InputMethod::sendXIMSetEventMask(ushort im, ushort ic, ulong fwrd, ulong sync) const
{
  QByteArray data;         // send data
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  
  ds << (uchar)XIM_SET_EVENT_MASK << (uchar)0;
  ds << numberElements(12);
  ds << im << ic;
  ds << fwrd << sync;

  sendClientMessage(data);
  qDebug("XSendEvent XIM_SET_EVENT_MASK");
}


void 
InputMethod::sendXIMRegisterTriggerkeys(ushort im) const
{
  QByteArray listofkey;
  QDataStream dskey(listofkey, IO_WriteOnly);
  dskey.setByteOrder(_xim->byteOrder(_curimid)); 
  QString s;
  
  // Set default starting key.
  if ((s = Config::readEntry("_cmbstartkey", "Kanji")) == "Kanji") {
    dskey << (ulong)XK_Kanji << 0L << 0L;
    
  } else {
    QKeySequence ks = QKeySequence( s );
    uint c = ks[0] & 0xff;

    switch (ks[0] & Qt::MODIFIER_MASK) {
    case Qt::SHIFT:
      dskey << c << (uint)ShiftMask << (uint)ShiftMask;
      break;
      
    case Qt::CTRL:
      dskey << c << (uint)ControlMask << (uint)ControlMask;
      if (tolower(c) && tolower(c) != c)
	dskey << (uint)tolower(c) << (uint)ControlMask << (uint)ControlMask;
      break;
      
    case Qt::ALT:
      dskey << c << (uint)Mod1Mask << (uint)Mod1Mask;
      if (tolower(c) && tolower(c) != c) 
	dskey << (uint)tolower(c) << (uint)Mod1Mask << (uint)Mod1Mask;
      break;
      
    default:
      dskey << c << 0L << 0L;
      if (tolower(c) && tolower(c) != c) 
	dskey << (uint)tolower(c) << 0L << 0L;
      break;
    }
  }

  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));
  
  ds << (uchar)XIM_REGISTER_TRIGGERKEYS << (uchar)0;
  ds << numberElements(12 + 2 * listofkey.size());
  ds << im << (ushort)0;
  ds << (ulong)listofkey.size();
  ds.writeRawBytes(listofkey.data(), listofkey.size()); // on-keys
  ds << (ulong)listofkey.size();
  ds.writeRawBytes(listofkey.data(), listofkey.size()); // off-keys

  sendClientMessage(data);
  qDebug("XSendEvent XIM_REGISTER_TRIGGERKEYS");
}


void
InputMethod::sendXIMSync() const
{
  QByteArray data;
  QDataStream ds(data, IO_WriteOnly);
  ds.setByteOrder(_xim->byteOrder(_curimid));

  ds << (uchar)XIM_SYNC << (uchar)0;
  ds << numberElements(4);
  ds << _curimid << _curicid; 

  sendClientMessage(data);
  qDebug("XSendEvent XIM_SYNC");
}


void 
InputMethod::sendClientMessageProtocol(Window w, XClientMessageEvent& cm) const
{
  cm.type = ClientMessage;
  cm.display = qt_xdisplay();
  cm.window = w;
  cm.message_type = _xim_protocol;
  cm.format = 8;  
  XSendEvent(qt_xdisplay(), w, False, 0, (XEvent*)&cm);
  XFlush(qt_xdisplay());
}


void
InputMethod::sendClientMessageMoredata(Window w, XClientMessageEvent& cm) const
{ 
  cm.type = ClientMessage;
  cm.display = qt_xdisplay();
  cm.window = w;
  cm.message_type = _xim_moredata;
  cm.format = 8; 
  XSendEvent(qt_xdisplay(), w, False, 0, (XEvent*)&cm);
}


void 
InputMethod::sendClientMessage(const QByteArray& data) const
{
  uint len = data.size();
  if (len > DIVIDINGSIZE) {
    sendPropertywithCM(data);   // send Property with CM
    return;
  }

  qDebug("Send message. major-protocol-number:%d length:%d", 
	 (uchar)data[0], len);
  XClientMessageEvent cm;

  int j = 0;
  for (;;) {
    if (len > 20) {
      for (int i = 0; i < 20; i++) {
	cm.data.b[i] = data[j++];
      }
      sendClientMessageMoredata(_xim->commWin(_curimid), cm);
      len -= 20;

    } else {
      memset(cm.data.b, 0, 20);
      for (int i = 0; i < len; i++) {
	cm.data.b[i] = data[j++];
      }
      sendClientMessageProtocol(_xim->commWin(_curimid), cm);
      break;
    }
  }
}


void 
InputMethod::sendPropertywithCM(const QByteArray& data) const
{
  Window commwin = _xim->commWin(_curimid);
  XChangeProperty(qt_xdisplay(), commwin, _kimera_atom, 
		  XA_STRING, 8, PropModeAppend, (uchar*)data.data(),
		  data.size());
  
  XClientMessageEvent cm;
  cm.type = ClientMessage;
  cm.display = qt_xdisplay();
  cm.window = commwin;
  cm.message_type = _xim_protocol;
  cm.format = 32;
  cm.data.l[0] = data.size();
  cm.data.l[1] = _kimera_atom;
  XSendEvent(qt_xdisplay(), commwin, False, 0, (XEvent*)&cm);
  XFlush(qt_xdisplay());
}


// X Transport Connection
// Receive X Transport Connection ClientMessage
void 
InputMethod::recvXTransportConnection(const XEvent& e)
{
  if (e.xclient.message_type != _xim_xconnect || 
      e.xclient.format != 32) {
    qDebug("incorrect format: %s", __FUNCTION__);
    return;
  }
  
  _curwin = e.xclient.data.l[0];  // set client window
  qDebug("Client communication window ID: %d", _curwin);
  qDebug("Request client-major-transport-version: %d", e.xclient.data.l[1]);
  qDebug("Request client-minor-transport-version: %d", e.xclient.data.l[2]);
  
  XClientMessageEvent cm;
  memset(cm.data.b, 0, 20);
  cm.type = e.xclient.type;
  cm.display = e.xclient.display;
  cm.window = _curwin;    
  cm.message_type = e.xclient.message_type;
  cm.format = 32;
  cm.data.l[0] = winId();
  cm.data.l[1] = 0;  // set major transport version
  cm.data.l[2] = 2;  // set minor transport version
  cm.data.l[3] = DIVIDINGSIZE;
  
  qDebug("Reply client-major-transport-version: %d", cm.data.l[1]);
  qDebug("Reply client-minor-transport-version: %d", cm.data.l[2]);
  
  XSendEvent(cm.display, cm.window, False, 0, (XEvent*)&cm);  
  qDebug("XSendEvent XTransportConnection reply");
}


//bool
//InputMethod::event(QEvent* e)
void 
InputMethod::customEvent(QCustomEvent* e)
{
  //if (e->type() == (QEvent::Type)SelectionEvent) {
  if (e->type() == KimeraApp::Selection) {
    recvSelectionEvent(e);
    //return true;
    return;

  //} else if (e->type() != (QEvent::Type)MessageRecv) {
  } else if (e->type() != KimeraApp::XIMClientMessage) {    
    //return false;
    return;
  }

  //if ( !_xim ) return true;
  if ( !_xim ) return;

  //XEvent *event = (XEvent*)((QCustomEvent*)e)->data();
  XEvent *event = (XEvent*)e->data();
  //qDebug("x11ClientMessage called. Message_type(Atom): %s(%d)", 
  //       XGetAtomName(qt_xdisplay(), event->xclient.message_type), 
  //       event->xclient.message_type);
  
  // Dispatch Client Message
  if (event->xclient.message_type == _xim_xconnect) {
    recvXTransportConnection(*event);
    //return true;
    return;

  } else if (event->xclient.message_type == _xim_protocol && 
             event->xclient.format == 32) {
    // Receive Property message (It must be Property-with-CM) and
    // write the receved data to buffer
    recvProperty(*event);
    
  } else if (event->xclient.message_type != _xim_protocol &&
             event->xclient.message_type != _xim_moredata) {
    qDebug("Unexpected ClientMessage message_type: %d", 
           event->xclient.message_type);
    qDebug("Unexpected ClientMessage format: %d", event->xclient.format);
    //return true;
    return;
   
  } else {
    Q_ASSERT(event->xclient.format == 8);
    
    // Write the receved data to buffer
    //_buffer.at(_buffer.size());  // at end of buffer
    Q_ASSERT(_buffer->isOpen());
    _buffer->writeBlock(event->xclient.data.b, 20);
  }
  
  if (event->xclient.message_type == _xim_protocol) {
    //qDebug("major-opcode: %d", (uchar)_buffer.buffer().at(0));
    Q_ASSERT( !_buffer->buffer().at(1) );  // minor-opcode must be zero
    //qDebug("length(byte): %d", *(ushort*)(_buffer.buffer().data() + 2) * 4);
    qDebug("*** Receive Message Protocol  number:%d  size:%d ***",
	   _buffer->buffer().at(0), _buffer->buffer().size());
  
    XEvent e = *event;    
    switch (_buffer->buffer().at(0)) {  // major-opcode of received packet
    case XIM_CONNECT:
      recvXIMConnect( e );
      break;

    case XIM_DISCONNECT:
      recvXIMDisconnect( e );
      break;
      
    case XIM_OPEN:
      recvXIMOpen( e );
      break;
      
    case XIM_CLOSE:
      recvXIMClose();
      break;

    case XIM_QUERY_EXTENSION:
      recvXIMQureyExtension();
      break;
      
    case XIM_ENCODING_NEGOTIATION:
      recvXIMEncodingNegotiation();
      break;

    case XIM_GET_IM_VALUES:
      recvXIMGetIMValues();
      break;

    case XIM_CREATE_IC:
      recvXIMCreateIC();
      break;
      
    case XIM_DESTROY_IC:
      recvXIMDestroyIC();
      break;

    case XIM_GET_IC_VALUES:
      recvXIMGetICValues();
      break;
 
    case XIM_SET_IC_VALUES:
      recvXIMSetICValues();
      break;
      
    case XIM_SET_IC_FOCUS:
      recvXIMSetICFocus();
      break;

    case XIM_UNSET_IC_FOCUS:
      recvXIMUnsetICFocus();
      break;

    case XIM_TRIGGER_NOTIFY:
      recvXIMTriggerNotify();
      break;

    case XIM_FORWARD_EVENT:
      recvXIMForwardEvent();
      break;
      
    case XIM_ERROR:
      recvXIMError();
      break;
      
    case XIM_RESET_IC:
      recvXIMResetIC();
      break;

    case XIM_SYNC:
      recvXIMSync();
      break;

    case XIM_SYNC_REPLY:
      recvXIMSyncReply();
      break;

    default:
      qDebug("major-opcode: %d", (uchar)_buffer->buffer().at(0));
      qDebug("length(byte): %d", *(ushort*)(_buffer->buffer().data() + 2) * 4);
      break;
    }
    
    _buffer->close();
    _buffer->setBuffer(QByteArray(0));   // clean buffer
    Q_ASSERT(_buffer->size() == 0); 
    _buffer->open( IO_WriteOnly | IO_Append );
  }
  
  //return true;
  return;
}


void 
InputMethod::recvSelectionEvent(const QEvent* e)
{
  //if (e->type() != (QEvent::Type)SelectionEvent) {
  if (e->type() != KimeraApp::Selection) {
    Q_ASSERT(0);
    return;
  }

  //XEvent event = *(XEvent*)((QCustomEvent*)e)->data();
  XEvent event = *(XEvent*)((QCustomEvent*)e)->data();

  // Dispatch Selection Event
  switch (event.type) {
  case SelectionRequest:
    qDebug("Receive SelectionRequest");
    recvSelectionRequest(event);
    break;

  case SelectionClear:
    qDebug("Receive SelectionClear");
    recvSelectionClear();
    break;

  case SelectionNotify:
    qDebug("Receive SelectionNotify");
    Q_ASSERT(0);
    break;

  default: 
    qDebug("unknown selection event:%d", event.type);
    Q_ASSERT(0);
    break;
  }

  return;
}


// Calculate preedit point of current window
// Store the specified IM-ID and IC-ID temporarily
QPoint
InputMethod::calcPreeditPoint(ushort im, ushort ic) const
{
  static ushort imid, icid;  // IM-ID and IC-ID of latest focus window
  
  if ( im && ic ) {
    imid = im;
    icid = ic;
  } else if ( !imid || !icid ) {
    Q_ASSERT(0);    // programming error
    return QPoint();
  }
  
  int x, y;
  Window child_win;
  Bool ret;
  // Translate client window coordinates
  ret = XTranslateCoordinates(qt_xdisplay(), _xim->focusWindow(imid, icid), qt_xrootwin(), 0, 0, &x, &y, &child_win);

  if ( !ret ) {
    // disconnect IM
    _xim->removeIM(imid);
    _xim->disconnectIM();
    imid = 0;
    icid = 0;
    return  QPoint();
  }
  
  qDebug("latest focus windows:%d IM:%d IC:%d x:%d y:%d", _xim->focusWindow(imid, icid), imid, icid, _xim->spotPreedit(imid, icid).x() + x, _xim->spotPreedit(imid, icid).y() + y);
  
  return _xim->spotPreedit(imid, icid) + QPoint(x, y);
}




