/*
 * 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 <string.h>
#include <stdio.h>
#include <assert.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <IMProtocolStruct.h>
#include <iconv.h>

#include "composer.h"
#include "hangul.h"
#include "kolelog.h"
#include "logf.h"

typedef struct _CoupledJamoInfo {
  UTFCHAR first;
  UTFCHAR second;
  UTFCHAR coupled;
} CoupledJamoInfo;

static HANGUL *composer_hangul_new (void);
static void composer_hangul_buffer_free (HANGULBuffer *hangul_buffer);

static HANGUL *
composer_hangul_buffer_get_first_hangul (HANGULBuffer *hangul_buffer);

static Bool
composer_hangul_remove_last_hangul (HANGULBuffer *hangul_buffer, HANGUL *deleted_hangul_return);

static Bool
composer_hangul_convert_input_to_unicode (int keyChar, int state, KEYBOARD_LAYOUT_T layout_id, MapInfo *map_info);


/* Jamo convenience */
static void _jamo_copy (Jamo *dst, Jamo *src);
/* static Bool _jamo_is_clean (Jamo *jamo); */
static Bool _jamo_initialize (Jamo *jamo);
/* static UTFCHAR _jamo_get_value (Jamo *jamo); */
static UTFCHAR _jamo_get_first (Jamo *jamo);
static UTFCHAR _jamo_get_second (Jamo *jamo);
static int _jamo_set_values (Jamo *jamo, UTFCHAR first, UTFCHAR second);
static UTFCHAR composer_hangul_coupled_jamo (UTFCHAR first, UTFCHAR second);

/* character set range checking */
static Bool is_not_euc_with_trailing_consonant (HANGUL *hangul, UTFCHAR tc);


static HANGUL *
composer_hangul_new (void)
{
  HANGUL *a_hangul;
  a_hangul = (HANGUL *)calloc (1, sizeof (HANGUL) );

  if(!a_hangul)
    return NULL;
  else {
#undef L
#if 0    
    a_hangul->L = (UTFCHAR)0x0000;
    a_hangul->V = (UTFCHAR)0x0000;
    a_hangul->T = (UTFCHAR)0x0000;
#endif

    memset (a_hangul, 0, sizeof (HANGUL));
    
    a_hangul->completed = False;
    return a_hangul;
  }
}

void
composer_hangul_initialize (HANGUL *a_hangul)
{
  assert (a_hangul != NULL);
#if 0
  a_hangul->L = 0x0000;
  a_hangul->V = 0x0000;
  a_hangul->T = 0x0000;
#endif
  memset (a_hangul, 0, sizeof (HANGUL));
  a_hangul->completed = False;
}

void
composer_hangul_free (HANGUL *a_hangul)
{
  if(a_hangul)
    free(a_hangul);
}

static void
_jamo_copy (Jamo *dst, Jamo *src)
{
  assert (dst != NULL);
  assert (src != NULL);

  memcpy (dst, src, sizeof (Jamo));
}

Bool
_jamo_is_clean (Jamo *jamo)
{
  assert (jamo != NULL);

  return jamo->first ? False : True;
}

static Bool
_jamo_is_done (Jamo *jamo)
{
  assert (jamo != NULL);
  return jamo->second ? True : False;
}

static Bool
_jamo_initialize (Jamo *jamo)
{
  assert (jamo != NULL);

  memset (jamo, 0, sizeof (Jamo));
}

UTFCHAR
_jamo_get_value (Jamo *jamo)
{
  assert (jamo != NULL);
  return jamo->coupled;
}
static UTFCHAR
_jamo_get_first (Jamo *jamo)
{
  assert (jamo != NULL);
  return jamo->first;
}
     
static UTFCHAR
_jamo_get_second (Jamo *jamo)
{
  assert (jamo != NULL);
  return jamo->second;
}

/*
  _jamo_set_values basically returns coupling result,
  which is same as composer_hangul_coupled_jamo ().
  Thus,
  0 return means 'invalid coupling',
  !0 means 'valid coupling'
*/

static int
_jamo_set_values (Jamo *jamo, UTFCHAR first, UTFCHAR second)
{
  UTFCHAR coupled_result;

  assert (jamo != NULL);

  if (first == 0x0000){
    jamo->first = jamo->second = jamo->coupled = 0x0000;
    return 0;
    
  } else if (second == 0x0000){
    
    jamo->first = first;
    jamo->second = 0x000;
    jamo->coupled = first;
    
    return jamo->first;
    
  } else {
    
    coupled_result = composer_hangul_coupled_jamo (first, second);
    if (coupled_result){
      jamo->first = first;
      jamo->second = second;
      jamo->coupled = coupled_result;

      return jamo->coupled;
      
    } else
      return 0;
  }
}

void
composer_hangul_copy (HANGUL *dst, HANGUL *src)
{
  assert (dst != NULL);
  assert (src != NULL);
  
  if (!dst || !src) {
    logf("error in composer_hangul_copy\n");
    return;
  }
  _jamo_copy (&dst->L, &src->L);
  _jamo_copy (&dst->V, &src->V);
  _jamo_copy (&dst->T, &src->T);
  
  dst->completed = src->completed;
  return;
}

HANGULBuffer *
composer_hangul_buffer_new (void)
{
  HANGULBuffer *_hangul_buffer;
  _hangul_buffer = (HANGULBuffer *) calloc(1, sizeof(HANGULBuffer));
	
  if(!_hangul_buffer)
    return NULL;
  else
    return _hangul_buffer;
}

void
composer_hangul_buffer_initialize (HANGULBuffer *hangul_buffer)
{
  assert (hangul_buffer != NULL);

  composer_hangul_buffer_free (hangul_buffer);

  hangul_buffer->count = 0;	
  hangul_buffer->buffer = NULL;
}

static void
composer_hangul_buffer_free (HANGULBuffer *hangul_buffer)
{
  int i;

  assert (hangul_buffer != NULL);
  if(hangul_buffer){
    for ( i = 0 ; i < hangul_buffer->count; i++)
      free (hangul_buffer->buffer[i]);
  }
  free (hangul_buffer->buffer);
  return;
}


static HANGUL *
composer_hangul_buffer_get_first_hangul (HANGULBuffer *hangul_buffer)
{
  assert (hangul_buffer != NULL );
  if (hangul_buffer == NULL) {
    KOLE_LOG (LOGDEST_STDERR, "hangul_buufer_get_first_hangul error: hangul_buffer can't be NULL\n");
    return NULL;
  } 
  else if (hangul_buffer->count == 0 ){
    KOLE_LOG (LOGDEST_STDERR, "hangl_buffer_get_first_hangul : there's no hangl character now\n");
    return NULL;
  } 
  else {
    return hangul_buffer->buffer[0];
  }
}


HANGUL *
composer_hangul_buffer_get_next_hangul (HANGULBuffer *hangul_buffer, HANGUL *current_hangul)
{
  HANGUL *next_hangul;

  assert (hangul_buffer != NULL);
  if (hangul_buffer == NULL) {
    KOLE_LOG (LOGDEST_STDERR, "hangul_buffer_get_next_hangul error: hangul_buffer can't be NULL\n");
    return NULL;
  } 
  next_hangul = current_hangul + 1;
}

int
composer_hangul_buffer_get_hangul_length (HANGULBuffer *hangul_buffer)
{
  assert (hangul_buffer != NULL);
  if (hangul_buffer == NULL) {
    KOLE_LOG (LOGDEST_STDERR, "hangul_buffer_get_hangul_length: hangul_buffer is null\n");
    return -1;
  }
  if (hangul_buffer->count < 0) {
    KOLE_LOG (LOGDEST_STDERR, "hangul_buffer_get_hangul_length: count is < 0\n");
    hangul_buffer->count = 0;
    return 0;
  } else 
    return hangul_buffer->count;
}

