#include <stdio.h>
#include <locale.h>
#include <assert.h>
#include "keyboard_config.h"

#define MAX_LINE_LEN 		256
#define DEFAULT_KEYBOARD_CONFIG_FILE	"keyboard.cfg"
#define DEFAULT_KEYBOARD_LAYOUT_FILE	"common/keyboard_layout.txt"
#define LABEL_STR		"LABEL"
#define TYPE_STR		"TYPE"
#define ENCODE_STR		"ENCODE"
#define STRING_TYPE_STR		"COMMIT_AS_STRING"
#define KEY_TYPE_STR		"COMMIT_AS_KEY"
#define LIST_BEGIN_STR		"LIST_BEGIN"
#define LIST_END_STR		"LIST_END"



static int g_virtual_keyboard_number = 0;
static KeyboardLayout *g_virtual_keyboards[MAX_KEYBOARD_NUM + 1];

static void get_line_from_file(FILE *fd, char *line);


KeyboardLayout *keyboard_layout_new (void);
static int read_keyboard_config_file (char *path_name, char *file_name);
static int read_keyboard_layout_file (char *path_name, char *file_name);
static void set_ctrlkey_label (KeyboardLayout *pVKB);
static void set_basekey_label(KeyboardLayout *pVKB, char *line);
static void set_keyboard_label (KeyboardLayout *pVKB, char *name_in_ko);
static int get_pc_keyboard_item (char *sLocaleName);
static int KeyboardItem_Free (KeyboardLayout *pVKB);


static void
get_line_from_file(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;
  }
}

/*
  
For keyboard.cfg 

format as follow:

[zh_CN.EUC]
zh_CN.EUC/keyboard_layout.txt
common/keyboard_layout.txt

[zh_CN.GBK]
zh_CN.GBK/keyboard_layout1.txt
zh_CN.GBK/keyboard_layout2.txt
common/keyboard_layout.txt

base directory:  /usr/lib/im/locale/<locale>/

*/

static int
read_keyboard_config_file (char *path_name, char *file_name)
{
  char	line[MAX_LINE_LEN], sFileName[256];
  char 	*ptr, *locale_str;
  FILE 	*fd;
#if 0
  int 	locale_flag = ENCODE_ERROR;
  int 	was_matched = 0;
#endif

  int 	len;

  sprintf(sFileName, "%s/%s", path_name, file_name);

  fd = fopen(sFileName, "r");
  if (!fd) {

    return(-1);
  }

  do {
    get_line_from_file(fd, line);

    if (line[0] == '\0') break;
    if (line[0] == '#') continue;
    
#if 0
    len = strlen(line);

    if (line[0] == '[' && line[len-1] == ']') {

      if (was_matched == 1) break;

      /* Check the previous item is a formal item */
      /* get english name of the item */
      ptr = line + 1;
      while(isspace(*ptr)) ptr++;
      locale_str = ptr;
			
      ptr = line + len - 2;
      while(isspace(*ptr)) ptr--;
      *(ptr+1) = '\0';


      if (*locale_str) {
	locale_flag = get_encodeid_from_locale(locale_str);
      }

      continue;
    }

    if (locale_flag == ENCODE_ERROR || locale_flag != nLocaleID)
      continue;

    was_matched = 1;
#endif
    /*
      read the layout file, in line[], contain the config file name.
    */
    
    read_keyboard_layout_file(path_name, line);
		
  } while (1);

  fclose(fd);

  /*
    if no entry for the locale, read the default layout file.
  */
#if 0
  if (was_matched == 0)
    return(-1);
#endif
  return(0);
}

/*
  For keyboard_layout.txt

  Format as follow:

  [ english name ]
  LABEL	chinese name
  TYPE  	COMMIT_AS_STRING/COMMIT_AS_KEY
  ENCODE  UTF-8
  LIST_BEGIN
  a   	lower_str	upper_str
  LIST_END

*/

