/*
  iiimqccontext.cpp
  Copyright (C) 2003 Free Standards Group

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions: The above copyright notice and this
  permission notice shall be included in all copies or substantial
  portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL OPENI18N WG OR FREE STANDARDS GROUP. BE LIABLE
  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
  THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
  ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.

  Except as contained in this notice, the names of OpenI18N WG and/or
  Free Standards Group shall not be used in advertising or otherwise to
  promote the sale, use or other dealings in this Software without prior
  written authorization from OpenI18N WG and/or Free Standards Group
  as applicable.

  Author: ILGYOUNG PARK <Karl.Park@Sun.COM>

*/

#include <qwidget.h>
#include <qinputcontext.h>
#include <qapplication.h>
#include <qvaluelist.h>
#include <iiimcf.h>

#include "iiimqccontext.h"
#include "keymapper.h"
#include "lookup.h"

class IIIMInputContextPrivate
{
public:
  typedef IIIMInputContext::IIIMICLIST IIIMICLIST;
  typedef IIIMInputContext::IIIMICLISTITR IIIMICLISTITR;
  typedef IIIMInputContext::IIIMInputContextID IIIMInputContextID;
  IIIMInputContextPrivate (IIIMInputContext *iiimqc);
  bool getLookupChoiceItems (QStringList &slist,
			     int *psize,
			     int *pfirst_index,
			     int *plast_index,
			     int *pcurrent_index);
private:
  IIIMInputContext *_iiimqc;
};

IIIMInputContextPrivate::IIIMInputContextPrivate (IIIMInputContext *iiimqc)
{
  if (iiimqc)
    _iiimqc = iiimqc;
  else
    _iiimqc = 0;
}

bool
IIIMInputContextPrivate::getLookupChoiceItems (QStringList &qstrlist,
					       int *psize,
					       int *pfirst_index,
					       int *plast_index,
					       int *pcurrent_index
					       )
{
  IIIMCF_context context;
  IIIMCF_lookup_choice lookup_choice;
  IIIMF_status st;
  QString *qlookupitem;
  
  //int *pchoices_per_window;
  int size;
  int index_of_first_candidate;
  int index_of_last_candidate;
  int index_of_current_candidate;

  int idx;
  IIIMCF_text acandidate;
  IIIMCF_text alabel;
  int flag;
  IIIMICLISTITR itr = _iiimqc->_curic;
  context = (*itr)._c;
  
      
  iiimcf_get_lookup_choice (context, &lookup_choice);
      
  iiimcf_get_lookup_choice_size (lookup_choice,
				 &size,
				 &index_of_first_candidate,
				 &index_of_last_candidate,
				 &index_of_current_candidate);
  
  if (psize)
    *psize = size;
  if (pfirst_index)
    *pfirst_index = index_of_first_candidate;
  if (plast_index)
    *plast_index = index_of_last_candidate;
  if (pcurrent_index)
    *pcurrent_index = index_of_current_candidate;
  
  for (int idx = 0; idx < size; idx++){
    const IIIMP_card16 *u16text;
    int thelength;

    iiimcf_get_lookup_choice_item (lookup_choice, idx,
				   &acandidate, &alabel,
				   &flag);

    st = iiimcf_get_text_utf16string (acandidate, &u16text);
    if (st != IIIMF_STATUS_SUCCESS)
      continue;
  
    iiimcf_get_text_length (acandidate, &thelength);
    if (st != IIIMF_STATUS_SUCCESS)
      continue;
    qlookupitem = new QString ((const QChar *)u16text, thelength);
    //qDebug ("candidate : %s\n", (const char *)*qlookupitem);
    qstrlist.append (*qlookupitem);
				       
  }

  return TRUE;
}


int IIIMInputContext::counter = 0;
IIIMInputContext *IIIMInputContext::singleton = 0;

IIIMInputContext *
IIIMInputContext::getIIIMInputContext()
{
  if (!singleton){
    singleton = new IIIMInputContext;
    return singleton;
  }
  else
    return singleton;
}

IIIMInputContext::IIIMInputContext ()
{
  qDebug ("IIIMInputcontext()");
  d = new IIIMInputContextPrivate (this);
  iiimcfIsInitialized = FALSE;
  _h = 0;
  //IIIMInputContext::singleton = this;  
  keymapper = new KeyMapper();
  iiimlookup = new IIIMQCFLookup(this->holderWidget (), this);

  if (!iiimqcf_init_iiim ()){
    qDebug ("iiimcf libarary initialization failed!!!\n");
  }
}

