/*
 * Copyright 2003 Sun Microsystems Inc.
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Karl Park <karl.park@sun.com>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <IMProtocolStruct.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "koinput.h"
#include "koif_prv.h"
#include "composer.h"
#include "hangul.h"
#include "hhdict.h"
#include "lookupbuf.h"
#include "utfchar.h"
#include "configapi.h"
#include "kolelog.h"
#include "logf.h"

static Bool hangul_engine_start (int n, KOLE_config **config);
static Bool hangul_engine_get_hotkeys (int *n_count, IMEKeyEventStruct ***keylist);
static Bool hangul_engine_finish (void);

static IMEBuffer __imebuffer_new (void);
static Bool hangul_imebuffer_new (IMEBuffer *new_buffer, int new_session_id,
				  int reuqest_engine_id);
static Bool hangul_imebuffer_initialize (IMEBuffer a_buffer);
static Bool hangul_imebuffer_conversion_start (IMEBuffer a_buffer);
static Bool hangul_imebuffer_conversion_end (IMEBuffer a_buffer);

static Bool hangul_imebuffer_get_session_id (IMEBuffer a_buffer,
					     int *session_id_return);
static Bool hangul_imebuffer_get_engine_id (IMEBuffer a_buffer,
					    int *engine_id_return);
static Bool hangul_imebuffer_get_input_mode (IMEBuffer, INPUT_MODE_T *);
static Bool hangul_imebuffer_get_keyboard_layout (IMEBuffer, KEYBOARD_LAYOUT_T *);
static Bool hangul_imebuffer_get_active_repertoire (IMEBuffer, OPTION_CHARSET *);
static Bool hangul_imebuffer_set_active_repertoire (IMEBuffer, OPTION_CHARSET *);
static Bool hangul_imebuffer_get_deletion_option (IMEBuffer, OPTION_DELETION *);
static Bool hangul_imebuffer_get_commitmode_option (IMEBuffer, OPTION_DELETION *);
static Bool hangul_imebuffer_get_preedit (IMEBuffer a_buffer, UTFCHAR **out_utfstr);
static Bool hangul_imebuffer_get_status (IMEBuffer a_buffer,
					 UTFCHAR **status_return);
static Bool hangul_imebuffer_get_commit_string (IMEBuffer a_buffer,
						UTFCHAR **commit_string_return);
static Bool hangul_imebuffer_get_candidates (IMEBuffer a_buffer, 
					     int *n_candidates_return,
					     UTFCHAR ***candidates_list_return);
static Bool hangul_imebuffer_lookup_do_next_page (IMEBuffer a_buffer,
						  int *new_page_num_return);
static Bool hangul_imebuffer_lookup_do_previous_page (IMEBuffer a_buffer,
						      int *new_page_num_return);
static Bool hangul_imebuffer_lookup_do_scroll (IMEBuffer a_buffer);
static Bool hangul_imebuffer_lookup_do_select_candidate (IMEBuffer a_buffer,
							 IMEKeyEventStruct *iiim_key_event);
static Bool hangul_imebuffer_lookup_get_current_candidate (IMEBuffer a_buffer,
							   int *lookup_position_return);
static Bool hangul_imebuffer_process_key_input (IMEBuffer ime_buffer,
						IMEKeyEventStruct *iiim_key_event);

static Bool hangul_imebuffer_free (IMEBuffer a_buffer);
static Bool hangul_imebuffer_get_what_to_do (IMEBuffer ime_buffer,
					     IMEBufferWhatToDo **to_do_list);

static Bool hangul_imebuffer_set_what_to_do (IMEBuffer ime_buffer,
					     IMEBufferWhatToDo *new_to_do_list);


static Bool hangul_imebuffer_free_lookup_buffer(LookupBufferStruct *lookup_buffer, int *number_of_strings_freed);

static Bool hangul_imebuffer_lookup_get_candidates (IMEBuffer a_buffer,
						    int *n_candidates_return,
						    int *n_candidate_number_return,
						    UTFCHAR ***candidates_list_return);


static Bool
hangul_imebuffer_do_commit (IMEBuffer a_buffer, IMEKeyEventStruct *key_event);

static Bool
hangul_imebuffer_lookup_get_subsidiary_candidates (IMEBuffer a_buffer, IMEKeyEventStruct *key_event);
static Bool hangul_imebuffer_get_configuration (IMEBuffer, int *, KOLE_config ***pconfig);
static Bool apply_kole_config_to_session (int n, KOLE_config **pconfig, Config *sconf);
static void hangul_engine_print_configuration ();
static void do_options (IMEBuffer pimebuffer);

IMEBufferMethodsRec ime_methods = {
  hangul_engine_start,
  hangul_engine_get_hotkeys,
  hangul_imebuffer_new,
  hangul_imebuffer_initialize,
  hangul_imebuffer_conversion_start,
  hangul_imebuffer_conversion_end,
  hangul_imebuffer_get_session_id,
  hangul_imebuffer_get_engine_id,
  hangul_imebuffer_get_input_mode,
  hangul_imebuffer_get_keyboard_layout,
  hangul_imebuffer_get_active_repertoire,
  hangul_imebuffer_set_active_repertoire,
  hangul_imebuffer_get_deletion_option,
  hangul_imebuffer_get_commitmode_option,
  hangul_imebuffer_get_preedit,
  hangul_imebuffer_get_status,
  hangul_imebuffer_get_commit_string,
  hangul_imebuffer_lookup_get_candidates,
  hangul_imebuffer_lookup_do_next_page,
  hangul_imebuffer_lookup_do_previous_page,
  hangul_imebuffer_lookup_do_scroll,
  hangul_imebuffer_lookup_do_select_candidate,
  hangul_imebuffer_lookup_get_current_candidate,
  hangul_imebuffer_process_key_input,
  hangul_imebuffer_free,
  hangul_imebuffer_get_what_to_do,
  hangul_imebuffer_set_what_to_do,
  hangul_imebuffer_get_configuration,
  hangul_engine_finish
};

static Bool hangul_engine_configure (int n, KOLE_config **config);
static void hangul_engine_configure_default (void);

static Bool do_search_candidates (IMEBuffer imebuffer);
static Bool is_printable_commit_character (IMEKeyEventStruct *iiim_key_event);

static Bool hangul_imebuffer_free_commit_buffer (IMEBuffer a_buffer);

static INPUT_COMMAND_TYPE_H hangul_buffer_get_keyevent_type (IMEBuffer a_buffer,
							    IMEKeyEventStruct *key_event);

static Bool hangul_buffer_lookup_selection_is_valid (IMEBuffer a_buffer,
						     IMEKeyEventStruct *key_event);
static Bool hangul_imebuffer_lookup_cancel_lookup (IMEBuffer a_buffer);

static int default_number_of_item_per_lookup_page = 10;
static int default_value_of_first_candidate = '0';


/* Keypad number related convenience functions */
static Bool is_keypad_num (int keycode);
static int map_keypad_num_to_regular_num (int keycode);


unsigned int g_engine_start_counter = 0;

static Dictionary hhdict = { 0, NULL };
static int g_n_config = KOLE_OPTION_TYPE_COUNT;
static KOLE_config *g_engine_config[KOLE_OPTION_TYPE_COUNT];

static void node_print (void *data);

static Bool
hangul_engine_start (int n, KOLE_config **config)
{
    char dic_path[256];
    Bool mthd_return;

    g_engine_start_counter++;
  
    if (g_engine_start_counter > 1)
	return True;
 
    sprintf (dic_path, "%s/%s/%s/%s/%s",
	     DEFAULT_ENGINE_PATH,
	     LANGNAME,
	     COMMON_ENGINE_PATH,
	     DEFAULT_DICT_PATH,
	     DEFAULT_DICT_NAME);
  
    mthd_return = construct_binary_tree_from_file
	(dic_path, &hhdict.p_trees, &hhdict.n_trees);

#if 0
    tree_print (hhdict.p_trees, (void * (*) (void *)) node_print);
#endif

    if (!mthd_return)
	return False;

    mthd_return = hangul_engine_configure (n, config);
    /* for debugging purpose */
    hangul_engine_print_configuration ();    

    if (!mthd_return){
      KOLE_LOG (LOGDEST_STDERR, "failed to set configuration of engine");
      return False;
    }
    
    return True;
}