static int
read_keyboard_layout_file (char *path_name, char *file_name)
{
  char	line[MAX_LINE_LEN], sFileName[256];
  char 	*ptr, *ename_str;
  FILE 	*fd;
  int 	i, len, need_alloc_new_item;
  int 	list_began = 0;
  KeyboardLayout *pVKB;

  sprintf(sFileName, "%s/%s", path_name, file_name);

  fd = fopen(sFileName, "r");
  if (!fd) {

    return(-1);
  }

  do {
    if (g_virtual_keyboard_number >= MAX_KEYBOARD_NUM) break;

    get_line_from_file(fd, line);

    if (line[0] == '\0') break;
    if (line[0] == '#') continue;

    len = strlen(line);
    if (line[0] == '[' && line[len-1] == ']') {
      list_began = 0;
#if 0
      /* Check the previous item is a formal item */
      need_alloc_new_item = 1;
      if (g_virtual_keyboard_number > 0) {
	pVKB = g_virtual_keyboards[g_virtual_keyboard_number-1];
	if (pVKB != NULL) {
	  if (pVKB->name_in_ko == NULL)
	    need_alloc_new_item = 0;
	}
      }

      if (need_alloc_new_item == 1) {
	pVKB = (KeyboardLayout *) keyboard_layout_new ();
	if (pVKB == NULL) {
	  break;
	}

	g_virtual_keyboards[g_virtual_keyboard_number] = pVKB;
	g_virtual_keyboard_number ++;
      }
#endif
      
      pVKB =  (KeyboardLayout *) keyboard_layout_new ();
      if (pVKB == NULL)
	break;
      g_virtual_keyboards[g_virtual_keyboard_number] = pVKB;
      g_virtual_keyboard_number ++;
      
      /* get english name of the item */
      ptr = line + 1;
      while(isspace(*ptr)) ptr++;
      ename_str = ptr;
			
      ptr = line + len - 2;
      while(isspace(*ptr)) ptr--;
      *(ptr+1) = '\0';

      if (*ename_str) {
	if (pVKB->ename) free((char *)pVKB->ename);
	pVKB->ename = (char *)strdup(ename_str);
      }

      continue;
    }
		
    if (!(strncasecmp(line, LABEL_STR, strlen(LABEL_STR)))) {
      ptr = line + strlen(LABEL_STR);
      while(*ptr && isspace(*ptr)) ptr++;
      if (*ptr) {
	if (pVKB->name_in_ko) free((char *)pVKB->name_in_ko);
	set_keyboard_label(pVKB, ptr);
      }
      continue;
    }

    if (!(strncasecmp(line, TYPE_STR, strlen(TYPE_STR)))) {
      ptr = line + strlen(TYPE_STR);
      while(*ptr && isspace(*ptr)) ptr++;
      if (*ptr) {
	if (!(strncasecmp(ptr, KEY_TYPE_STR, strlen(KEY_TYPE_STR)))) {
	  pVKB->type = KEYBOARD_KEY_TYPE;
	} else {
	  pVKB->type = KEYBOARD_STRING_TYPE;
	}
      }
      continue;
    }

    if (!(strncasecmp(line, LIST_BEGIN_STR, strlen(LIST_BEGIN_STR)))) {
      list_began = 1;
      continue;
    }

    if (!(strncasecmp(line, LIST_END_STR, strlen(LIST_END_STR)))) {
      list_began = 0;
      continue;
    }

    if (list_began == 1) {
      set_basekey_label(pVKB, line);
    }

  } while (1);

  fclose(fd);
  return(0);
}

static void
set_ctrlkey_label (KeyboardLayout *pVKB)
{
  if (pVKB == NULL) return;

  pVKB->ctrlkey[VK_BackSpace].label_str = BACKSPACE_KEY_LABEL;
  pVKB->ctrlkey[VK_Tab].label_str = TAB_KEY_LABEL;
  pVKB->ctrlkey[VK_Caps_Lock].label_str = CAPS_KEY_LABEL;
  pVKB->ctrlkey[VK_Enter].label_str = ENTER_KEY_LABEL;
  pVKB->ctrlkey[VK_Shift_L].label_str = SHIFT_KEY_LABEL;
  pVKB->ctrlkey[VK_Control_L].label_str = CTRL_KEY_LABEL;
  pVKB->ctrlkey[VK_Alt_L].label_str = ALT_KEY_LABEL;
  pVKB->ctrlkey[VK_space].label_str = SPACE_KEY_LABEL;
  pVKB->ctrlkey[VK_escape].label_str = ESCAPE_KEY_LABEL;
}