IIIMInputContext::~IIIMInputContext ()
{
  if (iiimlookup)
    delete iiimlookup;
}

#if 0
QString IIIMInputContext::name ()
{
  return "iiimqcf";
}
#endif

QString IIIMInputContext::identifierName ()
{
  return "iiimqcf";
}

QString IIIMInputContext::language ()
{
}


/*!
  setFocus () is called
  when (input context) onwerWidget gets focus in event
 */
void
IIIMInputContext::setFocus ()
{
  // Intentionally not calling QInputContext::focusWidget()
  // in accordance with the commenet in qinputcontext.h

  IIIMICLISTITR itr;
  int pos;
  int length;
  QString qpreedit;

  QWidget *keywidget = qApp->focusWidget();

  qDebug ("setFocus() for widget(name: %s, WId : %d)",
	  qApp->focusWidget ()->name (),
	  qApp->focusWidget ()->winId ()
	  );
  itr = get_iiimcf_session_context (keywidget);
  set_current_iiimcf_session_context (keywidget);
  

  if (get_preedit_data (keywidget, qpreedit, &pos)){
    sendIMEvent (QEvent::IMCompose, qpreedit, pos);    
  }

  //iiimlookup->showLookupWindow (keywidget);
}


/*!
  ussetFocus () is (supposed to be) called
  when (input context) onwerWidget gets focus in event.
  But for now, there doesn't seem to be actual implementation
  to call this method,from , say, QWidget class etc.
 */
void
IIIMInputContext::unsetFocus ()
{
  QWidget *w = qApp->focusWidget ();
  if (w)
    qDebug ("unsetFocus() for widiget \"%s\"",
	    qApp->focusWidget()->name());
  //iiimlookup->hideLookupWindow ();
  
}

void
IIIMInputContext::setMicroFocus( int x, int y, int w, int h, QFont *f)
{
  //qDebug ("setMicroFocus():Cursor position (%d, %d)", x, y);
}


void
IIIMInputContext::mouseHandler( int x, QEvent::Type type,
			       Qt::ButtonState button, Qt::ButtonState state )
{
  if ( type == QEvent::MouseButtonPress ||
       type == QEvent::MouseButtonDblClick )
    reset();
}