static Bool
hangul_engine_get_hotkeys (int *n_count, IMEKeyEventStruct ***keylist)
{
  int i;
  int x = 0;
  *n_count = 4;
  *keylist = (IMEKeyEventStruct **) calloc (4, sizeof (IMEKeyEventStruct *));
  for (i = 0; i < *n_count; i++)
    (*keylist)[i] = (IMEKeyEventStruct *) malloc (sizeof (IMEKeyEventStruct));

  (*keylist)[0]->key_code = IM_VK_F2;
  (*keylist)[0]->key_char = 0x0;
  (*keylist)[0]->key_modifier = IM_SHIFT_MASK;
  
  (*keylist)[1]->key_code = IM_VK_F3;
  (*keylist)[1]->key_char = 0x0;
  (*keylist)[1]->key_modifier = IM_SHIFT_MASK;
  
  (*keylist)[2]->key_code = IM_VK_F7;
  (*keylist)[2]->key_char = 0x0;
  (*keylist)[2]->key_modifier =IM_SHIFT_MASK;
  
  (*keylist)[3]->key_code = IM_VK_F8;
  (*keylist)[3]->key_char = 0x0;
  (*keylist)[3]->key_modifier = 0x0;
  
  return True;
}

static Bool
hangul_engine_configure (int n, KOLE_config **config)
{
  int i;
  int n_option;
  KOLE_option_type otype;

  hangul_engine_configure_default ();
  
  if ( (n > g_n_config) ||
       (n <= 0) ||
       !config){
    /*
      passed values are not correct, thus
      set all options to default
     */
    fprintf (stderr,"hangul_engine_configure : setting option to default\n");
    return True;
  } else {
    if (n < g_n_config)
      n_option = n;
    else
      n_option = g_n_config;
    
    for (i = 0 ; i < n_option; i++){

      otype = config[i]->type;
      if (g_engine_config[otype]){
	/* remove them */
	leoption_delete (g_engine_config[otype]);
      }
      g_engine_config[otype] = leoption_copy (config[i]);
    }
  }
  return True;
}

static void
hangul_engine_configure_default (void)
{
  /* FIXME */
  int i = 0;
  
  for (i = 0; i < g_n_config; i++){
    switch (i){
    case KOLE_OPTION_TYPE_KEYBOARD:
      g_engine_config[i] =
	leoption_make_option_with_value (i,STR_KEYBOARD_LAYOUT_2PAIR);
      break;
    case KOLE_OPTION_TYPE_CHARSET:
      g_engine_config[i] =
	leoption_make_option_with_value (i,STR_CHARSET_UNICODE);
      break;
    case KOLE_OPTION_TYPE_DELETION:
      g_engine_config[i] =
	leoption_make_option_with_value (i, STR_DELETION_BY_JASO_NO);
      break;
    case KOLE_OPTION_TYPE_CHARBASED_COMMIT:
      g_engine_config[i] =
	leoption_make_option_with_value (i, STR_COMMITMODE_BY_HOTKEY);
      break;
    }
  }
  return;
}

static Bool
hangul_engine_finish (void)
{
  int i;
  g_engine_start_counter--;
  if (g_engine_start_counter > 0)
    return True;

  for (i = 0 ; i < hhdict.n_trees; i++)
    tree_clear (&hhdict.p_trees[i]);

  hhdict.n_trees = 0;
  free (hhdict.p_trees);
  hhdict.p_trees = NULL;
  
  return True;
}

static IMEBuffer
__imebuffer_new (void)
{
  IMEBuffer new_buffer;
  new_buffer = (IMEBuffer ) calloc(1, sizeof (IMEBufferRec));
  assert(new_buffer != NULL);
  if (new_buffer == NULL){
    /* memory allocation error */
    return NULL;
  }
  return new_buffer;
	
}

static Bool
hangul_imebuffer_new (IMEBuffer *new_buffer, int new_session_id, int request_engine_id)
{
  assert(new_buffer != NULL);

  assert (new_session_id >= 0);

  if (!new_buffer)
    return False;

  /* make sure *new_buffer is not referencing anything */
  assert(*new_buffer == NULL);

  /* if *new_buffer is pointing something,
     try to free what was being pointed,
     and create a new one
  */	
  if (*new_buffer){
    hangul_imebuffer_free(*new_buffer);
  }

  *new_buffer = __imebuffer_new();

  assert (*new_buffer != NULL);

  (*new_buffer)->session_id = new_session_id;
  (*new_buffer)->engine_id = request_engine_id;
  (*new_buffer)->input_mode = INPUT_MODE_ENGLISH;
  
#if 0
  (*new_buffer)->config.charset = IN_UNICODE;
  (*new_buffer)->config.deletion = BY_CHAR;
  (*new_buffer)->config.keyboard = LAYOUT_2PART;
#endif
  
  apply_kole_config_to_session (g_n_config, g_engine_config,
				&((*new_buffer)->config));
  (*new_buffer)->what_to_do.do_preedit = False;
  (*new_buffer)->what_to_do.do_status = False;
  (*new_buffer)->what_to_do.do_lookup = False;
  (*new_buffer)->what_to_do.do_commit = False;
  (*new_buffer)->what_to_do.do_sendback = False;

  composer_hangul_buffer_initialize ( &((*new_buffer)->composer_buffer) );

  if (*new_buffer == NULL)
    return False;
  else
    return True;
}

static Bool
hangul_imebuffer_initialize (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);

  if(a_buffer == NULL){
    return False;
  }

  a_buffer->input_mode = INPUT_MODE_ENGLISH;

  a_buffer->what_to_do.do_lookup = False;
  a_buffer->what_to_do.do_preedit = False;
  a_buffer->what_to_do.do_lookup = False;
  a_buffer->what_to_do.do_preedit = False;
  a_buffer->what_to_do.do_sendback = False;

  /* clear LookupBuffer /HANGULBuffer/ Commit string */
  hangul_imebuffer_free_lookup_buffer (a_buffer->lookup_buffer, NULL);
  a_buffer->lookup_buffer = NULL;
  
  composer_hangul_buffer_initialize (&(a_buffer->composer_buffer));
  hangul_imebuffer_free_commit_buffer (a_buffer);

  return True;
}


static Bool
hangul_imebuffer_free_commit_buffer (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);

  if (a_buffer->commit_string){
    free (a_buffer->commit_string);
    a_buffer->commit_string = NULL;
  }
  return True;
}

static Bool
hangul_imebuffer_conversion_start (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);
  hangul_imebuffer_initialize (a_buffer);
  a_buffer->input_mode = INPUT_MODE_HANGUL;

  a_buffer->what_to_do.do_status = True;
  return True;
}

static Bool
hangul_imebuffer_conversion_end (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);
  a_buffer->input_mode = INPUT_MODE_ENGLISH;
  hangul_imebuffer_initialize (a_buffer);
  a_buffer->what_to_do.do_status = True;
  
  return True;
}



static Bool
hangul_imebuffer_get_session_id (IMEBuffer a_buffer, int *session_id_return)
{
  assert (a_buffer != NULL);
  if (a_buffer == NULL)
    return False;
  assert (session_id_return != NULL);
  if (session_id_return == NULL)
    return False;

  *session_id_return = a_buffer->session_id;
  return True;
}

static Bool
hangul_imebuffer_get_engine_id (IMEBuffer a_buffer, int *engine_id_return)
{
    assert (a_buffer != NULL);
    if (a_buffer == NULL)
	return False;
    assert (engine_id_return != NULL);
    if (engine_id_return == NULL)
	return False;

    assert (a_buffer->engine_id >= 0);
    if (a_buffer->engine_id < 0){
	fprintf(stderr, 
		"hangul_ime_biffer_get_engine_id error: "
		"engine_id can't be negative\n");
	/* try to set the engine id to the default one */
	a_buffer->engine_id = 0;
	*engine_id_return = a_buffer->engine_id;
	return False;
    } else {
	*engine_id_return = a_buffer->engine_id;
	return True;
    }
}

