/* Copyright (C) 1994-2011 Edward Der-Hua Liu, Hsin-Chu, Taiwan
 *
 * This library 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 version 2.1
 * of the License.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdio.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>

#include "hime.h"

#include "gtab-buf.h"
#include "gtab.h"

struct keystruc {
    char *kname;
    KeySym ksym;
    char *str;
    char *str_caps;
};

struct keystruc tran[] = {
    {"`", '~'},
    {"0", ')'},
    {"1", '!'},
    {"2", '@'},
    {"3", '#'},
    {"4", '$'},
    {"5", '%'},
    {"6", '^'},
    {"7", '&'},
    {"8", '*'},
    {"9", '('},
    {"a", 'a'},
    {"b", 'b'},
    {"c", 'c'},
    {"d", 'd'},
    {"e", 'e'},
    {"f", 'f'},
    {"g", 'g'},
    {"h", 'h'},
    {"i", 'i'},
    {"j", 'j'},
    {"k", 'k'},
    {"l", 'l'},
    {"m", 'm'},
    {"n", 'n'},
    {"o", 'o'},
    {"p", 'p'},
    {"q", 'q'},
    {"r", 'r'},
    {"s", 's'},
    {"t", 't'},
    {"u", 'u'},
    {"v", 'v'},
    {"w", 'w'},
    {"x", 'x'},
    {"y", 'y'},
    {"z", 'z'},
    {",", '<'},
    {".", '>'},
    {";", ':'},
    {"'", '"'},
    {"/", '?'},
    {"[", '{'},
    {"]", '}'},
    {"\\", '|'},
    {"-", '_'},
    {"=", '+'},
    {"f1", XK_F1},
    {"f2", XK_F2},
    {"f3", XK_F3},
    {"f4", XK_F4},
    {"f5", XK_F5},
    {"f6", XK_F6},
    {"f7", XK_F7},
    {"f8", XK_F8},
    {"f9", XK_F9},
    {"f10", XK_F10},
    {"f11", XK_F11},
    {"f12", XK_F12},
    {"left", XK_Left},
    {"right", XK_Right},
    {"down", XK_Down},
    {"up", XK_Up},
    {"k_ins", XK_KP_Insert},
    {"k_del", XK_KP_Delete},
    {"k_end", XK_KP_End},
    {"k_down", XK_KP_Down},
    {"k_pgup", XK_KP_Prior},
    {"k_up", XK_KP_Up},
    {"k_pgdn", XK_KP_Next},
    {"k_left", XK_KP_Left},
    {"k_5", XK_KP_Begin},
    {"k_right", XK_KP_Right},
    {"k_home", XK_KP_Home},
    {"k_up", XK_Up},
    {"k_pgup", XK_Prior},
    {"kp0", XK_KP_0},
    {"kp.", XK_KP_Decimal},
    {"kp1", XK_KP_1},
    {"kp2", XK_KP_2},
    {"kp3", XK_KP_3},
    {"kp4", XK_KP_4},
    {"kp5", XK_KP_5},
    {"kp6", XK_KP_6},
    {"kp7", XK_KP_7},
    {"kp8", XK_KP_8},
    {"kp9", XK_KP_9},
    {"kp/", XK_KP_Divide},
    {"kp*", XK_KP_Multiply},
    {"kp-", XK_KP_Subtract},
    {"kp+", XK_KP_Add},
    {"kpenter", XK_KP_Enter}};

struct keystruc tran_ctrl[] = {
    {",", ','},
    {".", '.'},
    {";", ';'},
    {"'", '\''},
    {"/", '/'},
    {"?", '?'},
    {"[", '['},
    {"]", ']'},
    {":", ':'},
    {"{", '{'},
    {"}", '}'},
    {"<", '<'},
    {">", '>'},
    {"\"", '"'},
};

int tranN = sizeof (tran) / sizeof (tran[0]);
int tran_ctrlN = sizeof (tran_ctrl) / sizeof (tran_ctrl[0]);
extern char *TableDir;

FILE *watch_fopen (char *filename, time_t *pfile_modify_time);
gboolean output_gbuf ();
gboolean gtab_cursor_end ();
gboolean gtab_phrase_on (), tsin_cursor_end ();
void flush_tsin_buffer ();
void add_to_tsin_buf_str (char *str);

static time_t file_modify_time;
static time_t ctrl_file_modify_time;

void load_phrase (char *fname, time_t *modtime, struct keystruc *tr, int trN) {
    FILE *fp;
    char kname[32];
    char ttt[512];

    //  dbg("load_phrase %s\n", fname);
    if ((fp = watch_fopen (fname, modtime)) == NULL) {
        return;
    }

    //  dbg("load succcc %s\n", fname);
    skip_utf8_sigature (fp);

    while (!feof (fp)) {
        int i, j;
        char str[512];

        kname[0] = str[0] = 0;
        myfgets (ttt, sizeof (ttt), fp);
        if (ttt[0] == '#')
            continue;
        for (i = 0; ttt[i] != ' ' && ttt[i] != 9 && ttt[i]; i++)
            kname[i] = ttt[i];

        kname[i] = 0;
        gboolean is_upper = FALSE;

        if (!(kname[0] & 0x80) && isupper (kname[0])) {
            is_upper = TRUE;
            kname[0] = tolower (kname[0]);
        }

        while ((ttt[i] == ' ' || ttt[i] == 9) && ttt[i])
            i++;

        for (j = 0; ttt[i] != '\n' && ttt[i]; i++, j++)
            str[j] = ttt[i];

        if (!str[0] || !kname[0])
            continue;

        str[j] = 0;

        for (i = 0; i < trN; i++)
            if (!strcmp (kname, tr[i].kname))
                break;
        if (i == trN) {
            dbg ("unknown key: %s\n", kname);
            continue;
        }

        if (is_upper)
            tr[i].str_caps = strdup (str);
        else
            tr[i].str = strdup (str);
    }
}

void free_phrase () {
    int i;

    for (i = 0; i < tranN; i++)
        free (tran[i].str);
}

gboolean feed_phrase (KeySym ksym, int state) {
    int i;

    //  dbg("ksym:%x %c\n", ksym, ksym);
    load_phrase ("phrase.table", &file_modify_time, tran, tranN);
    load_phrase ("phrase-ctrl.table", &ctrl_file_modify_time, tran_ctrl, tran_ctrlN);

    if (ksym < 0x7f && isupper (ksym))
        ksym = tolower (ksym);

    struct keystruc *tr;
    int trN;

    if (state & ControlMask) {
        tr = tran_ctrl;
        trN = tran_ctrlN;
    } else {
        tr = tran;
        trN = tranN;
    }

    char *str;

    for (i = 0; i < trN; i++) {
        if (tr[i].ksym != ksym)
            continue;

        str = ((state & LockMask) && tr[i].str_caps) ? tr[i].str_caps : tr[i].str;

        if (str) {
            // send_it:
            if (current_method_type () == method_type_TSIN && current_CS->b_im_enabled) {
                add_to_tsin_buf_str (str);
                if (hime_punc_auto_send && tsin_cursor_end ())
                    flush_tsin_buffer ();
            } else if (gtab_phrase_on ()) {
                insert_gbuf_nokey (str);
                if (hime_punc_auto_send && gtab_cursor_end ())
                    output_gbuf ();
            } else
                send_text (str);
            return TRUE;
        }
    }

#if 0
  char tt[2];
  if ((state&(ControlMask|ShiftMask|Mod1Mask|Mod4Mask|Mod5Mask))==ShiftMask && ksym>=' ' && ksym < 0x7e) {
    str = tt;
    tt[0]=ksym;
    tt[1]=0;
    goto send_it;
  }
#endif

    return FALSE;
}