void
composer_hangul_buffer_print (HANGULBuffer *hangul_buffer)
{
  int i;
  HANGUL *han;
  UTFCHAR hangul_character;
  KOLE_LOG (LOGDEST_STDOUT, "===this values are pseudo===\n");
  for(i = 0 ; i < hangul_buffer->count; i++){
    han = hangul_buffer->buffer[i];
#if 0
    KOLE_LOG (LOGDEST_STDOUT, "(0x%04X+0x%04X+0x%04X=>0x%04X):", 
	    han->L, han->V, han->T,
	    hangul_character = composer_hangul_combine_cho_jung_jong (han));
#endif

    KOLE_LOG (LOGDEST_STDOUT, "(0x%04X+0x%04X+0x%04X=>0x%04X):", 
	     han->L.coupled, han->V.coupled, han->T.coupled,
	     hangul_character = composer_hangul_combine_cho_jung_jong (han));
	     
  }
  KOLE_LOG (LOGDEST_STDOUT, "===this values are pseudo===\n");
  KOLE_LOG (LOGDEST_STDOUT, "\n");
}

/* delete_hangul_return may be NULL if you don't care about
   getting the copy of deleted HANGUL
*/
Bool
composer_hangul_buffer_delete_hangul_character (HANGULBuffer *hangul_buffer, 
						Bool delete_whole_character,
						HANGUL *deleted_hangul_return)
{
  int count;
  HANGULList *tmp_array;
  HANGUL *last_hangul;
  Bool remove_return;

  assert (hangul_buffer != NULL);
  count = hangul_buffer->count;
  assert (count >= 0);
  if (count < 0){
    KOLE_LOG (LOGDEST_STDERR, "count of HANGULBuffer can't be less than 0\n");
    return False;
  } else if (count == 0){
    /* Nothing to return */
    if (deleted_hangul_return){
      memcpy (deleted_hangul_return, 0, sizeof (HANGUL));
    }
    return True;
  }
  last_hangul = hangul_buffer->buffer [count - 1];
  assert (last_hangul != NULL);

  /* create a return copy here */
  if (deleted_hangul_return != NULL)
    composer_hangul_copy (deleted_hangul_return, last_hangul);

  /*
    last hangul is incomplete character, thus likely(should be)
    last character, thus I delete it ja so unit.
  */
  if (last_hangul->completed != True){
    /* I need to think about doubled jongseong too ... */
    /* There was jong seong pending */
    
    if (!_jamo_is_clean (&last_hangul->T)){
      _jamo_initialize (&last_hangul->T);
    } else if (!_jamo_is_clean (&last_hangul->V) &&
	       _jamo_get_value (&last_hangul->V) != JONGSEONG_FILLER) {
      /*
	There was jung seong pending
      */
      if (_jamo_get_second (&last_hangul->V)){
	_jamo_set_values (&last_hangul->V,
			 _jamo_get_first (&last_hangul->V),
			 0x00);
      } else
	_jamo_initialize (&last_hangul->V);
      
    } else {
      /*
	There was only choseong in the buffer,
	thus remove the entire character
      */
      remove_return =
	composer_hangul_remove_last_hangul (hangul_buffer,
					    deleted_hangul_return);
      if (remove_return == False)
	{
	  KOLE_LOG (LOGDEST_STDERR,
		   "composer_hangul_buffer_delete_hangul_character error:\n\t"
		   "composer_hangul_remove_last_hangul failed\n");
	  return False;
	}
    }
	  
  } else {
    /*
      I am begining to delete already completed character,
      not the current character being composed.
    */
    if (delete_whole_character != True){
      /* I need to think about doubled jongseong too ...*/
      
      /* There was jong seong pending */

      if (!_jamo_is_clean (&last_hangul->T)){
	_jamo_initialize (&last_hangul->T);
	
      } else if (!_jamo_is_clean (&last_hangul->V) &&
		 _jamo_get_value (&last_hangul->V) != JONGSEONG_FILLER){
	/* There was jung seong pending */
	_jamo_initialize (&last_hangul->V);
      } else {
	/*
	  There was only choseong in the buffer,
	  thus remove the entire character
	*/
      
	remove_return =
	  composer_hangul_remove_last_hangul (hangul_buffer,
					      deleted_hangul_return);
	if (remove_return == False){
	  KOLE_LOG (LOGDEST_STDERR,
		   "composer_hangul_buffer_delete_hangul_character error: \n\t"
		   "composer_hangul_remove_last_hangul failed\n");
	  return False;
	}
      }
    }

    else { /* delete_whole_character == True */
	
      remove_return =
	composer_hangul_remove_last_hangul (hangul_buffer,
					    deleted_hangul_return);
      if (remove_return == False){
	KOLE_LOG (LOGDEST_STDERR,
		 "composer_hangul_buffer_delete_hangul_character error:\n\t"
		 "composer_hangul_remove_last_hangul failed\n");
	return False;
      }
	
    } /* if (delete_whole_character != True) */
  }
  return True;
}

static Bool
composer_hangul_remove_last_hangul (HANGULBuffer *hangul_buffer,
				    HANGUL *deleted_hangul_return)
{
  int hangul_count = hangul_buffer->count;
  HANGUL *last_hangul = hangul_buffer->buffer [hangul_count - 1];
  HANGULList *tmp_list;

  assert (hangul_buffer != NULL);
  if (hangul_buffer == NULL) {
    KOLE_LOG (LOGDEST_STDERR, "composer_hangul_remove_last_hangul error: hangul_buffer was null \n");
    return False;
  }
  assert (last_hangul != NULL);
  if (last_hangul == NULL) {
    KOLE_LOG (LOGDEST_STDERR, "composer_hangul_remove_last_hangul error: last_hangul was null\n");
    return False;
  }
  assert (hangul_count > 0);
  if (hangul_count == 0){
    KOLE_LOG (LOGDEST_STDERR, "composer_hangul_remove_last_hangul error: hangul_count was 0\n");
    return False;
  }

  if (deleted_hangul_return != NULL) {
    composer_hangul_copy (deleted_hangul_return, last_hangul);
  }

  composer_hangul_free (last_hangul);

  if (hangul_count == 1) {
    tmp_list = NULL;
    free (hangul_buffer->buffer);
    hangul_buffer->buffer = NULL;
    hangul_buffer->count = 0;

  } else {

    tmp_list = (HANGULList *) calloc (hangul_count - 1, sizeof (HANGUL *));

    assert (tmp_list != NULL);
    if (tmp_list == NULL) {
      KOLE_LOG (LOGDEST_STDERR, "composer_hangul_remove_last_hangul error: calloc failed\n");
      return False;
    }

    memcpy (tmp_list, hangul_buffer->buffer, (hangul_count - 1) * sizeof (HANGUL *));
    free (hangul_buffer->buffer);
    hangul_buffer->buffer = tmp_list;
    hangul_buffer->count = hangul_count - 1;
  }
  return True;
}

/* create a copy of HANGUL a_hangul and append it to the array */
HANGUL *
composer_hangul_buffer_add_hangul (HANGULBuffer *hangul_buffer, HANGUL *a_hangul)
{
  HANGULList *tmp;
  HANGUL *hangul = composer_hangul_new ();
	
  assert (hangul != NULL);

  if (hangul == NULL){
    logf("Error creating new hangul");
    return NULL;
  } else {
    tmp = (HANGULList *) calloc(hangul_buffer->count+ 1,sizeof(HANGULList));
    assert(tmp != NULL);
    if(tmp == NULL){
      KOLE_LOG (LOGDEST_STDERR, "Fatal error in composer_hangul_buffer_add_hangul\n");
      exit(-1);
    }
    if (hangul_buffer->count == 0){
      hangul_buffer->count++;
      hangul_buffer->buffer = (HANGULList *)calloc(1, sizeof(HANGULList));
      hangul_buffer->buffer[0] = hangul;
      composer_hangul_copy(hangul_buffer->buffer[0], a_hangul);
      return hangul_buffer->buffer[0];
    }
    else {
      memset(tmp, 0, (hangul_buffer->count + 1) * sizeof(HANGULList));
      memcpy(tmp, hangul_buffer->buffer, (hangul_buffer->count) * sizeof(HANGULList));
      tmp[hangul_buffer->count] = hangul;
      composer_hangul_copy(tmp[hangul_buffer->count], a_hangul);
      /* FIX ME */
      free (hangul_buffer->buffer);
      /* END OF FIX ME */
      hangul_buffer->buffer = tmp;
      hangul_buffer->count += 1;	
      return tmp[hangul_buffer->count - 1];
    }
  }
}