static Bool
hangul_imebuffer_get_input_mode (IMEBuffer a_buffer,
				 INPUT_MODE_T *input_mode_return)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_input_mode error: "
	     "IMEBuffer is null\n");
    return False;
  }

  assert (input_mode_return != NULL);
  if (input_mode_return == NULL){
    fprintf (stderr, "hangul_imebuffer_get_input_mode error: "
	     "current_input_mode is null\n");
    return False;
  }
  *input_mode_return = a_buffer->input_mode;
  return True;
}

static Bool
hangul_imebuffer_get_keyboard_layout (IMEBuffer a_buffer,
				      KEYBOARD_LAYOUT_T *keyboard_layout_return)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_input_mode error: "
	     "IMEBuffer is null\n");
    return False;
  }

  assert (keyboard_layout_return != NULL);
  if (keyboard_layout_return == NULL){
    KOLE_LOG (LOGDEST_STDERR,
	      "hangul_imebuffer_get_input_mode error: "
	      "current_input_mode is null\n");
    return False;
  }
  *keyboard_layout_return = a_buffer->config.keyboard;
  return True;
}

static Bool
hangul_imebuffer_get_active_repertoire (IMEBuffer a_buffer,
					OPTION_CHARSET *charset_return)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_active_repertoire error: "
	     "IMEBuffer is null\n");
    return False;
  }

  assert (charset_return != NULL);
  if (charset_return == NULL){
    KOLE_LOG (LOGDEST_STDERR,
	      "hangul_imebuffer_get_active_repertoire error: "
	      "charset is null\n");
    return False;
  }
  *charset_return = a_buffer->config.charset;
  return True;
}

static Bool
hangul_imebuffer_set_active_repertoire (IMEBuffer a_buffer, OPTION_CHARSET *charset)
{
  assert (charset != NULL);
  if (charset == NULL){
    KOLE_LOG (LOGDEST_STDERR, "hangul_imebuffer_set_active_repertoire error:");
    KOLE_LOG (LOGDEST_STDERR, "charset is NULL");
    return False;
  }
  composer_config_switch_codeset (&a_buffer->config, charset);
  a_buffer->what_to_do.do_status = True;
  return True;
}

static Bool
hangul_imebuffer_get_deletion_option (IMEBuffer a_buffer,
				     OPTION_DELETION *deloption)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_deletion_option error: "
	     "IMEBuffer is null\n");
    return False;
  }

  assert (deloption != NULL);
  if (deloption == NULL){
    KOLE_LOG (LOGDEST_STDERR,
	      "hangul_imebuffer_get_deletion_option error: "
	      "deloption is null\n");
    return False;
  }
  *deloption = a_buffer->config.deletion;
  return True;
}

static Bool
hangul_imebuffer_get_commitmode_option (IMEBuffer a_buffer,
					OPTION_DELETION *commit_option)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_commitmode_option error: "
	     "IMEBuffer is null\n");
    return False;
  }

  assert (commit_option != NULL);
  if (commit_option == NULL){
    KOLE_LOG (LOGDEST_STDERR,
	      "hangul_imebuffer_get_commitmode_option error: "
	      "commit_option is null\n");
    return False;
  }
  *commit_option = a_buffer->config.commit_mode;
  return True;
}
/*
  param:
  out_utfchar: return UTFCHAR is allocated at heap.
  caller has responsibility to free it.

  After the function return, 
  if there's nothing in preedit buffer, *out_utfstr will be NULL
  if *out_utfstr is not NULL, there the preedit string has bee
  created for caller.
*/

static Bool
hangul_imebuffer_get_preedit (IMEBuffer a_buffer, UTFCHAR **out_utfstr)
{
  int i, utfchar_len;
  UTFCHAR *han_char;
  HANGULBuffer *session_buffer;
  HANGUL *current_hangul;

  HANGUL last_hangul; /* to be deleted */

  assert (a_buffer != NULL);

  utfchar_len = composer_hangul_buffer_get_hangul_length(&a_buffer->composer_buffer);
  assert (utfchar_len >= 0);

  if (utfchar_len <= -1) {
    fprintf(stderr, "hangul_imebuffer_get_preedit error: number of hangul "
	    "can't be negative\n");
    return False;
  }
  else if (utfchar_len == 0) {
    *out_utfstr = NULL;
    return True;
  }
  else {
    session_buffer = &a_buffer->composer_buffer;

    assert (session_buffer != NULL);
    if (session_buffer == NULL){
      fprintf(stderr, 
	      "hangul_imebuffer_get_preedit error: composer_buffer can't be null\n");
      return False;
    }
		
    *out_utfstr = (UTFCHAR *) calloc( utfchar_len + 1 , sizeof (UTFCHAR) );

    for (i = 0 ; i < utfchar_len; i++) {
      UTFCHAR combined_character;
      UTFCHAR tmp;
      combined_character = composer_hangul_combine_cho_jung_jong( (session_buffer->buffer[i]) );
      if (combined_character){
	(*out_utfstr)[i] = combined_character;
      } else {
	current_hangul = session_buffer->buffer[i];
	if (!_jamo_is_clean (&current_hangul->L)){
	  tmp = _jamo_get_value (&current_hangul->L);
	  (*out_utfstr)[i] =
	    hangul_combining_chosung_to_compatibility_jamo (tmp);
	} else if (!_jamo_is_clean (&current_hangul->V)) {
	  tmp = _jamo_get_value (&current_hangul->V);
	  (*out_utfstr)[i] =
	    hangul_combining_jungsung_to_compatibility_moeum (tmp);
	} else if (!_jamo_is_clean (&current_hangul->T)) {
	  tmp = _jamo_get_value (&current_hangul->T);
	  (*out_utfstr)[i] =
	    hangul_combining_jongsung_to_compatibility_jaeum (tmp);
	}
      }
    }
    (*out_utfstr)[i] = 0x0000;
  }

  return True;
}

static Bool
hangul_imebuffer_get_status (IMEBuffer a_buffer, UTFCHAR **status_return)
{
  assert (a_buffer != NULL);

  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_get_status error: imebuffer can't be null\n");
    *status_return = NULL;
    return False;
  }
  /* Fix me */
  if (a_buffer->input_mode == INPUT_MODE_ENGLISH) {
    
    *status_return = (UTFCHAR *) calloc (3, sizeof (UTFCHAR));
    (*status_return)[0] = 0xC601;
    (*status_return)[1] = 0xC5B4;
    (*status_return)[2] = 0x0000;
		
  } else if (a_buffer->input_mode == INPUT_MODE_HANGUL ){
    if (a_buffer->config.charset == IN_EUC){
      *status_return = (UTFCHAR *) calloc (6, sizeof (UTFCHAR));
      (*status_return)[0] = 0xD55C;
      (*status_return)[1] = 0xAE00;
      (*status_return)[2] = '[';
      (*status_return)[3] = 0xC644;
      (*status_return)[4] = ']';
      (*status_return)[5] = 0x0000;

    } else {
      *status_return = (UTFCHAR *) calloc (6, sizeof (UTFCHAR));
      (*status_return)[0] = 0xD55C;
      (*status_return)[1] = 0xAE00;
      (*status_return)[2] = '[';
      (*status_return)[3] = 0xC870;
      (*status_return)[4] = ']';
      (*status_return)[5] = 0x0000;
    }
  } else {
    /* for now, this defaults to korean */
    /* fix me */
    *status_return = (UTFCHAR *) calloc (3, sizeof (UTFCHAR));
    (*status_return)[0] = 0xD55C;
    (*status_return)[1] = 0xAE00;
    (*status_return)[2] = 0x0000;

  }

  return True;
}