static void
set_basekey_label (KeyboardLayout *pVKB, char *line)
{
  char ch, i, index;
  char *ptr, *lower_str, *upper_str, *label_str;
	
  if (!line || !*line) return;

  ch = tolower(line[0]);

  label_str = KEYLIST_LOWER;
  index = 0;
  for (i=0; i< strlen(label_str); i++) {
    if (ch == *(label_str + i)) {
      index = i;
      break;
    }
  }
	
  /* skip spaces */
  ptr = line + 1;
  while (*ptr && isspace(*ptr)) ptr++;
  lower_str = ptr;

  /* skip to space */
  while (*ptr && !isspace(*ptr)) ptr++;
  if (*ptr) {
    *ptr = '\0';
    ptr++;
  }

  while (*ptr && isspace(*ptr)) ptr++;
  upper_str = ptr;

  while (*ptr && !isspace(*ptr)) ptr++;
  *ptr = '\0';


  if (*lower_str)
    pVKB->basekey[index].lower_str = (char *)strdup(lower_str);
  if (*upper_str)
    pVKB->basekey[index].upper_str = (char *)strdup(upper_str);

}

static void
set_keyboard_label (KeyboardLayout *pVKB, char *name_in_ko)
{
  if (!name_in_ko || !*name_in_ko) return;

  pVKB->name_in_ko = (char *)strdup(name_in_ko);
}

/* Add VKB List for KeyboardAux */
KeyboardLayout *
keyboard_layout_new (void)
{
  KeyboardLayout *pVKB;
  int i;

  pVKB = (KeyboardLayout *)calloc(1, sizeof(KeyboardLayout));
  if (pVKB == NULL)
    return(NULL);

  pVKB->ename = NULL;
  pVKB->name_in_ko = NULL;
  pVKB->type  = KEYBOARD_STRING_TYPE;
#if 0
  pVKB->encode = ENCODE_ERROR;
#endif
  set_ctrlkey_label(pVKB);

  for (i=0; i<MAX_BASEKEY_NUM; i++) {
    pVKB->basekey[i].lower_str = NULL;
    pVKB->basekey[i].upper_str = NULL;
  }

  return (pVKB);
}


static int
KeyboardItem_Free (KeyboardLayout *pVKB)
{
  int i;

  if (pVKB == NULL)
    return;

  if (pVKB->ename)
    free(pVKB->ename);
  if (pVKB->name_in_ko)
    free(pVKB->name_in_ko);

  for (i=0; i<MAX_BASEKEY_NUM; i++) {
    if (pVKB->basekey[i].lower_str)
      free(pVKB->basekey[i].lower_str);
    if (pVKB->basekey[i].upper_str)
      free(pVKB->basekey[i].upper_str);
  }

  free((char *)pVKB);
}

int
Keyboards_Init (int *n_keyboards, KeyboardLayout ***keyboards)
{
  int i, ret;
  char *lang_name;
  char path_name[1024];

  g_virtual_keyboard_number = 0;

  
  for (i=0; i<MAX_KEYBOARD_NUM + 1; i++)
    g_virtual_keyboards[i] = NULL;

  lang_name = (char *) strdup ("ko_KR");
  sprintf(path_name, "/usr/lib/im/locale/%s", lang_name);
  free (lang_name);


  ret = read_keyboard_config_file(path_name, DEFAULT_KEYBOARD_CONFIG_FILE);


  /* if not ready */
  if (ret == -1) 
    read_keyboard_layout_file(path_name, DEFAULT_KEYBOARD_LAYOUT_FILE);

  /*
    Keyboards_Print();
  */
  if (n_keyboards && keyboards){
    int count;
    *n_keyboards = g_virtual_keyboard_number;
    *keyboards =
      (KeyboardLayout **) calloc (*n_keyboards, sizeof (KeyboardLayout *));
    for (count = 0; count < *n_keyboards; count++){
      (*keyboards)[count] =
	(KeyboardLayout *) calloc (1, sizeof (KeyboardLayout));
      
    }
    *keyboards = &g_virtual_keyboards[0];
  }
}

