/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include "koinput.h"
#include "kole.h"
#include "le.h"
#include "le_proc.h"
#include "iml.h"
#include "method.h"
#include "xaux_locale.h"
#include "key_event.h"
#include "kolelog.h"

Bool if_le_OpenIF (iml_if_t *);
Bool if_le_CloseIF (iml_if_t *);
Bool if_le_GetIFValue (iml_if_t *, IMArgList, int);
Bool if_le_SetIFValue (iml_if_t *, IMArgList, int);
Bool if_le_OpenDesktop (iml_desktop_t *, IMArgList, int);
Bool if_le_CloseDesktop (iml_desktop_t *);
Bool if_le_CreateSC (iml_session_t *, IMArgList, int);
Bool if_le_DestroySC (iml_session_t *);
Bool if_le_GetSCValue (iml_session_t *, IMArgList, int);
Bool if_le_SetSCValue (iml_session_t *, IMArgList, int);
IMText *if_le_ResetSC (iml_session_t *);
void if_le_SetSCFocus (iml_session_t *);
void if_le_UnsetSCFocus (iml_session_t *);
void if_le_SendEvent (iml_session_t *, IMInputEvent * ev);

/* IF Method */
if_methods_t le_methods = {
  if_le_OpenIF,
  if_le_CloseIF,
  if_le_GetIFValue,
  if_le_SetIFValue,
  if_le_OpenDesktop,
  if_le_CloseDesktop,
  if_le_CreateSC,
  if_le_DestroySC,
  if_le_GetSCValue,
  if_le_SetSCValue,
  if_le_ResetSC,
  if_le_SetSCFocus,
  if_le_UnsetSCFocus,
  if_le_SendEvent
};

extern char lename_string[];
extern UTFCHAR lename_utf_string[];
extern IMLocale locales[];
extern char default_locale_id;

extern int           g_number_of_engines;
extern int           g_default_engine_id;
extern IMEEngineRec *g_engines[MAX_ENGINE_NUM];

static IMLEName lename = {lename_string, lename_utf_string };
static IMObjectDescriptorStruct *objects = NULL;

static int s_id[MAX_SESSION];

/* create an IM Object instantance */
void
objects_init ()
{
  char xaux_so_path[125];
  IMObjectDescriptorStruct *l;

  KOLE_LOG (LOGDEST_STDOUT, "init objects, objects:%x\n", objects);

  if (objects != NULL) return;

  objects = (IMObjectDescriptorStruct *) calloc(2, sizeof(IMObjectDescriptorStruct));
  if (objects == NULL) return;

  sprintf(xaux_so_path, "./locale/%s/aux.so", XAUX_LOCALE_NAME);
  l = objects;
  l->leid = lename_string;                   /* engine id */
  l->type = IM_DOWNLOADINGOBJECT_BINGUI_TYPE; /* object type */
  l->name = lename_utf_string;                   
  l->name_length = UTFCHARLen(lename_utf_string);
  l->domain = "com.sun";
  l->scope = lename_string;
  l->path = (char *)(strdup(xaux_so_path));    /* path for .so from /usr/lib/im/ */
  l->signature = "";
  l->basepath = NULL;         /* only for CCDEF */
  l->encoding = NULL;         /* only for CCDEF */
}

void
objects_done()
{
  KOLE_LOG (LOGDEST_STDOUT, "free objects\n");

  if (objects == NULL) return;

  if (objects[0].path) free((char *)(objects[0].path));
  free(objects);
  objects = NULL;
}

/* htt_server get the IF information when start htt_server */
void
if_GetIfInfo (IMArgList args, int num_args)
{
  int i;

  KOLE_LOG (LOGDEST_STDOUT, "if_GetIfInfo == num_args:%d\n", num_args);

  objects_init(); 
  for (i = 0; i < num_args; i++, args++) {
    switch (args->id)
      {
      case IF_VERSION:
	args->value = (IMArgVal) "1.2";
	break;
      case IF_LE_NAME:
	args->value = (IMArgVal) & lename;
	break;
      case IF_SUPPORTED_LOCALES:
	/* List of supported locales */
	args->value = (IMArgVal) & locales;
	break;
      case IF_SUPPORTED_OBJECTS:
	args->value = (IMArgVal) objects; 
	break;
      case IF_METHOD_TABLE:
	args->value = (IMArgVal) & le_methods;
	break;
      case IF_NEED_THREAD_LOCK:
	args->value = (IMArgVal) True;
	break;
      default:
	break;
      }
  }
}