static Bool
hangul_imebuffer_get_commit_string (IMEBuffer a_buffer, UTFCHAR **commit_string_return)
{
  int commit_length;
  assert (a_buffer != NULL);
  if (a_buffer->commit_string == NULL){
    fprintf (stderr, "hangul_imebuffer_get_commit_string error: commit string is null\n");
    *commit_string_return = NULL;
    return False;
  }
  commit_length = _utfchar_length (a_buffer->commit_string);
  *commit_string_return = (UTFCHAR *) calloc (commit_length + 1, sizeof (UTFCHAR) );
  _utfchar_copy (*commit_string_return, a_buffer->commit_string);
  return True;
}

static Bool
hangul_imebuffer_lookup_get_candidates (IMEBuffer a_buffer, 
					int *n_candidates_return,
					int *n_current_candidate,
					UTFCHAR ***candidates_list_return)
{
  LookupBufferStruct *plub;  
  assert (a_buffer != NULL);
  plub = a_buffer->lookup_buffer;

  hangul_lookupbuf_get_current_page_candidates
    (plub, n_candidates_return, n_current_candidate, candidates_list_return);

  if (*candidates_list_return)
    return True;
  else
    return False;
}

static Bool
hangul_imebuffer_lookup_do_next_page (IMEBuffer a_buffer, int *new_page_num_return)
{
  assert (a_buffer != NULL);
  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_next_page error : "
	     "a_buffer is NULL\n");
    return False;
  }

  if (a_buffer->lookup_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_next_page error : "
	     "a_buffer->lookup_buffer is NULL\n");
    return False;
  }
  
  hangul_lookupbuf_next_page (a_buffer->lookup_buffer);
  /* ignoring new_page_num_return */
  return True;

}

static Bool
hangul_imebuffer_lookup_do_previous_page (IMEBuffer a_buffer, int *new_page_num_return)
{
  assert (a_buffer != NULL);
  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_next_page error : "
	     "a_buffer is NULL\n");
    return False;
  }

  if (a_buffer->lookup_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_next_page error : "
	     "a_buffer->lookup_buffer is NULL\n");
    return False;
  }
  hangul_lookupbuf_previous_page (a_buffer->lookup_buffer);
  /* ignoring new_page_num_return */
  return True;
}


static Bool
hangul_imebuffer_lookup_do_scroll (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);
  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_scroll error :"
	     "a_buffer is NULL\n");
    return False;
  }
  if (a_buffer->lookup_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_do_scroll error :"
	     "a_buffer->lookup_buffer is NULL\n");
    return False;
  }
  hangul_lookupbuf_next_candidate (a_buffer->lookup_buffer);
  
  a_buffer->what_to_do.do_preedit = False;
  a_buffer->what_to_do.do_status = False;
  a_buffer->what_to_do.do_lookup = True;
  a_buffer->what_to_do.do_commit = False;
  a_buffer->what_to_do.do_sendback = False;
  
  return True;
}

static  Bool
hangul_imebuffer_lookup_do_select_candidate (IMEBuffer a_buffer,
					     IMEKeyEventStruct *key_event)
{
  
  LookupBufferStruct *p_lookup_info;
  int selected_candidate_length;
  int i_selected_candidate;
  int key_code;
  int pressed_number;
  int available_candidates;

  assert (a_buffer != NULL);
  assert (key_event != NULL);
  assert (a_buffer->lookup_buffer != NULL);

  p_lookup_info = a_buffer->lookup_buffer;
	
  key_code = key_event->key_code;

  /* if number was selected to commit,
     I need to update the LooupBufferStruct before
     attempting to commit
  */
  available_candidates =
    hangul_lookupbuf_get_count_of_candidates_in_current_page (p_lookup_info);
  
  if (available_candidates == -1)
    return False;

  if (key_code >= IM_VK_0 && key_code <= IM_VK_9){
    pressed_number = key_code - default_value_of_first_candidate;
    if (pressed_number < 0 ||
	pressed_number >= available_candidates){
      fprintf (stderr, "hangul_imebuffer_lookup_do_select_candidate error : "
	       "selection is out of range\n");
      return False;
    }
  } else if (key_code == IM_VK_ENTER){
    pressed_number =
      hangul_lookupbuf_get_current_selection_index (p_lookup_info);
  } else
    return False;

  
  a_buffer->commit_string =
    hangul_lookupbuf_get_candidate_string (p_lookup_info, pressed_number);

  hangul_imebuffer_free_lookup_buffer (p_lookup_info, NULL);
  p_lookup_info = NULL;
  a_buffer->lookup_buffer = NULL;
  
  composer_hangul_buffer_initialize (&(a_buffer->composer_buffer));

  a_buffer->input_mode = INPUT_MODE_HANGUL;
  a_buffer->what_to_do.do_lookup = False;
  a_buffer->what_to_do.do_preedit = False;
  a_buffer->what_to_do.do_commit = True;
  a_buffer->what_to_do.do_sendback = False;
  return True;
}


static Bool
hangul_imebuffer_lookup_get_current_candidate (IMEBuffer a_buffer, int *lookup_position_return)
{
  LookupBufferStruct *p_lb;
  int i;

  assert (a_buffer != NULL);
  if (a_buffer == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_get_current_candidate error: "
	     "imebuffer can't be null\n");
    *lookup_position_return = -1;
    return False;
  }

  p_lb = a_buffer->lookup_buffer;

  assert (lookup_position_return != NULL);
  if (lookup_position_return == NULL){
    fprintf (stderr, "hangul_imebuffer_lookup_get_current_candidate error : "
	     "lookup_position_return can't be null\n");
    return False;
  }
  i = hangul_lookupbuf_get_current_selection_index (p_lb);
  if (i == -1){
    *lookup_position_return = -1;
    return False;
  } else {
    *lookup_position_return = i;
    return True;
  }
}

