/*
 * 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 <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <assert.h>

#include "koinput.h"
#include "kole.h"
#include "xaux_locale.h"
#include "encode.h"
#include "kolelog.h"
#include "logf.h"

#define MAX_LINE_LEN	256

int       		 g_number_of_engines;
const int		 g_default_engine_id = 0;
IMEEngineRec 	*g_engines[MAX_ENGINE_NUM];

static void print_core (void);
static void get_ime_line (FILE *fd, char *line);


int
koinput_init (void)
{
  char   file_name[256], line[MAX_LINE_LEN]; 
  char   *engine_name, *engine_path, *engine_options;
  char   locale_name[128], *kptr, *ptr;
    
  int    len, i;
  int    locale_flag = ENCODE_ERROR;
  FILE   *fd;
  int    ret = 0;


  g_number_of_engines = 0;
  for (i=0; i<MAX_ENGINE_NUM; i++)
    g_engines[i] = NULL;

  sprintf(file_name, "%s/%s/%s",  DEFAULT_ENGINE_PATH, 
	  XAUX_LOCALE_NAME, 
	  SYSTEM_PROFILE_NAME);

  logf("file name :%s\n",file_name);
  fd = fopen(file_name, "r");
  if (!fd)
    return (-1);

  do
    {
      get_ime_line(fd, line);
      if (line[0] == '\0') break;
      if (line[0] == '#') continue;

      len = strlen(line);
      if (line[0] == '[' && line[len-1] == ']') 
	{
	  /* compute locale_flag */
	  ptr = line + 1;
	  while(isspace(*ptr)) ptr++;
	  /* get locale section name */
	  kptr = locale_name;
	  while (*ptr && !isspace(*ptr) && *ptr!=']') 
	    *(kptr++) = *(ptr++);
	  *kptr = '\0';

	  /* get locale section name */
	  if (!strcasecmp(locale_name, COMMON_ENGINE_PATH))
	    locale_flag = ENCODES_NUM;
	  else
	    locale_flag = get_encodeid_from_locale(locale_name);
	  logf("locale_name:%s, locale_id:%d\n", locale_name, locale_flag);
	  continue;
	}

      if (locale_flag == ENCODE_ERROR) continue;
      /* get IME language engine name */
      ptr = line;
      engine_name = line;
      while (*ptr && !isspace(*ptr)) ptr++;
      if (*ptr) {
	*ptr = '\0';
	ptr++;
      }
	
      while (*ptr && isspace(*ptr)) ptr++;
      engine_path = ptr;

      while (*ptr && !isspace(*ptr)) ptr++;
      if (*ptr) {
	*ptr = '\0';
	ptr++;
      }
      while (*ptr && isspace(*ptr)) ptr++;
      engine_options = ptr;

      logf("locale_id:%d, locale:%s, Engine Name:%s\n", 
	   locale_flag, locale_name, engine_name, 
	   engine_path, engine_options);

      ret = open_engine (locale_flag, locale_name, 
			 engine_name, engine_path, engine_options);
      if (ret == -1)
	return -1;

    } while (1);	/* end of do */

  fclose(fd);
#if 0  
  print_core();
#endif
  return 0;
}

static void
get_ime_line (FILE *fd, char *line)
{
  int line_ptr;
  char line_buf[256], *ptr;

  line_ptr = 0;
  line[0] = '\0';

  /* get line with no space */
  while(fgets(line_buf, 255, fd) != NULL) {
    ptr = line_buf;

    /* skip space keys */
    while(*ptr && isspace(*ptr)) ptr++;

    /* if is space line, get new line */
    if (*ptr == '\n' || *ptr == '\0')
      continue;

    while(*ptr != '\n' && *ptr != '\0' && line_ptr < MAX_LINE_LEN) 
      line[line_ptr++] = *ptr++;

    /* trim right space */
    while (isspace(line[line_ptr-1])) line_ptr--;
    line[line_ptr] = '\0';

    /* if the line end with '\', then continue read the next line */
    if (line[line_ptr-1] == '\\') {
      line_ptr--;
      line[line_ptr] = '\0';
      continue;
    }

    break;
  }
}