unsigned int le_openif_counter = 0;

Bool 
if_le_OpenIF (iml_if_t * If)
{
  int ret = 0;
  
  KOLE_LOG (LOGDEST_STDOUT, "if_le_OpenIF ====\n");

  le_openif_counter++;
  if (le_openif_counter > 1) return True;

  ret = koinput_init();

  /* if ret == -1, this means, there's no language engine... */
  if (ret == -1)
    return False;
  else
    return True;
}

/* Close the IF interface */
Bool
if_le_CloseIF (iml_if_t * If)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_CloseIF === \n");
  if (le_openif_counter > 0)
    le_openif_counter--;
  if (le_openif_counter == 0) {
    objects_done();
    koinput_done();
  }
  return True;
}

Bool
if_le_GetIFValue (iml_if_t * If, IMArgList args, int num_args)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_GetIFValue()\n");
  return True;
}

Bool
if_le_SetIFValue (iml_if_t * If, IMArgList args, int num_args)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_SetIFValue()\n");
  return True;
}

Bool
if_le_OpenDesktop (iml_desktop_t * desktop, IMArgList args, int num_args)
{
  MyDataPerDesktop *desktop_data = (MyDataPerDesktop *)calloc(1,sizeof(MyDataPerDesktop));
  int i;

  KOLE_LOG (LOGDEST_STDOUT, "if_le_OpenDesktop()\n");

  desktop_data->selectaux_started = False;
  desktop_data->optionaux_started = False;
  desktop_data->keyboardaux_started = False;
  desktop_data->lookupaux_started = False;
  desktop_data->actionaux_started = False;
  desktop_data->paletteaux_started = False;
  desktop_data->paletteaux_first_trigger_on = True;
  desktop_data->paletteaux_ready = False;
  desktop_data->current_session = 0;
  desktop_data->root_session = 0;
  desktop_data->aux_locale_id = default_locale_id;

  /* Initialize UserPreference data by default value.
     This values will be overwritten by
     either user preference file or aux event.
  */
  desktop_data->user_pref.ime_user_id = 0;
  desktop_data->user_pref.ime_user_home = NULL;
  desktop_data->user_pref.keyboard_layout = 0;
  desktop_data->user_pref.conversion_format = 0;
  /* desktop_data->user_pref.delete_by_jaso = 0; */
  
  desktop_data->user_pref.auto_commit_on_char_basis = 0;
  desktop_data->user_pref.auto_commit_on_single_candidate_conversion = 0;
	
  for (i=0; i<MAX_ENGINE_NUM; i++) {
    desktop_data->ime_args[i] = (IMEArgList) NULL;
    desktop_data->bSentKeymapToAux[i] = False;
  }

  desktop->specific_data = (void *) desktop_data;

  return True;
}

Bool
if_le_CloseDesktop (iml_desktop_t * desktop)
{
  MyDataPerDesktop *desktop_data = (MyDataPerDesktop *) desktop->specific_data;
  int i;

  KOLE_LOG (LOGDEST_STDOUT, "if_le_CloseDesktop()\n");
  for (i=0; i<MAX_ENGINE_NUM; i++) {
    if (desktop_data->ime_args[i] != (IMEArgList) NULL)
      free((char *)desktop_data->ime_args[i]);
  }
  if(desktop_data->user_pref.ime_user_home)
    free((void *)desktop_data->user_pref.ime_user_home);

  free(desktop_data);

  return True;
}