static Bool
hangul_imebuffer_process_key_input (IMEBuffer ime_buffer, IMEKeyEventStruct *iiim_key_event)
{
  int key_code, key_char, key_modifier;
  int ret;
  HANGULBuffer *hangul_buffer = &ime_buffer->composer_buffer;
  LookupBufferStruct **lub;
  Bool method_return;
  INPUT_MODE_T input_mode_return;
  INPUT_COMMAND_TYPE_H input_command;
  Config *pconfig;

  int hangul_length;
  
  int i;
  int n_candidates = 0;
  UTFCHAR **string_list = NULL;
    
  assert (iiim_key_event != NULL);

  key_code = iiim_key_event->key_code;
  key_char = iiim_key_event->key_char;
  key_modifier = iiim_key_event->key_modifier;

  pconfig = &ime_buffer->config;
  hangul_imebuffer_get_input_mode(ime_buffer, &input_mode_return);
  lub = &(ime_buffer->lookup_buffer);
  input_command = hangul_buffer_get_keyevent_type (ime_buffer, iiim_key_event);

  if (input_command == INPUT_COMMAND_TURN_OFF_CONVERSION){

    hangul_imebuffer_initialize (ime_buffer);
    return True;
    
  } else if (input_command == INPUT_COMMAND_TURN_ON_CONVERSION) {
    
    hangul_imebuffer_conversion_start (ime_buffer);
    return True;

  } else if (input_command == INPUT_COMMAND_SWITCH_CODESET){
    /* keyevent is triggering this, thus cycling through.. */
    composer_config_switch_codeset (&ime_buffer->config, NULL);
    ime_buffer->what_to_do.do_status = True;
    return True;
    
  } else if (input_command == INPUT_COMMAND_SWITCH_KEYBOARD_LAYOUT){
    
    composer_config_switch_keyboard (&ime_buffer->config);
    return True;
    
  } else if (input_command == INPUT_COMMAND_SWITCH_COMMIT_METHOD){
    
    composer_config_switch_commit_method (&ime_buffer->config);
    return True;
    
  } else if (input_command == INPUT_COMMAND_HANJA_DO_LOOKUP) {
    
    method_return = do_search_candidates (ime_buffer);
    if (method_return){
      ime_buffer->input_mode = INPUT_MODE_HANJA;
      ime_buffer->what_to_do.do_lookup = True;
      ime_buffer->what_to_do.do_preedit = True;
      return True;
    } else
      return False;
    
  } else if (input_command == INPUT_COMMAND_LOOKUP_DO_NEXT_PAGE){
    method_return = hangul_imebuffer_lookup_do_next_page (ime_buffer, NULL);
	  
    if (method_return == False)
      return False;
    else
      return True;
  } else if (input_command == INPUT_COMMAND_LOOKUP_DO_PREVIOUS_PAGE){
    method_return = hangul_imebuffer_lookup_do_previous_page (ime_buffer, NULL);
	  
    if (method_return == False)
      return False;
    else
      return True;
	  
  } else if (input_command == INPUT_COMMAND_LOOKUP_DO_SCROLL){
    hangul_imebuffer_lookup_do_scroll (ime_buffer);
	  
    return True;
		
  } else if (input_command == INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE){
    
    if (ime_buffer->input_mode == INPUT_MODE_HANJA ||
	ime_buffer->input_mode == INPUT_MODE_SYMBOL ){
      hangul_imebuffer_lookup_do_select_candidate (ime_buffer,iiim_key_event);
    
    } else if (ime_buffer->input_mode == INPUT_MODE_SYMBOL_MENU){
      hangul_imebuffer_lookup_get_subsidiary_candidates (ime_buffer, iiim_key_event);
      hangul_imebuffer_lookup_do_next_page (ime_buffer, NULL);
      ime_buffer->input_mode = INPUT_MODE_SYMBOL;
      ime_buffer->what_to_do.do_lookup = True;
      ime_buffer->what_to_do.do_preedit = True;
    }
    
    return True;
    
  } else if (input_command == INPUT_COMMAND_CANCEL_LOOKUP) {
    hangul_imebuffer_lookup_cancel_lookup (ime_buffer);
    if (key_code == IM_VK_BACK_SPACE){
      if (composer_hangul_buffer_get_hangul_length (hangul_buffer) == 0){
	/* nothing to delete , this is very unlikely*/
	fprintf (stderr, "something very weird is going on!!!\n");
	return True;
      }
      composer_hangul_buffer_delete_hangul_character (hangul_buffer, True, NULL);
    }
    ime_buffer->what_to_do.do_preedit = True;

    return True;
    
  } else if (input_command == INPUT_COMMAND_SYMBOL_MENU_LOOKUP) {

    hangul_length =
      composer_hangul_buffer_get_hangul_length (&ime_buffer->composer_buffer);

    if (hangul_length > 0){
      if (!hangul_imebuffer_do_commit (ime_buffer, iiim_key_event))
	return False;
    }
    
    method_return =
      composer_symbol_menu_lookup_start (&n_candidates, &string_list);
    
    if ((method_return == False) || (n_candidates == 0))
      return False;
    
    *lub = 
      hangul_lookupbuf_new (n_candidates,
			    default_number_of_item_per_lookup_page,
			    string_list);
    for (i = 0 ; i < n_candidates; i++)
      free (string_list[i]);
    free (string_list);

    hangul_imebuffer_lookup_do_next_page (ime_buffer, NULL);
    ime_buffer->input_mode = INPUT_MODE_SYMBOL_MENU;
    ime_buffer->what_to_do.do_lookup = True;
    ime_buffer->what_to_do.do_preedit = True;
	    
    return True;
  } else if (input_command == INPUT_COMMAND_NOTHING){
    return False;
  } else if (input_command == INPUT_COMMAND_IGNORE){
    return True;
  } else if (input_command == INPUT_COMMAND_COMMIT){
    return hangul_imebuffer_do_commit (ime_buffer, iiim_key_event);
    
  } else if (input_command == INPUT_COMMAND_REGULAR_HANGUL){

    if (key_code == IM_VK_BACK_SPACE){
      Bool deletion_whole_character;
      if (composer_hangul_buffer_get_hangul_length (hangul_buffer) == 0){
	/* nothing to delete */
	return False;
      }
      if ((pconfig->deletion == BY_JASO) ||
	  (key_modifier & IM_SHIFT_MASK)){
	deletion_whole_character =  False;
      } else
	deletion_whole_character =  True;
      
      composer_hangul_buffer_delete_hangul_character (hangul_buffer,
						      deletion_whole_character,
						      NULL);
      ime_buffer->what_to_do.do_preedit = True;
      
      return True;
    }

    switch (pconfig->keyboard){
    case LAYOUT_2PART:
      ret = composer_hangul_automata_2pair (key_code,
					    key_char,
					    key_modifier,
					    hangul_buffer,
					    pconfig);
      break;
      
    case LAYOUT_3PART390:
      ret = composer_hangul_automata_3pair_final (key_code,
						  key_char,
						  key_modifier,
						  hangul_buffer,
						  pconfig);

      break;
      
    case LAYOUT_3PART_FINAL:
      ret = composer_hangul_automata_3pair_final (key_code,
						  key_char,
						  key_modifier,
						  hangul_buffer,
						  pconfig);

      break;
      
    }
    
    if(ret == ComposerRetKeep) {  /* SUCCEEDED */
	ime_buffer->what_to_do.do_preedit = True;
	do_options (ime_buffer);
	return True;
    } else if (ret == ComposerRetBreak) {
      /* succeeded, but to commit preedit buffer */
      if (hangul_buffer->count == 0) {
	return False;
      } else {
	method_return =
	  hangul_imebuffer_do_commit (ime_buffer, iiim_key_event);
	return True;
      }
    } else {
      return False;
    }
  }
}

static void
do_options (IMEBuffer pimebuffer)
{
  int i;
  
  if ((pimebuffer->config.commit_mode == COMMIT_BY_CHAR) &&
      (pimebuffer->composer_buffer.count > 1))
    {
      if (pimebuffer->commit_string)
	free (pimebuffer->commit_string);

      pimebuffer->commit_string =
	(UTFCHAR *) calloc (pimebuffer->composer_buffer.count, sizeof(UTFCHAR));
      for (i = 0; i < pimebuffer->composer_buffer.count - 1; i++)
	{
	  HANGUL *phangul;
	  UTFCHAR combined_character;
	  UTFCHAR tmp;
	  
	  phangul = composer_hangul_buffer_pop_hangul (&pimebuffer->composer_buffer);

	  combined_character = composer_hangul_combine_cho_jung_jong (phangul);
	  if (combined_character){
	    pimebuffer->commit_string[i] =
	      composer_hangul_combine_cho_jung_jong (phangul);
	  } else {

	    if (!_jamo_is_clean (&phangul->L)){
	      tmp = _jamo_get_value (&phangul->L);
	      pimebuffer->commit_string[i] =
		hangul_combining_chosung_to_compatibility_jamo (tmp);
	    } else if (!_jamo_is_clean (&phangul->V)) {
	      tmp = _jamo_get_value (&phangul->V);
	      pimebuffer->commit_string[i] =
		hangul_combining_jungsung_to_compatibility_moeum (tmp);
	    } else if (!_jamo_is_clean (&phangul->T)) {
	      tmp = _jamo_get_value (&phangul->T);
	      pimebuffer->commit_string[i] =
		hangul_combining_jongsung_to_compatibility_jaeum (tmp);
	    }
	    
	  }

	  composer_hangul_free (phangul);
	}
      pimebuffer->what_to_do.do_commit = True;
    }
}

static Bool
hangul_imebuffer_free (IMEBuffer a_buffer)
{
  assert (a_buffer != NULL);
  composer_hangul_buffer_initialize (&(a_buffer->composer_buffer));
  hangul_imebuffer_free_lookup_buffer (a_buffer->lookup_buffer, NULL);
  a_buffer->lookup_buffer = NULL;
  return True;
}

static Bool
hangul_imebuffer_get_what_to_do (IMEBuffer ime_buffer,IMEBufferWhatToDo **to_do_list)
{
  assert (ime_buffer != NULL);

  assert (*to_do_list == NULL);

  *to_do_list = NULL;
  *to_do_list = (IMEBufferWhatToDo *) calloc (1, sizeof (IMEBufferWhatToDo ));

  assert (*to_do_list != NULL);

  (*to_do_list)->do_preedit = ime_buffer->what_to_do.do_preedit;
  (*to_do_list)->do_status = ime_buffer->what_to_do.do_status;
  (*to_do_list)->do_lookup = ime_buffer->what_to_do.do_lookup;
  (*to_do_list)->do_commit = ime_buffer->what_to_do.do_commit;
  (*to_do_list)->do_sendback = ime_buffer->what_to_do.do_sendback;
  return True;
}