void
IIIMInputContext::iiimqcf_event_dispatch (QWidget *w)
{
  IIIMCF_context c;
  IIIMF_status st;
  IIIMCF_event ev;
  IIIMCF_event_type et;
  IIIMCF_text preedit_text;
  int conversion_mode = FALSE;

  QKeyEvent *pqkey;
  QString qpreedit;
  QString qcommitted;
  QString *qlookupitem;
  QStringList candidates;

  int size, first_index, last_index, current_index;
  /*
    if (_curic == 0)
    set_current_iiimcf_session_context (w);
  */
  c = (*_curic)._c;
  
  while ( (st = iiimcf_get_next_event (c, &ev)) == IIIMF_STATUS_SUCCESS){
    st = iiimcf_get_event_type (ev, &et);
    if (st != IIIMF_STATUS_SUCCESS)
      continue;

    switch (et){
    case IIIMCF_EVENT_TYPE_KEYEVENT:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_KEYEVENT ");
      /*
	I need to create a QEvent and send to the icHolder widget.
	(qlineedit, qtextedit for example).
	then, as defined in qwidget.cpp (bool QWidget::event( QEvent *e )),
	icHolder's imComposeEvent( QIMEvent *e) will be called,
	and this handles the preedit-related details.
      */
      IIIMCF_keyevent iiimkev;
      st = iiimcf_get_keyevent_value (ev, &iiimkev);
      qDebug ("\tkeycode(0x%x), keychar(0x%x), modifier (0x%x)",
	      iiimkev.keycode, iiimkev.keychar, iiimkev.modifier );
      
      if (st != IIIMF_STATUS_SUCCESS)
	break;
      pqkey = new QKeyEvent (QEvent::KeyPress,
			     keymapper->i2q(iiimkev.keycode),
			     keymapper->i2q(iiimkev.keychar),
			     keymapper->i2q(iiimkev.modifier));
      qApp->sendEvent (w, pqkey);

      qDebug ("iiimqcf_event_dispatch() : keyevent");
      break;
      
    case IIIMCF_EVENT_TYPE_TRIGGER_NOTIFY:
      st = iiimcf_get_current_conversion_mode (c, &conversion_mode);

      if (conversion_mode){
	qDebug ("iiimqcf_event_dispatch() : trigger on");
	sendIMEvent (QEvent::IMStart); 
      } else {
	qDebug ("iiimqcf_event_dispatch() : trigger off");
	iiimlookup->clearLookupChoices ();
	iiimlookup->hideLookupWindow ();
	
	sendIMEvent (QEvent::IMEnd);
      }

      break;
      
    case IIIMCF_EVENT_TYPE_UI_PREEDIT_START:
      qDebug ("iiimqcf_event_dispatch() : preedit start");
      sendIMEvent (QEvent::IMStart); 
      /*
	st = iiimcf_get_current_conversion_mode (c, &conversion_mode);

	if (conversion_mode){
	qDebug ("iiimqcf_event_dispatch() : trigger on");
	sendIMEvent (QEvent::IMStart); 
	}
      */
      break;
      
    case IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE:
      int pos;
      int length;
      if (!get_preedit_data (w, qpreedit, &pos))
	break;

      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_PREEDIT_CHANGE");
      //sendIMEvent (QEvent::IMCompose, qpreedit, pos, qpreedit.length ());
      //qDebug ("%s", qpreedit.utf8());
      sendIMEvent (QEvent::IMCompose, qpreedit, pos);
      qpreedit = QString::null;
      break;
    case IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_PREEDIT_DONE");
      get_committed_text (w, qcommitted);
      sendIMEvent (QEvent::IMEnd, qcommitted);
      /*
	st = iiimcf_get_current_conversion_mode (c, &conversion_mode);

	if (conversion_mode){
	qDebug ("iiimqcf_event_dispatch() : trigger on");
	sendIMEvent (QEvent::IMEnd);
	}
      */
      break;
      
    case IIIMCF_EVENT_TYPE_UI_COMMIT:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_COMMIT");
      if (!get_committed_text (w, qcommitted))
	break;
      sendIMEvent (QEvent::IMEnd, qcommitted, qcommitted.length ());

      break;

      
    case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_START:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_START");

      
      d->getLookupChoiceItems (candidates,
			       &size, &first_index,
			       &last_index, &current_index);
      
      iiimlookup->setLookupChoices (candidates,
				    size, first_index,
				    last_index, current_index);
      qDebug ("To OPEN LOOKUP WINDOW FROM %s", (*_curic)._w->name());
      iiimlookup->showLookupWindow ((*_curic)._w);

      break;
      
    case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_CHANGE:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_CHANGE");

      d->getLookupChoiceItems (candidates,
			       &size, &first_index,
			       &last_index, &current_index);
      
      iiimlookup->setLookupChoices (candidates,
				    size, first_index,
				    last_index, current_index);
      break;
      
    case IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_DONE:
      qDebug ("iiimqcf_event_dispatch():IIIMCF_EVENT_TYPE_UI_LOOKUP_CHOICE_DONE");
      iiimlookup->clearLookupChoices ();
      iiimlookup->hideLookupWindow ();
      break;
      
    default:
      qDebug ("iiimqcf_event_dispatch():event type 0x%x", et);
      break;
    }
      
  }
}


bool
IIIMInputContext::get_preedit_data (QWidget *w, QString &text, int *cursor)
{
  IIIMCF_context c;
  IIIMCF_text preedit_text;
  IIIMF_status st;
  QString *qtext;
  
  const IIIMP_card16 *u16text;
  int length;
  
  c = (*_curic)._c;
  st = iiimcf_get_preedit_text (c, &preedit_text, cursor);
  
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;

  st = iiimcf_get_text_utf16string (preedit_text, &u16text);
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;
  
  iiimcf_get_text_length (preedit_text, &length);
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;
  qtext = new QString ((const QChar *)u16text, length);
  text = *qtext;
  
  return TRUE;
}

bool
IIIMInputContext::get_committed_text (QWidget *w, QString &text)
{
  IIIMCF_context c;
  IIIMCF_text committed_text;
  IIIMF_status st;
  QString *qtext;
  
  const IIIMP_card16 *u16text;
  int length;
  
  c = (*_curic)._c;
  st = iiimcf_get_committed_text (c, &committed_text);
  
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;

  st = iiimcf_get_text_utf16string (committed_text, &u16text);
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;
  
  iiimcf_get_text_length (committed_text, &length);
  if (st != IIIMF_STATUS_SUCCESS)
    return FALSE;
  qtext = new QString ((const QChar *)u16text, length);
  text = *qtext;
  
  return TRUE;
}