Bool
if_le_CreateSC (iml_session_t *s, IMArgList args, int num_args)
{
  static int first_create = 1;
  int current_engine_id = g_default_engine_id;
  IMEEngine p_current_engine;
  IMEBufferMethods engine_methods_table;
	
  int i, session_id;
  iml_desktop_t *desktop = s->desktop;
  MyDataPerDesktop *desktop_data = (MyDataPerDesktop *) desktop->specific_data;
  MyDataPerSession *p = (MyDataPerSession *) calloc(1, sizeof(MyDataPerSession));

  KOLE_LOG (LOGDEST_STDOUT, "if_le_CreateSC()\n");

  if (first_create) {
    for (i = 0 ;i < MAX_SESSION ;i++)
      s_id[i] = 0;
    first_create = 0;
  }

  session_id = 0;
  for (i=0;i< MAX_SESSION;i++)
    if (s_id[i]==0) { 
      session_id = i; 
      break;
    }
  s_id[session_id] = 1;

  desktop_data->current_session = 0;
  p->paletteaux_status.x = 0;
  p->paletteaux_status.y = 0;
  p->paletteaux_status.on = False;
  p->paletteaux_status.junjabanja = False;
  p->paletteaux_status.punct = False;
  /* p->paletteaux_status.engine_id = 0xff; */
  /* At this juncture, engine_id is always 0, becuase there's only one engine for ko */
  p->paletteaux_status.engine_id = current_engine_id;
    
  p->keyboardaux_status.x = 0;
  p->keyboardaux_status.y = 0;
  p->keyboardaux_status.keyboard_id = -1;
  p->keyboardaux_status.key_status = 0;

  p->lookupaux_status.on = False;
  p->selectaux_status.on = False;
  p->optionaux_status.on = False;

  p->input_mode = INPUT_MODE_ENGLISH;
	
  p_current_engine = g_engines[current_engine_id];
  p->p_engine = p_current_engine;
  engine_methods_table = p_current_engine->so_methods;
  p->session_core.ime_methods_table = engine_methods_table;
  (*engine_methods_table->ime_buffer_new)(&p->session_core.ime_buffer, session_id, current_engine_id);

  s->specific_data = (void*) p;

  /* set aux proxy data */
  p->protocol_type = UNKNOWN_IMP;
  p->client_type = UNKNOWN_CLIENT;
  for (i = 0; i < num_args; i++, args++) {
    switch (args->id) {
    case UI_PROTOCOL_TYPE:
      if (args->value) {
	logf ("UI_PROTOCOL_TYPE: value: %s\n", args->value);
	if (!strcmp (UI_PROTOCOL_TYPE_XIMP, args->value))
	  p->protocol_type = XIMP;
      }
      break;
    case UI_CLIENT_TYPE:
      if (args->value) {
	KOLE_LOG (LOGDEST_STDOUT, "UI_CLIENT_TYPE: value: %s\n", args->value);
	if (!strcmp (UI_CLIENT_TYPE_X, args->value))
	  p->client_type = X11_CLIENT;
      }
      break;
    }
  }
  return True;
}

Bool
if_le_DestroySC (iml_session_t * s)
{
  int current_engine_id = 0;
  IMEEngine p_current_engine;
  IMEBufferMethods engine_methods_table;
  IMEBuffer p_ime_buffer;
  int i, session_id;
  iml_desktop_t *desktop;
  MyDataPerDesktop *desktop_data;
  MyDataPerSession *p;
  
  p_current_engine = g_engines[current_engine_id];
  engine_methods_table = p_current_engine->so_methods;
  desktop = s->desktop;

  desktop_data = (MyDataPerDesktop *) desktop->specific_data;
  p = (MyDataPerSession *) s->specific_data;
  p_ime_buffer = p->session_core.ime_buffer;

  KOLE_LOG (LOGDEST_STDOUT, "if_le_DestroySC(s=%x)\n", s);

  (*engine_methods_table->ime_buffer_conversion_end)(p_ime_buffer);
  if (p->paletteaux_status.on) {
    p->paletteaux_status.on = False;
    le_change_focus_notify(s);
  }

  desktop_data->current_session = 0;

  if (!(*engine_methods_table->ime_buffer_get_session_id)(p_ime_buffer , &session_id))
    return False;

  s_id[session_id] = 0;
  keyevent_switch_conversion_handler(s, CONVERSION_OFF);

  (*engine_methods_table->ime_buffer_free) (p->session_core.ime_buffer);

  free((char *) p);
  
  return True;
}