static Bool
hangul_imebuffer_set_what_to_do (IMEBuffer ime_buffer,
				 IMEBufferWhatToDo *new_to_do_list)
{
  assert (ime_buffer != NULL);

  assert (new_to_do_list != NULL);

  ime_buffer->what_to_do.do_preedit = new_to_do_list->do_preedit;
  ime_buffer->what_to_do.do_status = new_to_do_list->do_status;
  ime_buffer->what_to_do.do_lookup = new_to_do_list->do_lookup;
  ime_buffer->what_to_do.do_commit = new_to_do_list->do_commit;
  ime_buffer->what_to_do.do_sendback = new_to_do_list->do_sendback;

  if (ime_buffer->what_to_do.do_commit == False){
      if (hangul_imebuffer_free_commit_buffer (ime_buffer) != True){
	  return False;
      }
  }
  /* fix me */
#if 0
  if (ime_buffer->what_to_do.do_preedit == False){
    if (ime_buffer->lookup_buffer){
      hangul_imebuffer_free_lookup_buffer (ime_buffer->lookup_buffer, NULL);
      ime_buffer->lookup_buffer = NULL;
    }
  }
#endif
  return True;
}


static Bool
is_keypad_num (int keycode)
{
  if ((keycode >= IM_VK_NUMPAD0) &&
      (keycode <= IM_VK_NUMPAD9))
    return True;
  else
    return False;
}

static int
map_keypad_num_to_regular_num (int keycode)
{
  if (is_keypad_num (keycode))
    return (keycode - 0x30);
}


/*
  commit_trigger_key is not IMEKeyEventStruct key code,
  becuase the commit triggering key must have been
  re-interpreated based on the keyboard layout which is
  currently active.
*/

static Bool
hangul_imebuffer_do_commit (IMEBuffer a_buffer, IMEKeyEventStruct *key_event)

{
  UTFCHAR commit_trigger_key;
  UTFCHAR *commit_string_return = NULL;
  Bool result;
  int preedit_length;
  int commit_string_length;
  UTFCHAR utf_input;
  Config *pconfig;
  
  pconfig = &a_buffer->config;
  utf_input = get_utf_input_by_keyboard (key_event, pconfig->keyboard);

  preedit_length =
    composer_hangul_buffer_get_hangul_length (&a_buffer->composer_buffer);
  commit_string_length = preedit_length;

  if (preedit_length == 0){
    fprintf (stderr,
	     "hangul_imebuffer_do_commit error: preedit length was zero\n");
    return False;
  }

  result = hangul_imebuffer_get_preedit (a_buffer, &commit_string_return);
  
  if (result == False) {
    fprintf (stderr,
	     "hangul_imebuffer_do_commit error: "
	     "failure getting preedit string\n");
    return False;
  }

  if (utf_input){
    commit_trigger_key = utf_input;
    commit_string_length += 1;      
  } else /* Extra action needs to be taken */
    a_buffer->what_to_do.do_sendback = True;

  /* free up the memory for HANGULList buffer */
  composer_hangul_buffer_initialize (&a_buffer->composer_buffer);

  a_buffer->commit_string =
    (UTFCHAR *) calloc (commit_string_length + 1, sizeof (UTFCHAR));
  _utfchar_copy (a_buffer->commit_string, commit_string_return);
  
  if (preedit_length + 1 == commit_string_length){
    a_buffer->commit_string [commit_string_length - 1] = commit_trigger_key;
    a_buffer->commit_string [commit_string_length ] = 0x0000;
  }

  hangul_imebuffer_free_lookup_buffer(a_buffer->lookup_buffer, NULL);
  a_buffer->lookup_buffer = NULL;

  free (commit_string_return);
  
  a_buffer->what_to_do.do_commit = True;
  a_buffer->what_to_do.do_preedit = False;
  
  return True;	
}

static Bool
hangul_imebuffer_free_lookup_buffer (LookupBufferStruct *lookup_buffer, int *number_of_strings_freed)
{
  Bool ret;
  if (lookup_buffer){
    ret = hangul_lookupbuf_free (lookup_buffer);
  }
  return ret;
}

static Bool
hangul_imebuffer_lookup_get_subsidiary_candidates (IMEBuffer a_buffer,
						   IMEKeyEventStruct *key_event)
{
  LookupBufferStruct **p_lookup_info;
  int selected_candidate_length;
  int i_selected_group;
  int key_code;
  int pressed_number;
  int available_candidates;
  int i;

  int symbol_count = 0;
  UTFCHAR **symbol_list = NULL;
  
  assert (a_buffer != NULL);
  assert (key_event != NULL);

  p_lookup_info = &a_buffer->lookup_buffer;
	
  key_code = key_event->key_code;

  /* if number was selected to commit,
     I need to update the LooupBufferStruct before
     attempting to commit
  */

  available_candidates =
    hangul_lookupbuf_get_count_of_candidates_in_current_page (*p_lookup_info);
  
  if (available_candidates == -1)
    return False;
  
  if (key_code >= IM_VK_0 && key_code <= IM_VK_9){

    i_selected_group = key_code - default_value_of_first_candidate;
    
    if (i_selected_group < 0 ||
	i_selected_group >= available_candidates){
      fprintf (stderr, "hangul_imebuffer_lookup_do_select_candidate error : "
	       "selection is out of range\n");
      return False;
    }
  } else if (key_code == IM_VK_ENTER){
    i_selected_group =
      hangul_lookupbuf_get_current_selection_index (*p_lookup_info);
  } else
    return False;

  /* free symbol menu lookup data first */
  hangul_imebuffer_free_lookup_buffer (*p_lookup_info, NULL);
  *p_lookup_info = NULL;

  /* get detail info for newly chosen symbol group*/
  composer_symbol_detail_lookup_start (i_selected_group,
				       &symbol_count, &symbol_list);
  
  *p_lookup_info = 
    hangul_lookupbuf_new (symbol_count,
			  default_number_of_item_per_lookup_page,
			  symbol_list);
  for (i = 0 ; i < symbol_count; i++)
    free (symbol_list[i]);
  free (symbol_list);
  

  a_buffer->input_mode = INPUT_MODE_SYMBOL;
  a_buffer->what_to_do.do_lookup = True;
  a_buffer->what_to_do.do_preedit = False;
  a_buffer->what_to_do.do_commit = False;
  
  return True;
}



static Bool
hangul_imebuffer_lookup_cancel_lookup (IMEBuffer a_buffer)
{
  hangul_imebuffer_free_lookup_buffer (a_buffer->lookup_buffer, NULL);
  a_buffer->lookup_buffer = NULL;
  
  a_buffer->input_mode = INPUT_MODE_HANGUL;
  a_buffer->what_to_do.do_lookup = False;
  a_buffer->what_to_do.do_preedit = True;
}