static int
Keyboards_Done (void)
{
  int i, j;

  for (i=0; i<g_virtual_keyboard_number; i++) {
    KeyboardItem_Free (g_virtual_keyboards[i]);
    g_virtual_keyboards[i] = NULL;
  }

}

static BaseKeyStruct *
basekey_struct_new (void)
{
  BaseKeyStruct *bs;
  bs = (BaseKeyStruct *) calloc (1, sizeof (BaseKeyStruct));
  if (!bs)
    return NULL;

  bs->lower_str = NULL;
  bs->upper_str = NULL;
  
  return bs;
}

static CtrlKeyStruct *
crtlkey_struct_new (void)
{
  CtrlKeyStruct *cs;
  cs = (CtrlKeyStruct *) calloc (1, sizeof (CtrlKeyStruct));
  if (!cs)
    return NULL;
  cs->label_str = NULL;
  return cs;
}

KeyboardLayout *
keyboard_layout_struct_new (void)
{
  KeyboardLayout *kl;
  kl = (KeyboardLayout *) calloc (1, sizeof (KeyboardLayout));
  if (!kl)
    return NULL;
  kl->ename = NULL;
  kl->name_in_ko = NULL;
  kl->type = 0;
  return kl;
}

static void
basekey_struct_free (BaseKeyStruct *bs)
{
  if (bs == NULL)
    return;
  if (bs->lower_str)
    free (bs->lower_str);
  if (bs->upper_str)
    free (bs->upper_str);
}

static void
ctrlkey_struct_free (CtrlKeyStruct *cs)
{
  if (cs == NULL)
    return;
  if (cs->label_str)
    free (cs->label_str);
}

static void
copy_basekey_struct (BaseKeyStruct *dst, BaseKeyStruct *src)
{
  assert (dst != NULL && src != NULL);
  if (dst == NULL || src == NULL){
    printf ("dst, src can't be null\n");
    return;
  }
  if (src->lower_str)
    dst->lower_str = (char *)strdup (src->lower_str);
  if (src->upper_str)
    dst->upper_str = (char *)strdup (src->upper_str);
  return;
}

static void
copy_ctrlkey_struct (CtrlKeyStruct *dst, CtrlKeyStruct *src)
{
  assert (dst != NULL && src != NULL);
  if (dst == NULL || src == NULL){
    printf ("dst, src can't be null\n");
    return;
  }
  if (src->label_str)
    dst->label_str = (char *)strdup (src->label_str);
  return;
}

void
copy_keyboardlayout_struct (KeyboardLayout *dst, KeyboardLayout *src)
{
  int i;
  for (i = 0; i < MAX_BASEKEY_NUM; i++)
    copy_basekey_struct (&dst->basekey[i], &src->basekey[i]);
  for (i = 0; i < MAX_CTRLKEY_NUM; i++)
    copy_ctrlkey_struct (&dst->ctrlkey[i], &src->ctrlkey[i]);


  dst->ename = (char *)strdup (src->ename);
  dst->name_in_ko = (char *)strdup (src->name_in_ko);

  dst->type = src->type;
}

#ifdef TEST_RUN
void
Keyboards_Print (void)
{
  KeyboardLayout *pVKB;
  int i, j;
  char ch, *label_str;

  printf ("g_virtual_keyboard_number : %d\n", g_virtual_keyboard_number );

  label_str = KEYLIST_LOWER;
  for (i=0; i<g_virtual_keyboard_number; i++) {
    pVKB = g_virtual_keyboards[i];
    if (pVKB == NULL) continue;

    if (pVKB->ename)
      printf("ename:%s#\n", pVKB->ename);
    if (pVKB->name_in_ko)
      printf("name_in_ko:%s#\n", pVKB->name_in_ko);

    for (j=0; j<MAX_BASEKEY_NUM; j++) {
      if (pVKB->basekey[j].lower_str)  {
	printf("%c:#%s#       ", *(label_str + j), pVKB->basekey[j].lower_str);
	if (pVKB->basekey[j].upper_str) 
	  printf("#%s#", pVKB->basekey[j].upper_str);
	printf("\n");
      }
    }
  }
}
#endif