Bool
if_le_GetSCValue (iml_session_t * s, IMArgList args, int num_args)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_GetSCValue(s=%x)\n\n", s);
  return True;
}

Bool
if_le_SetSCValue (iml_session_t * s, IMArgList args, int num_args)
{
  int i;
  IMArg *p = args;
  MyDataPerDesktop *desktop_data =
    (MyDataPerDesktop *)(s->desktop->specific_data);
  MyDataPerSession *ds = (MyDataPerSession *)(s->specific_data);
  IMEBufferMethods mthds = ds->session_core.ime_methods_table;
  IMEBuffer ime_buffer = ds->session_core.ime_buffer;

  KOLE_LOG (LOGDEST_STDOUT, "if_le_SetSCValue(), s:0x%x\n", s);

  desktop_data->current_session = s;

  for (i = 0; i < num_args; i++, p++) {
    switch (p->id) {
    case SC_TRIGGER_ON_NOTIFY:
      KOLE_LOG (LOGDEST_STDOUT, "SC_TRIGGER_ON_NOTIFY\n");
      keyevent_switch_conversion_handler (s, CONVERSION_ON);
      break;

    case SC_TRIGGER_OFF_NOTIFY:
      KOLE_LOG (LOGDEST_STDOUT, "SC_TRIGGER_OFF_NOTIFY\n");
      keyevent_switch_conversion_handler (s, CONVERSION_OFF);
      break;
                
    case SC_REALIZE:
      if (desktop_data->paletteaux_started == False) {
	iml_aux_start(s, XAUX_PALETTE_CLASS_NAME);
	auxhandler_send_session_property (s, XAUX_PALETTE_CLASS_NAME);
	desktop_data->paletteaux_started = True;
      }


      iml_status_draw (s);
      le_change_focus_notify(s);
      break;

    default:
      break;
    }
  }
  return True;
}

IMText *
if_le_ResetSC (iml_session_t * s)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_ResetSC(s=%x)\n\n", s);
  return (IMText *) NULL;
}

void
if_le_SetSCFocus (iml_session_t * s)
{
  MyDataPerDesktop *desktop_data =
    (MyDataPerDesktop *)(s->desktop->specific_data);

  KOLE_LOG (LOGDEST_STDOUT, "if_le_SetSCFocus() s:%x, current_session:%x\n",
       s, desktop_data->current_session);

  desktop_data->current_session = s;

  iml_status_draw (s);
  le_change_focus_notify(s);
}

void
if_le_UnsetSCFocus (iml_session_t * s)
{
  KOLE_LOG (LOGDEST_STDOUT, "if_le_UnsetSCFocus() s:%x\n", s);
}

void
if_le_SendEvent (iml_session_t * s, IMInputEvent * ev)
{
  MyDataPerDesktop *dd = (MyDataPerDesktop *)(s->desktop->specific_data);
  MyDataPerSession *sd = (MyDataPerSession *)(s->specific_data);
  iml_session_t *s1;

  KOLE_LOG (LOGDEST_STDOUT, "if_le_SendEvent session=%x\n", s);
  
  if (ev) {
    if (ev->type == IM_EventKeyList) {
      KOLE_LOG (LOGDEST_STDOUT, "Keyboard Event\n");
      keyevent_handler (s, (IMKeyListEvent *)ev);
    } else if (ev->type == IM_EventAux) {
      KOLE_LOG (LOGDEST_STDOUT, "Aux Event\n");
      s1 = dd->current_session;
      if (!s1) s1 = s;
      auxevent_handler (s1, (IMAuxEvent *)ev);
    } 
  }
}