/* engine_options can be data_path or engine options. */
int
open_engine (int locale_id, char *locale_name, char *engine_name, 
	     char *engine_path, char *engine_options)
{
  int		i;
  char		file_name[256];
  char		data_path[256];
  struct	stat	file_buffer;
  Bool          mthd_return;

  void		*so_handler;
  IMEBufferMethods	methods;

  int		is_codetable_engine = 0;

  int ret_noptions;
  KOLE_config **kole_options;
  char *option_file;

  if (g_number_of_engines > MAX_ENGINE_NUM)
    return (-1);
    
  /* read profile from file to memory buffer  */ 
  if (*engine_path) {
    if (engine_path[0] != '/') {
      sprintf(file_name, 
	      "%s/%s/%s",  DEFAULT_ENGINE_PATH, 
	      XAUX_LOCALE_NAME, 
	      engine_path);
			
    } else {
      sprintf(file_name, "%s", engine_path);
    }

    logf("file_name: %s\n", file_name);
  } else {
    is_codetable_engine = 1;
    sprintf(file_name, "%s/%s/%s/%s.so", DEFAULT_ENGINE_PATH, 
	    XAUX_LOCALE_NAME, 
	    locale_name, 
	    engine_name);
		
    logf("file_name: %s\n", file_name);
  }

  if ((stat(file_name, &file_buffer)) == -1) {
    if (is_codetable_engine) {
      sprintf(file_name, "%s/%s/%s/%s.so", DEFAULT_ENGINE_PATH, 
	      XAUX_LOCALE_NAME, 
	      COMMON_ENGINE_PATH, 
	      CODETABLE_ENGINE_NAME);
      if ((stat(file_name, &file_buffer)) == -1)
	return (-1);
    } else {
      return (-1);
    }
  }
  logf("so_file_name:%s\n", file_name);

  so_handler = (void *) dlopen(file_name, RTLD_LAZY);
  assert (so_handler != NULL);
  if (!so_handler) {
    printf("can not open so file: %s\n", file_name);
    return (-1);
  } else {
    methods = (IMEBufferMethods) dlsym(so_handler, "ime_methods");
    assert (methods != NULL);
    if (!methods) {
      printf("can not open method tables of file:%s\n", file_name);
      dlclose(so_handler);
      return(-1);
    }
  }

  g_engines[g_number_of_engines] = (IMEEngineRec *)calloc(1, sizeof(IMEEngineRec));
  if (g_engines[g_number_of_engines] == NULL) return(0);

  g_engines[g_number_of_engines]->core.baseinfo.engine_id = g_number_of_engines;
  g_engines[g_number_of_engines]->core.baseinfo.locale_id = locale_id;
  g_engines[g_number_of_engines]->core.baseinfo.status = ENGINE_NOT_INITIATED;
  g_engines[g_number_of_engines]->core.baseinfo.ename = (char *)strdup(engine_name);

  g_engines[g_number_of_engines]->core.envinfo.lang_name = (char *)strdup(XAUX_LOCALE_NAME);
  g_engines[g_number_of_engines]->core.envinfo.locale_name = (char *)strdup(locale_name);
  g_engines[g_number_of_engines]->core.envinfo.data_path = NULL;
  g_engines[g_number_of_engines]->core.envinfo.data_ptr = NULL;

  g_engines[g_number_of_engines]->core.keymapinfo.bSet = 0;
  for (i=0; i<MAX_KEYMAP_KEY_NUM; i++) 
    g_engines[g_number_of_engines]->core.keymapinfo.keymap[i] = NULL;

  g_engines[g_number_of_engines]->so_handler = so_handler;
  g_engines[g_number_of_engines]->so_methods = methods;

  if (!(option_file = getenv ("KOLE_OPTION_FILE"))){
    mthd_return =
      get_configuration (DEFAULT_KOLE_OPTION_FILE, &ret_noptions, &kole_options);
  } else
    mthd_return =
      get_configuration (option_file, &ret_noptions, &kole_options);

  mthd_return = True;

  if (mthd_return)
    mthd_return = (*methods->ime_engine_start) (ret_noptions, kole_options);
  else /* set default here in case there was problem with reading option */
    mthd_return = (*methods->ime_engine_start) (0, NULL);
  
  if (mthd_return == False) {
    KOLE_LOG (LOGDEST_STDERR,
	      "Failed to initialize the input method engine:%s\n", engine_name);
    dlclose (so_handler);
    return (-1);
  }

  mthd_return =
    (*methods->ime_engine_get_hotkeys) (&g_engines[g_number_of_engines]->core.hotkeys.n_count,&g_engines[g_number_of_engines]->core.hotkeys.keylist);
  
  if (mthd_return == False){
    KOLE_LOG (LOGDEST_STDERR,
	      "Failed to get list of hotkeys, ignoring...");
  }
  g_number_of_engines ++;
  
  return (0);
}

void
koinput_done (void)
{
  int i;
  char *name;

  for(i=0; i<g_number_of_engines; i++) {
    name = g_engines[i]->core.baseinfo.ename;
    if (name) free(name);

    name = g_engines[i]->core.envinfo.lang_name;
    if (name) free(name);

    name = g_engines[i]->core.envinfo.locale_name;
    if (name) free(name);

    if (g_engines[i]->core.hotkeys.n_count > 0){
      int j;
      for (j = 0; g_engines[i]->core.hotkeys.n_count; j++)
	free (g_engines[i]->core.hotkeys.keylist[j]);
    }
    g_engines[i]->so_methods->ime_engine_finish(&(g_engines[i]->core));
    dlclose(g_engines[i]->so_handler);
    free ((char *)g_engines[i]);
    g_engines[i] = NULL;
  }
  g_number_of_engines = 0;
}

static void
print_core (void)
{
  int i;

  logf("g_number_of_engines:%d\n", g_number_of_engines);
  for (i = 0; i < g_number_of_engines ; i++) {
    logf("localeid:%d, imid:%d, ename:%s, kname:%s, status:%d\n", 
	 g_engines[i]->core.baseinfo.locale_id,
	 g_engines[i]->core.baseinfo.engine_id,
	 g_engines[i]->core.baseinfo.ename,
	 g_engines[i]->core.baseinfo.kname,
	 g_engines[i]->core.baseinfo.status);
  }
}