static Bool
is_printable_commit_character (IMEKeyEventStruct *iiim_key_event)
{
  int key_code;
  
  key_code = iiim_key_event->key_code;
  
  switch (key_code){
  case IM_VK_ENTER:
  case IM_VK_TAB:
  case IM_VK_COMMA:
  case IM_VK_SPACE:
  case IM_VK_MINUS:
  case IM_VK_PERIOD:
  case IM_VK_SLASH:
  case IM_VK_0:
  case IM_VK_1:
  case IM_VK_2:
  case IM_VK_3:
  case IM_VK_4:
  case IM_VK_5:
  case IM_VK_6:
  case IM_VK_7:
  case IM_VK_8:
  case IM_VK_9:
  case IM_VK_SEMICOLON:
  case IM_VK_EQUALS:
  case IM_VK_OPEN_BRACKET:
  case IM_VK_BACK_SLASH:
  case IM_VK_CLOSE_BRACKET:
  case IM_VK_NUMPAD0:
  case IM_VK_NUMPAD1:
  case IM_VK_NUMPAD2:
  case IM_VK_NUMPAD3:
  case IM_VK_NUMPAD4:
  case IM_VK_NUMPAD5:
  case IM_VK_NUMPAD6:
  case IM_VK_NUMPAD7:
  case IM_VK_NUMPAD8:
  case IM_VK_NUMPAD9:
  case IM_VK_MULTIPLY:
  case IM_VK_ADD:
  case IM_VK_SEPARATER:
  case IM_VK_SUBTRACT:
  case IM_VK_DECIMAL:
  case IM_VK_DIVIDE:
  case IM_VK_BACK_QUOTE:
  case IM_VK_QUOTE:
  case IM_VK_AMPERSAND:
  case IM_VK_ASTERISK:
  case IM_VK_QUOTEDBL:
  case IM_VK_LESS:
  case IM_VK_GREATER:
  case IM_VK_BRACELEFT:
  case IM_VK_BRACERIGHT:
  case IM_VK_AT:
  case IM_VK_COLON:
  case IM_VK_CIRCUMFLEX:
  case IM_VK_DOLLAR:
  case IM_VK_EURO_SIGN:
  case IM_VK_EXCLAMATION_MARK:
  case IM_VK_INVERTED_EXCLAMATION_MARK:
  case IM_VK_LEFT_PARENTHESIS:
  case IM_VK_NUMBER_SIGN:
  case IM_VK_PLUS:
  case IM_VK_RIGHT_PARENTHESIS:
  case IM_VK_UNDERSCORE:
  case IM_VK_DEAD_TILDE:
  case 0x7e: /* Tilde.. */
    return True;
    break;
  default:
    return False;
    break;
  }

}

static Bool
u16_to_ko_euc (UTFCHAR *u16str, unsigned char **euc_return)
{
  iconv_t cd;
  size_t ret;
  char inbuffer[256], outbuffer[256];
  char *inptr;
  char *outptr;
  int utflen;
  int inlen, outlen;
  
  assert (u16str != NULL);

  if  (u16str == NULL){
    fprintf (stderr,
	     "u16_to_ko_euc error: u16str is NULL\n");
    return False;
  }
#if 0
  inbuffer = (unsigned char *) calloc (2 * (_utfchar_length (u16str) + 1), 1);
#endif
  utflen = _utfchar_length (u16str);
  memset (inbuffer, 0, sizeof (inbuffer));
  memset (outbuffer, 0, sizeof (outbuffer));
  memcpy (inbuffer, u16str, sizeof (UTFCHAR ) * (utflen + 1));
  inptr = inbuffer;
  inlen = sizeof (UTFCHAR ) * utflen;
  
#if 0
  inlen = 2 * (_utfchar_length (u16str) + 1);
  outbuffer = (unsigned char *) calloc (2 * (_utfchar_length (u16str) + 1), 1);
#endif
  
  outptr = outbuffer;
  outlen = sizeof (outbuffer);
  
  cd = iconv_open ("EUC-KR", "UTF16");
  if (cd == (iconv_t) -1) {
    fprintf (stderr, "u16_to_ko_euc: iconv_open error\n");
    return False;
  }
  
  ret = iconv (cd,
	       (const char **)&inptr, &inlen,
	       (char **)&outptr, &outlen);
  
  if (ret == (size_t) -1){
    fprintf (stderr, "u16_to_ko_euc error: iconv error\n");
    if ((iconv_t)cd)
      iconv_close (cd);
    
    return False;
    
  } else {
    
    if (euc_return){
      memcpy (*euc_return, outbuffer,
	      2 * (_utfchar_length (u16str) + 1) - outlen);
      *(*euc_return + (2 * (_utfchar_length (u16str) + 1) - outlen + 1)) = '\0';
    }
    iconv_close (cd);
    return True;
  }
}

static Bool
do_search_candidates (IMEBuffer imebuffer)
{
  int n_candidates;
  UTFCHAR **candidate_list;
  
  int n_result;
  UTFCHAR **result_list;
  LookupBufferStruct **lub;  
  Bool mthd_return;
  int i;
  HANGULBuffer *hangul_buffer = &imebuffer->composer_buffer;
  lub = &(imebuffer->lookup_buffer);
  mthd_return = composer_hangul_hanja_lookup_start (hangul_buffer,
						    &n_candidates,
						    &candidate_list);
  if ((mthd_return == False) || (n_candidates == 0)){
    return False;
  } else {
    if (imebuffer->config.charset == IN_UNICODE){
      *lub = 
	hangul_lookupbuf_new (n_candidates,
			      default_number_of_item_per_lookup_page,
			      candidate_list);
      
      for (i = 0 ; i < n_candidates; i++)
	free (candidate_list[i]);
      free (candidate_list);
      
      imebuffer->input_mode = INPUT_MODE_HANJA;
      imebuffer->what_to_do.do_lookup = True;
      imebuffer->what_to_do.do_preedit = True;
	    
      return True;
    } else { /* I need to return euc candidates only */

      result_list = NULL;
      for (i = 0, n_result = 0 ; i < n_candidates; i++){
	if (u16_to_ko_euc (candidate_list[i],NULL)){
	  n_result++;
	  result_list = realloc (result_list, sizeof (UTFCHAR *) * n_result);
	  result_list[n_result - 1] = _utfchar_dup (candidate_list [i]);
	}
      }

      if (!n_result)
	return False;
      *lub = 
	hangul_lookupbuf_new (n_result,
			      default_number_of_item_per_lookup_page,
			      result_list);

      for (i = 0 ; i < n_candidates; i++)
	free (candidate_list[i]);
      free (candidate_list);

      for (i = 0; i < n_result; i++)
	free (result_list[i]);
      free (result_list);
      return True;
    }
  }
}

/* these methods are not meant to be called from inside */