HANGUL *
composer_hangul_buffer_pop_hangul (HANGULBuffer *hangul_buffer)
{
  HANGUL *phangul;
  HANGULBuffer *tmp;
  int i;
  assert (hangul_buffer != NULL);

  if (hangul_buffer == NULL){
    KOLE_LOG (LOGDEST_STDERR, "%s",
	      "composer_hangul_buffer_pop_hangul error");
    return NULL;
  }
  if (hangul_buffer->count == 0){
    KOLE_LOG (LOGDEST_STDERR, "%s",
	      "composer_hangul_buffer_pop_hangul error: no hangul in buffer");
    return NULL;
  }

  tmp = (HANGULBuffer *) calloc (hangul_buffer->count - 1,
				 sizeof (HANGUL *));

  phangul = hangul_buffer->buffer[0];
  
  memcpy (tmp, hangul_buffer->buffer + 1,
	  sizeof (HANGUL *) * hangul_buffer->count - 1);
  free (hangul_buffer->buffer);
  hangul_buffer->buffer = tmp;
  hangul_buffer->count--;

  return phangul;
}

static JAMO_T
composer_jamo_type_of_input(UTFCHAR input_char)
{
  if (input_char < 0x1100 || input_char > 0x11F9)
    return JM_NONE;
  else 
    {
      if (input_char >= 0x1100 && input_char <= 0x1112)
	return JAEUM;
      else if (input_char >= 0x1161 && input_char <= 0x1175)
	return MOEUM;
      else
	return JM_NONE;
    }
}

static Bool
is_not_euc_with_trailing_consonant (HANGUL *hangul, UTFCHAR tc)
{
  HANGUL my_hangul;
  iconv_t cd;
  size_t conv_ret;
  
  char inbuf[2];
  char *inptr = inbuf;
  size_t inlen = 2;

  char outbuf[2];
  char *outptr = outbuf;
  size_t outlen = 2;
  
  UTFCHAR u16_hangul;

  composer_hangul_copy (&my_hangul, hangul);
  if (_jamo_is_done (&my_hangul.T))
    return False;

  if (_jamo_is_clean (&my_hangul.T))
    _jamo_set_values (&my_hangul.T, tc, 0);
  else
    _jamo_set_values (&my_hangul.T, my_hangul.T.first, tc);

  
  u16_hangul = composer_hangul_combine_cho_jung_jong (&my_hangul);
  
  cd = iconv_open ("EUC-KR", "UTF16");
  memset (inbuf, 0, sizeof (inbuf));
  memset (inbuf, 0, sizeof (outbuf));
  memcpy (inbuf, &u16_hangul, sizeof (unsigned short));
  
  conv_ret = iconv (cd, &inptr, &inlen, &outptr, &outlen);
  iconv_close (cd);
  
  if (conv_ret == (size_t)-1)
    return True;
  else
    return False;
  
}

static Bool
is_not_euc_with_vowel (HANGUL *hangul, UTFCHAR vowel)
{
  HANGUL my_hangul;
  iconv_t cd;
  size_t conv_ret;
  
  char inbuf[2];
  char *inptr = inbuf;
  size_t inlen = 2;

  char outbuf[2];
  char *outptr = outbuf;
  size_t outlen = 2;
  
  UTFCHAR u16_hangul;

  composer_hangul_copy (&my_hangul, hangul);
  if (_jamo_is_done (&my_hangul.V))
    return False;

  if (_jamo_is_clean (&my_hangul.V))
    _jamo_set_values (&my_hangul.V, vowel, 0);
  else
    _jamo_set_values (&my_hangul.V, my_hangul.V.first, vowel);

  
  u16_hangul = composer_hangul_combine_cho_jung_jong (&my_hangul);
  
  cd = iconv_open ("EUC-KR", "UTF16");
  memset (inbuf, 0, sizeof (inbuf));
  memset (inbuf, 0, sizeof (outbuf));
  memcpy (inbuf, &u16_hangul, sizeof (unsigned short));
  
  conv_ret = iconv (cd, &inptr, &inlen, &outptr, &outlen);
  iconv_close (cd);
  
  if (conv_ret == (size_t)-1)
    return True;
  else
    return False;
  
}

/* NULL return indicates either
   hangul_buffer was not valid
   or
   there was not HANGUL element in the buffer
*/

HANGUL *
composer_hangul_buffer_get_last_hangul (HANGULBuffer *hangul_buffer)
{
  HANGUL *last_hangul;
  assert(hangul_buffer != NULL);

  if ( hangul_buffer->count == 0 )
    return NULL;

  last_hangul = hangul_buffer->buffer[hangul_buffer->count - 1];
  return last_hangul;
}


/****************************************************
	For detailed information on this algorithim,
	See Unicode Standard 3.0,
		3.11 Conjoining Jamo Behavior

	return =  0: failure
			!=  0: success
****************************************************/

UTFCHAR
composer_hangul_combine_cho_jung_jong (HANGUL *a_hangul)
{
  static UTFCHAR hangul_base    = 0xac00;
  static UTFCHAR choseong_base  = 0x1100;
  static UTFCHAR jungseong_base = 0x1161;
  static UTFCHAR jongseong_base = 0x11a7;

  static int number_of_jungseong = 21;
  static int number_of_jongseong = 28;

  UTFCHAR l_consonant,vowel,t_consonant;
  UTFCHAR ch;

  assert (a_hangul != NULL);
  if (a_hangul == NULL)
    return 0;

  l_consonant = _jamo_get_value (&a_hangul->L);
  vowel = _jamo_get_value (&a_hangul->V);
  t_consonant = _jamo_get_value (&a_hangul->T);
  	
  if (t_consonant == 0)
    t_consonant = JONGSEONG_FILLER; /* 0x11A7 */
  	
  if (l_consonant  < 0x1100 || l_consonant  > 0x1112)
    return 0;
  if (vowel < 0x1161 || vowel > 0x1175)
    return 0;
  if (t_consonant < 0x11a7 || t_consonant > 0x11c2)
    return 0;
  	
  l_consonant  -= choseong_base;
  vowel -= jungseong_base;
  t_consonant -= jongseong_base;
  	
  ch = ((l_consonant * number_of_jungseong) + vowel) * number_of_jongseong + 
    t_consonant + hangul_base;
  return ch;
}

/*
  If non-printable keycodes like IM_VK_HOME, etc is passed,
  the return is 0x0000, signalling the caller that extra
  action needs to be taken depending on the sementics of the
  non-printable keycode, for example, moving the cursor to the home.
 */