/*
  Button State is defined as following
  in qnamespace.h
  
  enum ButtonState {
  NoButton	= 0x0000,
  LeftButton	= 0x0001,
  RightButton	= 0x0002,
  MidButton	= 0x0004,
  MouseButtonMask = 0x0007,
  ShiftButton	= 0x0100,
  ControlButton   = 0x0200,
  AltButton	= 0x0400,
  MetaButton	= 0x0800,
  KeyButtonMask	= 0x0f00,
  Keypad	= 0x4000
  };

*/
bool
IIIMInputContext::filterEvent (const QEvent *event)
{
  IIIMCF_keyevent *pkev;
  IIIMCF_event ev;
  IIIMF_status st;
  QKeyEvent *qkev;
  QWidget *keywidget;

  int code;
  char ascii;
  Qt::ButtonState state;
  
  qDebug ("filterEvent()");
  // We are only interested in key event..
    if ((event->type() != QEvent::KeyRelease) &&
      (event->type() != QEvent::KeyPress))
    return FALSE;
  
  // But for now, let's not care about key release event
  if (event->type() == QEvent::KeyRelease){
    //qDebug ("filterEvent=KeyRelease Event ignoring");
    return FALSE;
  }

#if 0
  /*
    It is not likely this code will evaluate to true, becuase
    this checking is already done within qapplication_x11.cpp.
    see QApplication::x11ProcessEvent( XEvent* event )
  */
  if (!keywidget->isEnabled () || !keywidget->isInputMethodEnabled ()){
    qDebug ("IIIMInputContext(%d) : not yet composing, thus ignoring",
	      id);
    return FALSE;
  }
#endif
  
  //qkev = dynamic_cast<QKeyEvent *>(event);
  qkev = (QKeyEvent *)event;
  code = qkev->key();
  ascii = qkev->ascii();
  state = qkev->state();
  
  // if current status is not Composing, then return FALSE
  //if (isNotComposing ())
  //return FALSE;

  // ugly test code
  // see the qnamespace.h in qt library.
  // I need a mapping table from QT's value to IIIM
  
  if (state == Qt::ControlButton){
    if (code == ' ')
      qDebug ("control space pressed");
  }
  
  pkev = get_iiimcf_keyevent_from (qkev);
  if (!pkev){
    qDebug ("Error creating IIIMCF_keyevent object");
    return FALSE;
  }
  
  st = iiimcf_create_keyevent (pkev, &ev);


  if (st != IIIMF_STATUS_SUCCESS){
    qDebug ("Error creating IIIMCF_keyevent object");
    return FALSE;
  }
  /* delete pkev */
  delete pkev;
  
  /* FIX ME */
  keywidget = qApp->focusWidget();
  
  if (forwardEvent (keywidget, ev, &st)){
    iiimqcf_event_dispatch (keywidget);
    return TRUE;
  }

  return FALSE;
}

IIIMCF_handle
IIIMInputContext::get_iiimcf_handle ()
{
  return _h;
}

IIIMCF_handle
IIIMInputContext::iiimqcf_init_iiim (void)
{
  IIIMF_status st;
  IIIMCF_attr attr;

  if (iiimcfIsInitialized){
    counter++;
    qDebug ("iiimqcf_init_iiim : Handle alread exists, returning");
    return _h;
  }
  
  st = iiimcf_initialize (IIIMCF_ATTR_NULL);
  if (st != IIIMF_STATUS_SUCCESS){
    return 0;
  }

  st = iiimcf_create_attr (&attr);
  if (st != IIIMF_STATUS_SUCCESS){
    return 0;
  }
  st = iiimcf_attr_put_string_value (attr,
				     IIIMCF_ATTR_CLIENT_TYPE,
				     "QT IIIMCF Module");
  if (st != IIIMF_STATUS_SUCCESS){
    return 0;
  }
  
  qDebug ("iiimqcf_init_iiim : Creating IIIMCF handle");
#if 0
  int i = 1;
  while (i);
#endif
  st = iiimcf_create_handle (attr, &_h);
  
  if (st != IIIMF_STATUS_SUCCESS){
    return 0;
  }
  
  st = iiimcf_destroy_attr (attr);
  iiimcfIsInitialized = TRUE;
  return _h;
}

#if 0
IIIMCF_keyevent *
IIIMInputContext::createIIIMCFKeyEvent (int code, char ascii, int state)
{
  IIIMCF_keyevent *pkev = new IIIMCF_keyevent;
  if (pkev == 0){
    qDebug ("createIIIMCFKeyEvent failed");
    return 0;
  }
  
  /* instantiation code here..*/
  pkev->keychar = code;
  pkev->keycode = code;
  pkev->modifier = state;

  return pkev;
}
#endif