static INPUT_COMMAND_TYPE_H
hangul_buffer_get_keyevent_type (IMEBuffer a_buffer, IMEKeyEventStruct *key_event)
{
  int keycode, keystatus, keychar;
  INPUT_MODE_T input_mode;
  Bool method_result;
  UTFCHAR *ubuf;

  assert (a_buffer != NULL);

  keycode = key_event->key_code;
  keychar = key_event->key_char;
  keystatus = key_event->key_modifier;

  input_mode = a_buffer->input_mode;

  logf ("keycode: %d, keychar: %d, status: %d\n",
	keycode, keychar, keystatus);

  if (keystatus == IM_CTRL_MASK && keycode == IM_VK_SPACE){
    switch (a_buffer->input_mode){
    case INPUT_MODE_HANGUL:
      return INPUT_COMMAND_TURN_OFF_CONVERSION;
      break;
    case INPUT_MODE_ENGLISH:
      return INPUT_COMMAND_TURN_ON_CONVERSION;
      break;
    default:
      return INPUT_COMMAND_TURN_OFF_CONVERSION;
    }
  }
  if (input_mode == INPUT_MODE_HANGUL){
    switch (keycode){

    case IM_VK_ENTER:
    case IM_VK_TAB:
      return INPUT_COMMAND_COMMIT;
      break;
      
    case IM_VK_SPACE:
      if (keystatus & IM_SHIFT_MASK)
	return INPUT_COMMAND_TOGGLE_FULL_HALF_WIDTH;
      else
	return INPUT_COMMAND_COMMIT;
      break;
      
    case IM_VK_F2:
      if (keystatus & IM_SHIFT_MASK)
	return INPUT_COMMAND_SWITCH_KEYBOARD_LAYOUT;
      else
	return INPUT_COMMAND_IGNORE;
      break;
    case IM_VK_F3:
      if (keystatus & IM_SHIFT_MASK)
	return INPUT_COMMAND_SWITCH_COMMIT_METHOD;
      else
	return INPUT_COMMAND_IGNORE;
      break;
      
    case IM_VK_F7:
      if (keystatus & IM_SHIFT_MASK)
	return INPUT_COMMAND_SWITCH_CODESET;
      else
	return INPUT_COMMAND_IGNORE;
      break;
      
    case IM_VK_F8:
      return INPUT_COMMAND_SYMBOL_MENU_LOOKUP;
      break;
      
    case IM_VK_F9:
      if (keystatus & IM_SHIFT_MASK)
	return INPUT_COMMAND_HANJA_DO_LOOKUP;
      else
	return INPUT_COMMAND_IGNORE;
      break;
      


    case IM_VK_F1:
    case IM_VK_F4:  case IM_VK_F5:  case IM_VK_F6:
    case IM_VK_F10: case IM_VK_F11: case IM_VK_F12:
      return INPUT_COMMAND_IGNORE;
      break;

    case IM_VK_LEFT:
    case IM_VK_UP:
    case IM_VK_RIGHT:
    case IM_VK_DOWN:
    case IM_VK_PAGE_DOWN:
    case IM_VK_PAGE_UP:
    case IM_VK_HOME:
    case IM_VK_END:
      if (hangul_imebuffer_get_preedit (a_buffer, &ubuf))
	{
	  if (ubuf == NULL)
	    return INPUT_COMMAND_NOTHING;
	  else
	    return INPUT_COMMAND_COMMIT;
	}
      break;
      
    default:
      return INPUT_COMMAND_REGULAR_HANGUL;
      break;
    }
  } else if (input_mode == INPUT_MODE_HANJA) {
    switch (keycode){
    case IM_VK_PAGE_DOWN:
      return INPUT_COMMAND_LOOKUP_DO_NEXT_PAGE;
    case IM_VK_PAGE_UP:
      return INPUT_COMMAND_LOOKUP_DO_PREVIOUS_PAGE;
    case IM_VK_SPACE:
      return INPUT_COMMAND_LOOKUP_DO_SCROLL;
    case IM_VK_ENTER:
      return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
    case IM_VK_0: case IM_VK_1: case IM_VK_2:
    case IM_VK_3: case IM_VK_4: case IM_VK_5:
    case IM_VK_6: case IM_VK_7: case IM_VK_8:
    case IM_VK_9:
      method_result = hangul_buffer_lookup_selection_is_valid (a_buffer, key_event);
      if (method_result == True)
	return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
      else
	return INPUT_COMMAND_NOTHING;
    case IM_VK_BACK_SPACE:
    case IM_VK_ESCAPE:
      return INPUT_COMMAND_CANCEL_LOOKUP;
      break;
    default:
      return INPUT_COMMAND_IGNORE;
    }
  } else if (input_mode == INPUT_MODE_SYMBOL_MENU){
    
    switch (keycode) {
    case IM_VK_PAGE_DOWN:
      return INPUT_COMMAND_LOOKUP_DO_NEXT_PAGE;
    case IM_VK_PAGE_UP:
      return INPUT_COMMAND_LOOKUP_DO_PREVIOUS_PAGE;
    case IM_VK_SPACE:
      return INPUT_COMMAND_LOOKUP_DO_SCROLL;
    case IM_VK_ENTER:
      return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
    case IM_VK_0: case IM_VK_1: case IM_VK_2:
    case IM_VK_3: case IM_VK_4: case IM_VK_5:
    case IM_VK_6: case IM_VK_7: case IM_VK_8:
    
      method_result =
	hangul_buffer_lookup_selection_is_valid (a_buffer, key_event);
      if (method_result == True)
	return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
      else
	return INPUT_COMMAND_NOTHING;
    case IM_VK_BACK_SPACE:
    case IM_VK_ESCAPE:
      return INPUT_COMMAND_CANCEL_LOOKUP;
      break;
    default:
      return INPUT_COMMAND_NOTHING;

    }
  } else if (input_mode == INPUT_MODE_SYMBOL){
    switch (keycode) {
    case IM_VK_PAGE_DOWN:
      return INPUT_COMMAND_LOOKUP_DO_NEXT_PAGE;
    case IM_VK_PAGE_UP:
      return INPUT_COMMAND_LOOKUP_DO_PREVIOUS_PAGE;
    case IM_VK_SPACE:
      return INPUT_COMMAND_LOOKUP_DO_SCROLL;
    case IM_VK_ENTER:
      return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
    case IM_VK_0: case IM_VK_1: case IM_VK_2:
    case IM_VK_3: case IM_VK_4: case IM_VK_5:
    case IM_VK_6: case IM_VK_7: case IM_VK_8:
    case IM_VK_9:
    
      method_result =
	hangul_buffer_lookup_selection_is_valid (a_buffer, key_event);
      if (method_result == True)
	return INPUT_COMMAND_LOOKUP_SELECT_CANDIDATE;
      else
	return INPUT_COMMAND_NOTHING;
    case IM_VK_BACK_SPACE:
    case IM_VK_ESCAPE:
      return INPUT_COMMAND_CANCEL_LOOKUP;
      break;
    default:
      return INPUT_COMMAND_NOTHING;

    }
  }
  return INPUT_COMMAND_NOTHING;
}

static Bool
hangul_buffer_lookup_selection_is_valid (IMEBuffer a_buffer,
					 IMEKeyEventStruct *key_event)
{
  LookupBufferStruct *lub;
  Bool method_result;
  int n_count;

  assert (a_buffer != NULL);
  lub = a_buffer->lookup_buffer;
  n_count =
    hangul_lookupbuf_get_count_of_candidates_in_current_page (lub);
  
  
  if (n_count == -1)
    return False;
  else {
    if (key_event->key_code >= IM_VK_0 && 
	key_event->key_code < (IM_VK_0 + n_count) )
      return True;
    else
      return False;
  }
  return True;
}

static Bool
hangul_imebuffer_get_configuration (IMEBuffer imebuffer, int *count, KOLE_config ***pconfig)
{
  int i;
  *count = g_n_config;
  *pconfig = (KOLE_config **) calloc (*count, sizeof (KOLE_config *));
  for (i = 0; i < *count; i++)
    (*pconfig)[i] = leoption_copy (g_engine_config [i]);
  
  return True;
}

static void
node_print (void *data)
{
  HHItem *p = (HHItem *) data;
  hhitem_print_string (p, NULL);
  printf ("\n");
  return;
}

static Bool
apply_kole_config_to_session (int n, KOLE_config **pconfig, Config *sconf)
{
  int i;
  assert (sconf != NULL);
  if (sconf == NULL){
    fprintf (stderr,
	     "apply_kole_config_to_session error: sconf is NULL\n");
    return False;
  }
  for (i = 0; i < n; i++){
    switch (pconfig[i]->type){
    case KOLE_OPTION_TYPE_KEYBOARD:
      sconf->keyboard = pconfig[i]->v.kbd;
      break;
    case KOLE_OPTION_TYPE_CHARSET:
      sconf->charset = pconfig[i]->v.cset;
      break;
    case KOLE_OPTION_TYPE_DELETION:
      sconf->deletion = pconfig[i]->v.del;
      break;
    case KOLE_OPTION_TYPE_CHARBASED_COMMIT:
      sconf->commit_mode = pconfig[i]->v.commit_by;
      break;
    }
  }
  return True;
}

static void
hangul_engine_print_configuration ()
{
  int i;
  for (i = 0; i < g_n_config; i++){
    leoption_debug_print (g_engine_config[i]);
  }
}

#if 0
static void
hangul_buffer_debug_print (IMEBuffer a_buffer)
{
  printf ("session id: %d\n", a_buffer->session_id);
  printf ("engine  id :%d\n", a_buffer->engine_id);
  switch (a_buffer->input_mode){
  case INPUT_MODE_NONE:
    printf ("input mode : NONE");
    
    break;
  case INPUT_MODE_ENGLISH:
    printf ("input mode : ENGLISH");
    break;
  case INPUT_MODE_HANGUL:
    printf ("input mode : HANGUL");
    break;
  case INPUT_MODE_HANJA:
    printf ("input mode : HANJA");
    break;
  case INPUT_MODE_HEX_EUC:
    printf ("input mode : EUC");
    break;
  case INPUT_MODE_HEX_UTF8:
    printf ("input mode : UTF8");
    break;
  case INPUT_MODE_SYMBOL:
    printf ("input mode : SYMBOL");
    break;
  default:
    printf ("strange input mode value: %d\n", a_buffer->input_mode);
    break;
  }
}

#endif