UTFCHAR
get_utf_input_by_keyboard (IMEKeyEventStruct *iiim_key_ev,
			    KEYBOARD_LAYOUT_T i_keyboard)
{
  UTFCHAR utf_input  = 0x0000;
  Bool keymap_return;
  int keychar;
  int keycode;
  int state;
  MapInfo map_info;

  assert (iiim_key_ev != NULL);

  keycode = iiim_key_ev->key_code;
  keychar = iiim_key_ev->key_char;
  state = iiim_key_ev->key_modifier;

  /* Below case is cheked, before keyboard mapping lookup is done.
     This is becuase thos mapping table doesn't consider
     mapping entry for below values, and actually this is not
     keyboard mapping dependent input
  */
  switch (keycode){
  case IM_VK_ENTER:
  case IM_VK_TAB:
  case IM_VK_SPACE:
    return (UTFCHAR )keycode;
    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:
    return 0x0000;
    break;
  default:
    break;
  }

  if (keycode < 0x41){
    keymap_return = composer_hangul_convert_input_to_unicode (keycode,
							      state,
							      i_keyboard,
							      &map_info);
  } else {
    keymap_return = composer_hangul_convert_input_to_unicode (keychar,
							      state,
							      i_keyboard,
							      &map_info);
  }
  if (keymap_return)
    utf_input = map_info.val;
  else {
    utf_input = 0x0000;
  }
  
  return utf_input;
}


/*
  parameters: 

  return:
  return -1 indicates input character was not valid, thus ignore
  return -2 indicates input character was not valid, but should commit

  $@2beolsik@$
*/



int
composer_hangul_automata_2pair (int keyCode, int keyChar, int state,
				HANGULBuffer *hangul_buffer, Config *pconfig)
{
  UTFCHAR utf_input;
  HANGUL *last_hangul;
  HANGUL a_hangul;
  HANGUL *new_hangul;
  
  JAMO_T input_type_is;
  
  KEYBOARD_LAYOUT_T i_keyboard = LAYOUT_2PART;
  
  UTFCHAR jongseong_from_choseong, choseong_from_jongseong;
  UTFCHAR decomposed_jongseong, decomposed_choseong;

  UTFCHAR coupled_jaeum, coupled_moeum;
  Bool last_hangul_was_newly_created = False;
  Bool keymap_return;
  MapInfo map_info;

  assert (hangul_buffer != NULL);

  keymap_return = composer_hangul_convert_input_to_unicode (keyChar,
							    state,
							    i_keyboard,
							    &map_info);
  if (keymap_return)
    utf_input = map_info.val;
  else
    
    return ComposerRetErr;

  input_type_is = composer_jamo_type_of_input (utf_input);
  if (input_type_is == JM_NONE){     /* not a hangul character */
    if (utf_input >= 0x0020 && utf_input <= 0x007f){
      /* printable commit characters */
      return ComposerRetBreak;
    } else
      return ComposerRetErr;
  }


  last_hangul = composer_hangul_buffer_get_last_hangul (hangul_buffer);

  composer_hangul_initialize (&a_hangul);

  if (last_hangul == NULL){
    /* there was no HANGUL, so create the first one */
    last_hangul = composer_hangul_new ();
    last_hangul_was_newly_created = True;

    if (input_type_is == JAEUM){
      _jamo_set_values (&last_hangul->L, utf_input, 0x0);
    } else {
      _jamo_set_values (&last_hangul->L, 0x0, 0x0);
      _jamo_set_values (&last_hangul->V, utf_input, 0);
      _jamo_set_values (&last_hangul->T, 0x0, 0x0);
      
      last_hangul->completed = True;
    }

    composer_hangul_buffer_add_hangul (hangul_buffer, last_hangul); 
    composer_hangul_free (last_hangul);
	
    return ComposerRetKeep;
  }

  if (input_type_is == JAEUM) {
    if (last_hangul->completed == True){
      new_hangul = composer_hangul_new ();
      _jamo_set_values (&new_hangul->L, utf_input, 0x0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
      return ComposerRetKeep;
      
    } else if (!_jamo_is_clean (&last_hangul->T)) {
      /* last_hangul had T */
      jongseong_from_choseong =
	hangul_combining_chosung_to_combining_jongsung (utf_input);
      if ((!_jamo_is_done (&last_hangul->T)) &&
	  (IS_VALID_JONGSEONG (jongseong_from_choseong))){

	coupled_jaeum =
	  composer_hangul_coupled_jamo (last_hangul->T.first,
					jongseong_from_choseong);
	if (!coupled_jaeum){
	  /* invalid coupling */
	  _jamo_set_values (&a_hangul.L , utf_input, 0x0);
	  /* finished the previous character,
	     and starting new character */
	  last_hangul->completed = True;
	  composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);
	} else {
	  /* valid coupling */
	  if (pconfig->charset == IN_EUC &&
	      is_not_euc_with_trailing_consonant (last_hangul, jongseong_from_choseong)){

	    last_hangul->completed = True;
	    
	    new_hangul = composer_hangul_new ();
	    _jamo_set_values (&new_hangul->L, utf_input, 0x0);
	    composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	    composer_hangul_free (new_hangul);
	    
	  } else {
	    _jamo_set_values (&last_hangul->T,
			      last_hangul->T.first, jongseong_from_choseong);
	  }
	}
	
      } else {
	_jamo_set_values (&a_hangul.L , utf_input, 0x0);
	composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);
      }
      return ComposerRetKeep;

    } else if (!_jamo_is_clean (&last_hangul->V)) {
      jongseong_from_choseong =
	hangul_combining_chosung_to_combining_jongsung (utf_input);
      if (IS_VALID_JONGSEONG(jongseong_from_choseong) ){
	if (pconfig->charset == IN_EUC &&
	    is_not_euc_with_trailing_consonant (last_hangul, jongseong_from_choseong)){
	  last_hangul->completed = True;
	  new_hangul = composer_hangul_new ();
	  _jamo_set_values (&new_hangul->L, utf_input, 0x0);
	  composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	  composer_hangul_free (new_hangul);
	} else {
	  _jamo_set_values (&last_hangul->T, jongseong_from_choseong, 0x0);
	}
      }
      else {
	last_hangul->completed = True;
	_jamo_set_values (&a_hangul.L, utf_input, 0x0);
	composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);
      }
      return ComposerRetKeep;
    }
    else if (!_jamo_is_clean (&last_hangul->L)) {
      coupled_jaeum =
	composer_hangul_coupled_jamo (last_hangul->L.first, utf_input);
      if (!_jamo_is_done (&last_hangul->L) && coupled_jaeum){
	_jamo_set_values (&last_hangul->L,
			  last_hangul->L.first, utf_input);
      } else {
	_jamo_set_values (&last_hangul->V, 0x0, 0x0);
	_jamo_set_values (&last_hangul->T, 0x0, 0x0);
	last_hangul->completed = True;
	_jamo_set_values (&a_hangul.L, utf_input, 0);
	composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);	
      }
      return ComposerRetKeep;
    }
			
  } else if (input_type_is == MOEUM) {
    if (last_hangul->completed == True){

      new_hangul = composer_hangul_new ();
      _jamo_set_values (&new_hangul->L, 0x0, 0x0);
      _jamo_set_values (&new_hangul->V, utf_input, 0x0);
      _jamo_set_values (&new_hangul->T, 0x0, 0x0);
      new_hangul->completed = True;
      
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
      return ComposerRetKeep;
      
    } else if (!_jamo_is_clean (&last_hangul->T)){
      UTFCHAR tmp;
      if (!_jamo_is_done (&last_hangul->T)){
	/* last_hangul->T.first needs to be taken forward
	   to current new character */
	tmp =
	  hangul_combining_jongsung_to_combining_chosung
	  (_jamo_get_first (&last_hangul->T));
	_jamo_set_values (&last_hangul->T, 0x0, 0x0);
      } else {
	tmp =
	  hangul_combining_jongsung_to_combining_chosung (
							  _jamo_get_second (&last_hangul->T));
	_jamo_set_values (&last_hangul->T, last_hangul->T.first, 0x0);
      }
      last_hangul->completed = True;
      
      _jamo_set_values (&a_hangul.L, tmp, 0x0);
      _jamo_set_values (&a_hangul.V, utf_input, 0x0);
      composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);
      return ComposerRetKeep;
    }
    else if(!_jamo_is_clean (&last_hangul->V)) {
      coupled_moeum =
	composer_hangul_coupled_jamo (
				      _jamo_get_first (&last_hangul->V),
				      utf_input);
      if (!_jamo_is_done (&last_hangul->V) && coupled_moeum){
	if (pconfig->charset == IN_EUC &&
	    is_not_euc_with_vowel (last_hangul, utf_input)){
	  
	  last_hangul->completed = True;
	  new_hangul = composer_hangul_new ();
	  _jamo_set_values (&new_hangul->L, 0x0, 0x0);
	  _jamo_set_values (&new_hangul->V, utf_input, 0x0);
	  new_hangul->completed = True;
	  composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	  composer_hangul_free (new_hangul);

	} else {
	  _jamo_set_values (&last_hangul->V,
			    last_hangul->V.first, utf_input);
	}
      } else {
	last_hangul->completed = True;
	
	_jamo_set_values (&a_hangul.L, 0x0, 0x0);
	_jamo_set_values (&a_hangul.V, utf_input, 0x0);
	a_hangul.completed = True;
	
	composer_hangul_buffer_add_hangul (hangul_buffer, &a_hangul);
      }
      return ComposerRetKeep;
    } else if(!_jamo_is_clean(&last_hangul->L)){
      _jamo_set_values (&last_hangul->V, utf_input, 0x0);
      return ComposerRetKeep;
    }
  }
}