IIIMCF_keyevent *
IIIMInputContext::get_iiimcf_keyevent_from (QKeyEvent *kev)
{
  IIIMCF_keyevent *pkev = new IIIMCF_keyevent;

  
  Qt::ButtonState state;
  if (pkev == 0){
    qDebug ("get_iiimcf_keyevent_from() failed");
    return 0;
  }
  
  pkev->keycode = keymapper->q2i(kev->key());
  pkev->keychar = keymapper->q2i(kev->key());  // am I doing things right with keychar here?
  /*
  if (pkev->keycode == Qt::Key_F9){
    pkev->keycode =  IIIMF_KEYCODE_F9;
    pkev->keychar =  IIIMF_KEYCODE_F9;
  }
  */
  pkev->modifier = keymapper->modifier (kev->state());
  //pkev->modifier = state;
  //pkev->modifier = 0;
  
  return pkev;
}

bool
IIIMInputContext::forwardEvent (QWidget *keywidget, IIIMCF_event ev, IIIMF_status *st)
{
  IIIMCF_context c;
  IIIMICLISTITR itr;
    
  qDebug ("forwardEvent() on widget %s", keywidget->name ());

  itr = get_iiimcf_session_context (keywidget);

  c = (*itr)._c;

  *st = iiimcf_forward_event (c, ev);
  switch (*st){
   
  case IIIMF_STATUS_SUCCESS:
    break;
  case IIIMF_STATUS_IC_INVALID:
  case IIIMF_STATUS_EVENT_NOT_FORWARDED:
    break;
  case IIIMF_STATUS_STREAM_SEND:
  case IIIMF_STATUS_STREAM_RECEIVE:
  case IIIMF_STATUS_CONNECTION_CLOSED:
    //set_error_message (context_iiim);
    iiimcf_ignore_event (ev);
    break;
  default:
    iiimcf_ignore_event (ev);
    //status_window_set_text (context_iiim->status_window, "");
    break;
  }

  return (*st == IIIMF_STATUS_SUCCESS);
}

bool
IIIMInputContext::set_current_iiimcf_session_context (QWidget *widget)
{
  IIIMICLISTITR it = get_iiimcf_session_context (widget);
  _curic = it;
  if (it != _iclist.end ()){
    _curic = it;
    return TRUE;
  } else
    return FALSE;
}


IIIMInputContext::IIIMICLISTITR
IIIMInputContext::search_iiimiclist_for (QWidget *widget)
{
  IIIMICLISTITR it = _iclist.begin ();

  qDebug ("Searcing widget %s", widget->name());
  while (it != _iclist.end ()){
    qDebug ("search_iiimiclist_for ():%s\n", (*it)._w->name());
    if ((*it)._w == widget)
      break;
    else
      it++;
  }
  if (it == _iclist.end ())
    qDebug ("Search Failed");
  else
    qDebug ("Search Success");
  return it; // it == _iclist.end ();
}

IIIMInputContext::IIIMICLISTITR
IIIMInputContext::create_iiimcf_session_context (QWidget *widget)
{
  IIIMICLISTITR it = search_iiimiclist_for (widget);
  IIIMInputContextID iiimic;
  
  if ( (it == _iclist.end ()) ||
       !(*it)._c ){
    IIIMCF_attr attr;
    IIIMF_status st;
    // new iiimcf session needs to be created for widget
    
    //IIIMCF_language iiim_lang = NULL;
    //iiim_lang =
    st = iiimcf_create_attr (&attr);
    // Need to do returned status checking

    //iiimcf_attr_put_ptr_value (attr, IIIMCF_ATTR_INPUT_LANGUAGE,
    //		       iiim_lang);
    st = iiimcf_create_context (_h, NULL, &iiimic._c);
    qDebug ("create_iiimcf_session_context():creating IIIMCF_context");
    iiimic._w = widget;
    it = _iclist.prepend (iiimic);
    
    iiimcf_destroy_attr (attr);
    
  }
  return it;
}

IIIMInputContext::IIIMICLISTITR
IIIMInputContext::get_iiimcf_session_context (QWidget *widget, bool doCreate)
{
  IIIMICLISTITR it = search_iiimiclist_for (widget);
  
  if ( (it == _iclist.end () || !(*it)._c) &&  doCreate){
    qDebug ("get_iiimcf_session_context() : need to create new IIIMCF_context on %s", widget->name());
    return create_iiimcf_session_context (widget);
  }
  return it;
}