int
composer_hangul_automata_3pair_final (int keyCode, int keyChar, int state,
				      HANGULBuffer *hangul_buffer, Config *pconfig)
{
  UTFCHAR utf_input;
  HANGUL *last_hangul;
  HANGUL a_hangul;
  HANGUL *new_hangul;
  
  JAMO_T input_type_is;
  
  KEYBOARD_LAYOUT_T i_keyboard = LAYOUT_3PART_FINAL;
  
  UTFCHAR jongseong_from_choseong, choseong_from_jongseong;
  UTFCHAR decomposed_jongseong, decomposed_choseong;

  UTFCHAR coupled_jaeum, coupled_moeum;
  Bool last_hangul_was_newly_created = False;

  Bool keymap_return;
  MapInfo map_info;
  CHOJUNGJONG_T cjj;

  assert (hangul_buffer != NULL);

  keymap_return = composer_hangul_convert_input_to_unicode (keyCode,
							    state,
							    i_keyboard,
							    &map_info);
  
  if (keymap_return){
    utf_input = map_info.val;
    cjj = map_info.type;
  } else
    return -1;
  
  if (cjj == CJJ_NONE){
    KOLE_LOG (LOGDEST_STDERR, "composer_hangul_automata_3pair_final error: "
	     "cjj is CJJ_NONE\n");
    return -1;
  }

  last_hangul = composer_hangul_buffer_get_last_hangul (hangul_buffer);

  composer_hangul_initialize (&a_hangul);
    
  /* Create New buffer to do automata */
    
  if (last_hangul == NULL){
    /* there was no HANGUL, so create the first one */
    last_hangul = composer_hangul_new ();
    last_hangul_was_newly_created = True;

    if (cjj == CJJ_CHOSUNG){
	  
      _jamo_set_values (&last_hangul->L, utf_input, 0);
	  
    } else if (cjj == CJJ_JUNGSUNG) {
	  
      _jamo_set_values (&last_hangul->V, utf_input, 0);
      last_hangul->completed = True;
	  
    } else if (cjj == CJJ_JONGSUNG){
	  
      _jamo_set_values (&last_hangul->T, utf_input, 0);
      last_hangul->completed = True;
    }

    composer_hangul_buffer_add_hangul (hangul_buffer, last_hangul); 
    composer_hangul_free (last_hangul);
    last_hangul = NULL;
    return ComposerRetKeep;

  }

  if (cjj == CJJ_CHOSUNG){
    if (last_hangul->completed == True){
      new_hangul = composer_hangul_new ();
      _jamo_set_values (&new_hangul->L, utf_input, 0x0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
    } else if (!_jamo_is_clean (&last_hangul->T) ||
	       !_jamo_is_clean (&last_hangul->V) ){
      last_hangul->completed = True;
      new_hangul = composer_hangul_new ();
      _jamo_set_values (&new_hangul->L, utf_input, 0x0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
    } else if (_jamo_is_done (&last_hangul->L)){
      /* chosung is already finished */
      last_hangul->completed = True;
      /* thus, create a next one */
      new_hangul = composer_hangul_new ();
      _jamo_set_values (&new_hangul->L, utf_input, 0x0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
      new_hangul = NULL;
    } else if (!_jamo_is_clean (&last_hangul->L)){
      
      UTFCHAR first, ch;
      first = _jamo_get_first (&last_hangul->L);
      ch = composer_hangul_coupled_jamo (first, utf_input);
      if (ch){
	_jamo_set_values (&last_hangul->L, first, utf_input);
      } else {
	/* invalid coupling, thus create a next one */
	last_hangul->completed = True;
	
	new_hangul = composer_hangul_new ();
	_jamo_set_values (&new_hangul->L, utf_input, 0);
	composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	/* free new_hangul */
	composer_hangul_free (new_hangul);
	new_hangul = NULL;
      
      }
    } else {
      /* This is not likely to be happened... */
      _jamo_set_values (&last_hangul->L, utf_input, 0);
      
    }
    return ComposerRetKeep;
  } else if (cjj == CJJ_JUNGSUNG) {
    if (last_hangul->completed == True){
      new_hangul = composer_hangul_new ();
      new_hangul->completed = True;
      _jamo_set_values (&new_hangul->V, utf_input, 0);

      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
    } else if (!_jamo_is_clean (&last_hangul->T) ||
	       _jamo_is_done (&last_hangul->V)){
      /* last_hangul is done, 'cause new hangul is being created */
      last_hangul->completed = True;
	    
      new_hangul = composer_hangul_new ();
      new_hangul->completed = True;
      _jamo_set_values (&new_hangul->V, utf_input, 0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
    } else if (!_jamo_is_clean (&last_hangul->V)){
      coupled_jaeum =
	composer_hangul_coupled_jamo (last_hangul->V.first, utf_input);
	    
      if(coupled_jaeum == 0){
	/* invalid coupling */
	/* last_hangul is done, 'cause new hangul is being created */
	last_hangul->completed = True;
		
	new_hangul = composer_hangul_new ();
	new_hangul->completed = True;
	_jamo_set_values (&new_hangul->V, utf_input, 0);
	composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	/* free new_hangul */
	composer_hangul_free (new_hangul);

      } else {
	/* valid coupling */
	_jamo_set_values (&last_hangul->V,
			  last_hangul->V.first, utf_input);
      }
    } else if (!_jamo_is_clean (&last_hangul->L)){
      /*
	Here, there's some chosung, but no jung sung,
	thus, put the current utf_input(jungsung)
      */
      _jamo_set_values (&last_hangul->V, utf_input, 0);
	    
    } else {
      /*
	Here, there was no chosung, thus,
	I need to create a new hangul buffer again..
      */
      last_hangul->completed = True;
		
      new_hangul = composer_hangul_new ();
      new_hangul->completed = True;
      _jamo_set_values (&new_hangul->V, utf_input, 0);
      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
    }
    return ComposerRetKeep;
  } else if (cjj == CJJ_JONGSUNG) {
    if (last_hangul->completed == True ||
	_jamo_is_done (&last_hangul->T)){
	    
      new_hangul = composer_hangul_new ();
      new_hangul->completed = True;
      _jamo_set_values (&new_hangul->T, utf_input, 0);

      composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
      /* free new_hangul */
      composer_hangul_free (new_hangul);
	    
    } else if (!_jamo_is_clean (&last_hangul->T)){
	    
      coupled_jaeum =
	composer_hangul_coupled_jamo (last_hangul->T.first, utf_input);
	    
      if(coupled_jaeum == 0){
	/* invalid coupling */
	/* last_hangul is done, 'cause new hangul is being created */
	last_hangul->completed = True;
		
	new_hangul = composer_hangul_new ();
	new_hangul->completed = True;
	_jamo_set_values (&new_hangul->T, utf_input, 0);
	composer_hangul_buffer_add_hangul (hangul_buffer, new_hangul);
	/* free new_hangul */
	composer_hangul_free (new_hangul);
      } else {
	/* valid coupling */
	_jamo_set_values (&last_hangul->T,
			  last_hangul->T.first, utf_input);
	/* because both jongsungs are in place,
	   this character is not done..
	*/
	last_hangul->completed = True;
      }
    } else {
      
      if (_jamo_is_clean (&last_hangul->L) ||
	  _jamo_is_clean (&last_hangul->V)){
	/* invalid composition */
	last_hangul->completed = True;
	new_hangul = composer_hangul_new ();
	new_hangul->completed = True;
	_jamo_set_values (&new_hangul->T, utf_input, 0);
      } else {
	_jamo_set_values (&last_hangul->T, utf_input, 0);
      }
      
    }

    return ComposerRetKeep;
  } else {
    /*
      invalid input, that is,
      neither chosung, jungsung nor jongsung
    */
    return -1;

  }
}

static Bool
composer_hangul_convert_input_to_unicode (int keyChar, int state, KEYBOARD_LAYOUT_T layout_id, MapInfo *map_info)
{
  static const KeymapData map[] = {
    { 0x0021, CJJ_NONE,0x11bd, CJJ_JONGSUNG,0x11a9, CJJ_JONGSUNG},
    { 0x0022, CJJ_NONE,0x0022, CJJ_NONE,0x00b7, CJJ_NONE},
    { 0x0023, CJJ_NONE,0x0023, CJJ_NONE,0x11bd, CJJ_NONE},
    { 0x0024, CJJ_NONE,0x0024, CJJ_NONE,0x11b5, CJJ_JONGSUNG},
    { 0x0025, CJJ_NONE,0x0025, CJJ_NONE,0x11b4, CJJ_JONGSUNG},
    { 0x0026, CJJ_NONE,0x0026, CJJ_NONE,0x201c, CJJ_NONE},
    { 0x0027, CJJ_NONE,0x1110, CJJ_CHOSUNG,0x1110, CJJ_CHOSUNG},
    { 0x0028, CJJ_NONE,0x0028, CJJ_NONE,0x0027, CJJ_NONE},
    { 0x0029, CJJ_NONE,0x0029, CJJ_NONE,0x007e, CJJ_NONE},
    { 0x002a, CJJ_NONE,0x002a, CJJ_NONE,0x201d, CJJ_NONE},
    { 0x002b, CJJ_NONE,0x002b, CJJ_NONE,0x002b, CJJ_NONE},
    { 0x002c, CJJ_NONE,0x002c, CJJ_NONE,0x002c, CJJ_NONE},
    { 0x002d, CJJ_NONE,0x002d, CJJ_NONE,0x0029, CJJ_NONE},
    { 0x002e, CJJ_NONE,0x002e, CJJ_NONE,0x002e, CJJ_NONE},
    { 0x002f, CJJ_NONE,0x1169, CJJ_JUNGSUNG,0x1169, CJJ_JUNGSUNG},
    { 0x0030, CJJ_NONE,0x110f, CJJ_CHOSUNG,0x110f, CJJ_CHOSUNG},
    { 0x0031, CJJ_NONE,0x11c2, CJJ_JONGSUNG,0x11c2, CJJ_JONGSUNG},
    { 0x0032, CJJ_NONE,0x11bb, CJJ_JONGSUNG,0x11bb, CJJ_JONGSUNG},
    { 0x0033, CJJ_NONE,0x11b8, CJJ_JONGSUNG,0x11b8, CJJ_JONGSUNG},
    { 0x0034, CJJ_NONE,0x116d, CJJ_JUNGSUNG,0x116d, CJJ_JUNGSUNG},
    { 0x0035, CJJ_NONE,0x1172, CJJ_JUNGSUNG,0x1172, CJJ_JUNGSUNG},
    { 0x0036, CJJ_NONE,0x1163, CJJ_JUNGSUNG,0x1163, CJJ_JUNGSUNG},
    { 0x0037, CJJ_NONE,0x1168, CJJ_JUNGSUNG,0x1168, CJJ_JUNGSUNG},
    { 0x0038, CJJ_NONE,0x1174, CJJ_JUNGSUNG,0x1174, CJJ_JUNGSUNG},
    { 0x0039, CJJ_NONE,0x116e, CJJ_JUNGSUNG,0x116e, CJJ_JUNGSUNG},
    { 0x003a, CJJ_NONE,0x003a, CJJ_NONE,0x0034, CJJ_NONE},
    { 0x003b, CJJ_NONE,0x1107, CJJ_CHOSUNG,0x1107, CJJ_CHOSUNG},
    { 0x003c, CJJ_NONE,0x0032, CJJ_NONE,0x002c, CJJ_NONE},
    { 0x003d, CJJ_NONE,0x003d, CJJ_NONE,0x003e, CJJ_NONE},
    { 0x003e, CJJ_NONE,0x0033, CJJ_NONE,0x002e, CJJ_NONE},
    { 0x003f, CJJ_NONE,0x003f, CJJ_NONE,0x0021, CJJ_NONE},
    { 0x0040, CJJ_NONE,0x0040, CJJ_NONE,0x11b0, CJJ_JONGSUNG},
    { 0x1106, CJJ_NONE,0x11ae, CJJ_JONGSUNG,0x11ae, CJJ_JONGSUNG},
    { 0x1172, CJJ_NONE,0x0021, CJJ_NONE,0x003f, CJJ_NONE},
    { 0x110e, CJJ_NONE,0x11b1, CJJ_JONGSUNG,0x11bf, CJJ_JONGSUNG},
    { 0x110b, CJJ_NONE,0x11b0, CJJ_JONGSUNG,0x11b2, CJJ_JONGSUNG},
    { 0x1104, CJJ_NONE,0x11bf, CJJ_JONGSUNG,0x11ac, CJJ_JONGSUNG},
    { 0x1105, CJJ_NONE,0x11a9, CJJ_JONGSUNG,0x11b1, CJJ_JONGSUNG},
    { 0x1112, CJJ_NONE,0x002f, CJJ_NONE,0x1164, CJJ_JUNGSUNG},
    { 0x1169, CJJ_NONE,0x0027, CJJ_NONE,0x0030, CJJ_NONE},
    { 0x1163, CJJ_NONE,0x0038, CJJ_NONE,0x0037, CJJ_NONE},
    { 0x1165, CJJ_NONE,0x0034, CJJ_NONE,0x0031, CJJ_NONE},
    { 0x1161, CJJ_NONE,0x0035, CJJ_NONE,0x0032, CJJ_NONE},
    { 0x1175, CJJ_NONE,0x0036, CJJ_NONE,0x0033, CJJ_NONE},
    { 0x1173, CJJ_NONE,0x0031, CJJ_NONE,0x0022, CJJ_NONE},
    { 0x116e, CJJ_NONE,0x0030, CJJ_NONE,0x002d, CJJ_NONE},
    { 0x1164, CJJ_NONE,0x0039, CJJ_NONE,0x0038, CJJ_NONE},
    { 0x1168, CJJ_NONE,0x003e, CJJ_NONE,0x0039, CJJ_NONE},
    { 0x1108, CJJ_NONE,0x11c1, CJJ_JONGSUNG,0x11c1, CJJ_JONGSUNG},
    { 0x1101, CJJ_NONE,0x1164, CJJ_JUNGSUNG,0x11b6, CJJ_JONGSUNG},
    { 0x1102, CJJ_NONE,0x11ad, CJJ_JONGSUNG,0x11ad, CJJ_JONGSUNG},
    { 0x110a, CJJ_NONE,0x003b, CJJ_NONE,0x11b3, CJJ_JONGSUNG},
    { 0x1167, CJJ_NONE,0x0037, CJJ_NONE,0x0036, CJJ_NONE},
    { 0x1111, CJJ_NONE,0x11b6, CJJ_JONGSUNG,0x11aa, CJJ_JONGSUNG},
    { 0x110d, CJJ_NONE,0x11c0, CJJ_JONGSUNG,0x11c0, CJJ_JONGSUNG},
    { 0x1110, CJJ_NONE,0x11b9, CJJ_JONGSUNG,0x11b9, CJJ_JONGSUNG},
    { 0x116d, CJJ_NONE,0x003c, CJJ_NONE,0x0035, CJJ_NONE},
    { 0x110f, CJJ_NONE,0x11be, CJJ_JONGSUNG,0x11be, CJJ_JONGSUNG},
    { 0x005b, CJJ_NONE,0x005b, CJJ_NONE,0x0028, CJJ_NONE},
    { 0x005c, CJJ_NONE,0x005c, CJJ_NONE,0x003a, CJJ_NONE},
    { 0x005d, CJJ_NONE,0x005d, CJJ_NONE,0x003c, CJJ_NONE},
    { 0x005e, CJJ_NONE,0x005e, CJJ_NONE,0x003d, CJJ_NONE},
    { 0x005f, CJJ_NONE,0x005f, CJJ_NONE,0x003b, CJJ_NONE},
    { 0x0060, CJJ_NONE,0x0060, CJJ_NONE,0x002a, CJJ_NONE},
    { 0x1106, CJJ_NONE,0x11bc, CJJ_JONGSUNG,0x11bc, CJJ_JONGSUNG},
    { 0x1172, CJJ_NONE,0x116e, CJJ_JUNGSUNG,0x116e, CJJ_JUNGSUNG},
    { 0x110e, CJJ_NONE,0x1166, CJJ_JUNGSUNG,0x1166, CJJ_JUNGSUNG},
    { 0x110b, CJJ_NONE,0x1175, CJJ_JUNGSUNG,0x1175, CJJ_JUNGSUNG},
    { 0x1103, CJJ_NONE,0x1167, CJJ_JUNGSUNG,0x1167, CJJ_JUNGSUNG},
    { 0x1105, CJJ_NONE,0x1161, CJJ_JUNGSUNG,0x1161, CJJ_JUNGSUNG},
    { 0x1112, CJJ_NONE,0x1173, CJJ_JUNGSUNG,0x1173, CJJ_JUNGSUNG},
    { 0x1169, CJJ_NONE,0x1102, CJJ_CHOSUNG,0x1102, CJJ_CHOSUNG},
    { 0x1163, CJJ_NONE,0x1106, CJJ_CHOSUNG,0x1106, CJJ_CHOSUNG},
    { 0x1165, CJJ_NONE,0x110b, CJJ_CHOSUNG,0x110b, CJJ_CHOSUNG},
    { 0x1161, CJJ_NONE,0x1100, CJJ_CHOSUNG,0x1100, CJJ_CHOSUNG},
    { 0x1175, CJJ_NONE,0x110c, CJJ_CHOSUNG,0x110c, CJJ_CHOSUNG},
    { 0x1173, CJJ_NONE,0x1112, CJJ_CHOSUNG,0x1112, CJJ_CHOSUNG},
    { 0x116e, CJJ_NONE,0x1109, CJJ_CHOSUNG,0x1109, CJJ_CHOSUNG},
    { 0x1162, CJJ_NONE,0x110e, CJJ_CHOSUNG,0x110e, CJJ_CHOSUNG},
    { 0x1166, CJJ_NONE,0x1111, CJJ_CHOSUNG,0x1111, CJJ_CHOSUNG},
    { 0x1107, CJJ_NONE,0x11ba, CJJ_JONGSUNG,0x11ba, CJJ_JONGSUNG},
    { 0x1100, CJJ_NONE,0x1162, CJJ_JUNGSUNG,0x1162, CJJ_JUNGSUNG},
    { 0x1102, CJJ_NONE,0x11ab, CJJ_JONGSUNG,0x11ab, CJJ_JONGSUNG},
    { 0x1109, CJJ_NONE,0x1165, CJJ_JUNGSUNG,0x1165, CJJ_JUNGSUNG},
    { 0x1167, CJJ_NONE,0x1103, CJJ_CHOSUNG,0x1103, CJJ_CHOSUNG},
    { 0x1111, CJJ_NONE,0x1169, CJJ_JUNGSUNG,0x1169, CJJ_JUNGSUNG},
    { 0x110c, CJJ_NONE,0x11af, CJJ_JONGSUNG,0x11af, CJJ_JONGSUNG},
    { 0x1110, CJJ_NONE,0x11a8, CJJ_JONGSUNG,0x11a8, CJJ_JONGSUNG},
    { 0x116d, CJJ_NONE,0x1105, CJJ_CHOSUNG,0x1105, CJJ_CHOSUNG},
    { 0x110f, CJJ_NONE,0x11b7, CJJ_JONGSUNG,0x11b7, CJJ_JONGSUNG},
    { 0x007b, CJJ_NONE,0x007b, CJJ_NONE,0x0025, CJJ_NONE},
    { 0x007c, CJJ_NONE,0x007c, CJJ_NONE,0x005c, CJJ_NONE},
    { 0x007d, CJJ_NONE,0x007d, CJJ_NONE,0x002f, CJJ_NONE},
    { 0x007e, CJJ_NONE,0x007e, CJJ_NONE,0x203b, CJJ_NONE},
  };

  assert (map_info != NULL);
  
  if (keyChar >= '!' && keyChar <= '~') {
    if (state & IM_SHIFT_MASK) {
      if (keyChar >= 'a' && keyChar <= 'z')
	keyChar -= ('a' - 'A');
    } else {
      if (keyChar >= 'A' && keyChar <= 'Z')
	keyChar += ('a' - 'A');
    }
    map_info->val = map[keyChar - '!'].layout[layout_id].val;
    map_info->type = map[keyChar - '!'].layout[layout_id].type;
#if 0
    return map[keyChar - '!'].layout[layout_id].val;
#endif
    return True;

  }
  
  map_info->val = 0x0000;
  map_info->type = CJJ_NONE;
  
  return False;
}

static const CoupledJamoInfo coupled_jamo[] = {
  { 0x1100, 0x1100, 0x1101 },   /* LC : g + g  = gg   */
  { 0x1103, 0x1103, 0x1104 },   /* LC : d + d  = dd   */
  { 0x1107, 0x1107, 0x1108 },   /* LC : b + b  = bb   */
  { 0x1109, 0x1109, 0x110a },   /* LC : s + s  = ss   */
  { 0x110c, 0x110c, 0x110d },   /* LC : j + j  = jj   */
  { 0x1169, 0x1161, 0x116a },   /* IV : o + a  = wa   */
  { 0x1169, 0x1162, 0x116b },   /* IV : o + ae = wae  */
  { 0x1169, 0x1175, 0x116c },   /* IV : o + i  = we   */
  { 0x116e, 0x1165, 0x116f },   /* IV : u + eo = weo  */
  { 0x116e, 0x1166, 0x1170 },   /* IV : u + e  = we   */
  { 0x116e, 0x1175, 0x1171 },   /* IV : u + i  = wi   */
  { 0x1173, 0x1175, 0x1174 },   /* IV : eu+ i  = eui  */
  { 0x11a8, 0x11a8, 0x11a9 },   /* TC : g + g  = gg   */
  { 0x11a8, 0x11ba, 0x11aa },   /* TC : g + s  = gs   */
  { 0x11ab, 0x11bd, 0x11ac },   /* TC : n + s  = ns   */
  { 0x11ab, 0x11c2, 0x11ad },   /* TC : n + h  = nh   */
  { 0x11af, 0x11a8, 0x11b0 },   /* TC : l + g  = lg   */
  { 0x11af, 0x11b7, 0x11b1 },   /* TC : l + m  = lm   */
  { 0x11af, 0x11b8, 0x11b2 },   /* TC : l + b  = lb   */
  { 0x11af, 0x11ba, 0x11b3 },   /* TC : l + s  = ls   */
  { 0x11af, 0x11c0, 0x11b4 },   /* TC : l + t  = lt   */
  { 0x11af, 0x11c1, 0x11b5 },   /* TC : l + p  = lp   */
  { 0x11af, 0x11c2, 0x11b6 },   /* TC : l + h  = lh   */
  { 0x11b8, 0x11ba, 0x11b9 },   /* TC : b + s  = bs   */
  { 0x11ba, 0x11ba, 0x11bb },   /* TC : s + s  = ss   */
};


/* return:
   0: not valid
   !0: valid
*/
static UTFCHAR
composer_hangul_coupled_jamo (UTFCHAR first, UTFCHAR second)
{
  int i, len;
  
  len = (sizeof(coupled_jamo) / sizeof(coupled_jamo[0]));
  /*
    Fix me. Not very smart way,
    but should be okay becuase the loop will happen
    on relatively small array
  */
  
  for (i = 0 ; i < len ; i++){
    if (coupled_jamo [i].first == first &&
	coupled_jamo [i].second == second)
      return coupled_jamo [i].coupled;
  }
  return 0;  
}

Bool
composer_hangul_hanja_lookup_start (HANGULBuffer *hangul_buffer, 
				    int *number_of_candidates,
				    UTFCHAR ***hanja_list_return)
{
  HANGUL *first_hangul;
  UTFCHAR hangul_key;
  int table_index;
  int i;
  Bool mthd_return;
  UTFCHAR *u16hangul = NULL;
  int n_hangul_chars;

  assert (hangul_buffer != NULL);
#if 0
  first_hangul = composer_hangul_buffer_get_first_hangul (hangul_buffer);
  if (first_hangul == NULL)
    return False;

  hangul_key = composer_hangul_combine_cho_jung_jong (first_hangul);
  if (hangul_key == 0x0000)
    return False;
#endif
  n_hangul_chars = 
    composer_hangul_buffer_get_hangul_length (hangul_buffer);
  if (n_hangul_chars < 1)
    return False;
  
  mthd_return =
    composer_hangul_buffer_get_content_in_u16 (hangul_buffer, &u16hangul);
  if (!mthd_return){
    KOLE_LOG (LOGDEST_STDOUT, "composer_hangul_hanja_lookup_start error :"
	     "composer_hangul_buffer_get_content_in_u16 failed\n");
    *number_of_candidates = 0;
    *hanja_list_return = NULL;
    return False;
  }
  
  mthd_return = dictionary_search_hanja_candidates_in_utf16
    (u16hangul, number_of_candidates, hanja_list_return );

  if (!mthd_return){
    return False;
  } else
    return True;
}


/*
  I create this function here after copying code from 
  hangul_imebuffer_get_preedit (IMEBuffer a_buffer, UTFCHAR **out_utfstr)

  I feel, hangul_imebuffer_get_preedit should call this function.
  I'll do it soon..
*/
Bool
composer_hangul_buffer_get_content_in_u16(HANGULBuffer *hbuf,
					  UTFCHAR **u16hangul_return)
{
  int i, utfchar_len;
  UTFCHAR *han_char;
  HANGUL *current_hangul;

  assert (hbuf != NULL);
  if (hbuf == NULL){
    KOLE_LOG (LOGDEST_STDOUT, "composer_hangul_buffer_get_content_in_u16 error :"
	     "hbuf is null\n");
    *u16hangul_return = NULL;
    return False;
  }
  
  utfchar_len =
    composer_hangul_buffer_get_hangul_length (hbuf);
  
  assert (utfchar_len >= 0);

  if (utfchar_len <= -1) {
    KOLE_LOG (LOGDEST_STDERR, "hangul_imebuffer_get_preedit error: number of hangul "
	    "can't be negative\n");
    return False;
  }
  else if (utfchar_len == 0) {
    *u16hangul_return = NULL;
    return True;
  }
  else {
  
    if (hbuf == NULL){
      KOLE_LOG (LOGDEST_STDERR, 
	      "hangul_imebuffer_get_preedit error: composer_buffer can't be null\n");
      return False;
    }
		
    *u16hangul_return = (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( (hbuf->buffer[i]) );
      if (combined_character){
	(*u16hangul_return)[i] = combined_character;
      } else {
	current_hangul = hbuf->buffer[i];
	
	if (!_jamo_is_clean (&current_hangul->L)){
	  tmp = _jamo_get_value (&current_hangul->L);
	  (*u16hangul_return)[i] =
	    hangul_combining_chosung_to_compatibility_jamo (tmp);
	} else if (!_jamo_is_clean (&current_hangul->V)){
	  tmp = _jamo_get_value (&current_hangul->V);
	  (*u16hangul_return)[i] =
	    hangul_combining_jungsung_to_compatibility_moeum (tmp);
	} else if (!_jamo_is_clean (&current_hangul->T)){
	  tmp = _jamo_get_value (&current_hangul->V);
	  (*u16hangul_return)[i] =
	    hangul_combining_jongsung_to_compatibility_jaeum (tmp);     
	}
      }
    }
    (*u16hangul_return)[i] = 0x0000;
  }

  return True;
}


void
composer_config_switch_codeset (Config *pconfig, int *next)
{
  assert (pconfig != NULL);
  if (!pconfig){
    KOLE_LOG (LOGDEST_STDERR,
	     "composer_config_switch_codeset error: pconfig is null\n");
    return;
  }
  if (!next){
    if (pconfig->charset == IN_EUC)
      pconfig->charset = IN_UNICODE;
    else
      pconfig->charset = IN_EUC;
  } else if ((*next >= IN_EUC) && (*next <= IN_UNICODE)) {
    pconfig->charset = *next;
  } else
    return;
}


void
composer_config_switch_keyboard (Config *pconfig)
{
  assert (pconfig != NULL);
  if (!pconfig){
    KOLE_LOG (LOGDEST_STDERR,
	     "composer_config_switch_codeset error: pconfig is null\n");
    return;
  }
  if (pconfig->keyboard == (LAYOUT_NUM - 1)){
    pconfig->keyboard = LAYOUT_2PART;
  } else {
    pconfig->keyboard++;
  }
}


void
composer_config_switch_commit_method (Config *pconfig)
{
  assert (pconfig != NULL);
  if (!pconfig){
    KOLE_LOG (LOGDEST_STDERR,
	     "composer_config_switch_codeset error: pconfig is null\n");
    return;
  }
  if (pconfig->commit_mode == COMMIT_BY_HOTKEY){
    pconfig->commit_mode = COMMIT_BY_CHAR;
  } else {
    pconfig->commit_mode++;
  }
}

#ifdef TEST_RUN
int
main(int argc, char **argv)
{
  int c;
  HANGULBuffer *hb;
  hb = composer_hangul_buffer_new ();
  while(c = getc(stdin))
    {
      if(c == '\n'){
	KOLE_LOG (LOGDEST_STDOUT, "continue...\n");
	continue;
      }
      composer_hangul_automata_2pair (c, 0, 0, hb);
      composer_hangul_buffer_print (hb);
    }

}
#endif
