/***************************************************************************
 *   Copyright (C) 2004 by TAM(Teppei Tamra)                               *
 *   tam-t@par.odn.ne.jp                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "nicolainput.h"

#include <sys/time.h>

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

// 国際化のおまじない。
#ifdef HAVE_GETTEXT
  #include <libintl.h>
  #define _(String) dgettext(GETTEXT_PACKAGE,String)
  #define N_(String) (String)
#else
  #define _(String) (String)
  #define N_(String) (String)
  #define bindtextdomain(Package,Directory)
  #define textdomain(domain)
  #define bind_textdomain_codeset(domain,codeset)
#endif

// プライグイン化のおまじないです。
HonokaPluginRegister(NicolaInput);
HonokaPluginSetup(NicolaInput);

HonokaSetupCorePage *NicolaInput::setup()
{
    bindtextdomain (GETTEXT_PACKAGE, HONOKA_LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    HonokaSetupPage *page = new HonokaSetupPage(_("Nicola-plugin"),"","");
    page->append(new HonokaSetupKeyItem(
        _("_Left KANA-Shift keys:"),
        HONOKA_CONFIG_NICOLAINPUT_LSHIFT,
        "",
        HONOKA_DEFAULT_NICOLAINPUT_LSHIFT
    ));
    page->append(new HonokaSetupKeyItem(
        _("_Right KANA-Shift keys:"),
        HONOKA_CONFIG_NICOLAINPUT_RSHIFT,
        "",
        HONOKA_DEFAULT_NICOLAINPUT_RSHIFT
    ));
    page->append(new HonokaSetupIntItem(
        _("_Simultaneously press timer:"),
        HONOKA_CONFIG_NICOLAINPUT_SPTIMER,
        "",
        HONOKA_DEFAULT_NICOLAINPUT_SPTIMER,
        0,199
    ));
    page->append(new HonokaSetupFileItem(
        _("Nicola _table: "),
        HONOKA_CONFIG_NICOLAINPUT_TABLE_FILE,
        _("input the path of Nicola table file."),
        HONOKA_DEFAULT_NICOLAINPUT_TABLE_FILE
    ));

    HonokaSetupPage *sc = new HonokaSetupPage(_("shortcut keys: "),"","");
    sc->append(new HonokaSetupKeyItem(
        _("Nicola input: "),
        String(HONOKA_CONFIG_KEY_PREEDITOR_PREFIX) + String("/Nicola"),
        "",
        ""
    ));
    page->append(sc);

    return page;
};

namespace Honoka {
struct {
    KeyCode key;
    char noShift[6];
    char leftShift[6];
    char rightShift[6];
} _nicolaTable[] = {
    {SCIM_KEY_1,"1","?",""},
    {SCIM_KEY_2,"2","/",""},
    {SCIM_KEY_3,"3","~",""},
    {SCIM_KEY_4,"4","「",""},
    {SCIM_KEY_5,"5","」",""},
    
    {SCIM_KEY_6,"6","","["},
    {SCIM_KEY_7,"7","","]"},
    {SCIM_KEY_8,"8","","("},
    {SCIM_KEY_9,"9","",")"},
    {SCIM_KEY_0,"0","","『"},
    {SCIM_KEY_minus,"-","","』"},
    {SCIM_KEY_backslash,"\\","",""},

    {SCIM_KEY_q,"。","ぁ",""},
    {SCIM_KEY_w,"か","え","が"},
    {SCIM_KEY_e,"た","り","だ"},
    {SCIM_KEY_r,"こ","ゃ","ご"},
    {SCIM_KEY_t,"さ","れ","ざ"},
    
    {SCIM_KEY_y,"ら","ぱ","よ"},
    {SCIM_KEY_u,"ち","ぢ","に"},
    {SCIM_KEY_i,"く","ぐ","る"},
    {SCIM_KEY_o,"つ","づ","ま"},
    {SCIM_KEY_p,",","ぴ","ぇ"},
    {SCIM_KEY_at,"、","",""},
    {SCIM_KEY_bracketleft,"゛","","゜"},


    {SCIM_KEY_a,"う","を",""},
    {SCIM_KEY_s,"し","あ","じ"},
    {SCIM_KEY_d,"て","な","で"},
    {SCIM_KEY_f,"け","ゅ","げ"},
    {SCIM_KEY_g,"せ","も","ぜ"},
    
    {SCIM_KEY_h,"は","ば","み"},
    {SCIM_KEY_j,"と","ど","お"},
    {SCIM_KEY_k,"き","ぎ","の"},
    {SCIM_KEY_l,"い","ぽ","ょ"},
    {SCIM_KEY_semicolon,"ん","","っ"},

    {SCIM_KEY_z,".","ぅ",""},
    {SCIM_KEY_x,"ひ","ー","び"},
    {SCIM_KEY_c,"す","ろ","ず"},
    {SCIM_KEY_v,"ふ","や","ぶ"},
    {SCIM_KEY_b,"へ","ぃ","べ"},
    
    {SCIM_KEY_n,"め","ぷ","ぬ"},
    {SCIM_KEY_m,"そ","","ゆ"},
    {SCIM_KEY_comma,"ね","ぺ","む"},
    {SCIM_KEY_period,"ほ","ぼ","わ"},
    {SCIM_KEY_slash,"・","","ぉ"},

    {SCIM_KEY_NullKey,"","",""} // terminator
};
}


NicolaInput::NicolaInput(ConfigPointer cfg) : PreEditor(cfg)
{
    bindtextdomain (GETTEXT_PACKAGE, HONOKA_LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    bkt = 0;
    delay = cfg->read(HONOKA_CONFIG_NICOLAINPUT_SPTIMER,HONOKA_DEFAULT_NICOLAINPUT_SPTIMER);
    scim_string_to_key_list(LShiftKey,cfg->read(HONOKA_CONFIG_NICOLAINPUT_LSHIFT,String(HONOKA_DEFAULT_NICOLAINPUT_LSHIFT)));
    scim_string_to_key_list(RShiftKey,cfg->read(HONOKA_CONFIG_NICOLAINPUT_RSHIFT,String(HONOKA_DEFAULT_NICOLAINPUT_RSHIFT)));
    String table = cfg->read(HONOKA_CONFIG_NICOLAINPUT_TABLE_FILE,String(HONOKA_DEFAULT_NICOLAINPUT_TABLE_FILE));
    LShift = false;
    RShift = false;

    if (table.length()) loadTable(table);
    if (!keymap.size()) {
        for(unsigned int i = 0;_nicolaTable[i].key != SCIM_KEY_NullKey;i ++) {
            NicolaKey k;
            k.noShift = utf8_mbstowcs(_nicolaTable[i].noShift);
            k.leftShift = utf8_mbstowcs(_nicolaTable[i].leftShift);
            k.rightShift = utf8_mbstowcs(_nicolaTable[i].rightShift);
            keymap.insert(pair<KeyEvent,NicolaKey>(_nicolaTable[i].key,k));
        }
    }
    vsc = utf8_mbstowcs(String("゛"));
    svsc = utf8_mbstowcs(String("゜"));
    vs1 = utf8_mbstowcs(String("かきくけこさしすせそたちつてとはひふへほぱぴぷぺぽ"));
    vs2 = utf8_mbstowcs(String("がぎぐげござじずぜぞだぢづでどばびぶべぼばびぶべぼ"));
    svs1 = utf8_mbstowcs(String("はひふへほばびぶべぼ"));
    svs2 = utf8_mbstowcs(String("ぱぴぷぺぽぱぴぷぺぽ"));
}


NicolaInput::~NicolaInput()
{
}


/*!
    \fn Honoka::NicolaInput::loadTable(const String &filename)
 */
bool Honoka::NicolaInput::loadTable(const String &filename)
{
    FILE *f = fopen(filename.c_str(),"r");
    if (!f) {
        #ifdef HONOKA_DATADIR
        String fn = HONOKA_DATADIR;
        fn += "/" + filename;
        f = fopen(fn.c_str(),"r");
        if (!f) return false;
        #else
        return false;
        #endif
    }
    
    while(-1) {
        char s[256];
        if(fgets(s,256,f) == NULL) break;
        String l = s;
        unsigned int p = 0;
        vector<String> t;
        for(unsigned int i = 0;i < l.length();i ++) {
            if ((l[i] == '\t') || (l[i] == '\n')) {
                t.push_back(l.substr(p,i - p));
                p = i + 1;
            }
        }
        if (p < l.length()) t.push_back(l.substr(p));
        if (t.size() != 4) continue;
        KeyEvent k;
        NicolaKey n;
        n.noShift = utf8_mbstowcs(t[1]);
        n.leftShift = utf8_mbstowcs(t[2]);
        n.rightShift = utf8_mbstowcs(t[3]);
        scim_string_to_key(k,t[0]);
        keymap.insert(pair<KeyEvent,NicolaKey>(k,n));
    }
    fclose(f);
    if (keymap.size()) return true;
    return false;
}


/*!
    \fn NicolaInput::getModeName()
 */
String NicolaInput::getModeName()
{
    return String(_("KANA"));
}

/*!
    \fn NicolaInput::getName()
 */
String NicolaInput::getName()
{
    return String("Nicola");
}

/*!
    \fn NicolaInput::getPropertyName()
 */
String NicolaInput::getPropertyName()
{
    return String(_("NICOLA"));
}


/*!
    \fn NicolaInput::inputEvent(const KeyEvent &key)
 */
bool NicolaInput::inputEvent(const KeyEvent &key)
{
    if (PreEditor::isThrough(key)) return true;


    map<KeyEvent,NicolaKey>::iterator it = keymap.find(key);
    if (it == keymap.end()) {
        if (isPrintable(key)) {
            text = text.substr(0,pos) + key.get_unicode_code() + text.substr(pos);
            pos ++;
            return true;
        } else return false;
    }
    WideString w;
    if (LShift) w = it->second.leftShift;
    else if (RShift) w = it->second.rightShift;
    else w = it->second.noShift;
    bk = key;
    if (w == vsc) {
        for(unsigned int i = 0;i < vs1.length();i ++) {
            if (vs1[i] == text[pos - 1]) text = text.substr(0,pos - 1) + vs2[i] + text.substr(pos);
        }
    } else
    if (w == svsc) {
        for(unsigned int i = 0;i < svs1.length();i ++) {
            if (svs1[i] == text[pos - 1]) text = text.substr(0,pos - 1) + svs2[i] + text.substr(pos);
        }
    } else
    if (w.length()) {
        struct timeval tv;
        gettimeofday(&tv, NULL);
        bkt = (tv.tv_sec * 1000000) + tv.tv_usec;
        text = text.substr(0,pos) + w + text.substr(pos);
        pos ++;
    }
    return true;
    //return false;
}



/*!
    \fn NicolaInput::keyEventHook(const KeyEvent &key)
 */
bool NicolaInput::keyEventHook(const KeyEvent &key)
{
    for(unsigned int i = 0;i < RShiftKey.size();i ++) {
        if (key.code == RShiftKey[i].code) {
            if (key.is_key_press()) {
                RShift = true;
                struct timeval tv;
                gettimeofday(&tv, NULL);
                long t = (tv.tv_sec * 1000000) + tv.tv_usec;
                if ((t - bkt) < (delay * 1000)) {
                    backspace();
                    return(inputEvent(bk));
                }
            }
            else if (key.is_key_release()) RShift = false;
            return true;
        }
    }
    for(unsigned int i = 0;i < LShiftKey.size();i ++) {
        if (key.code == LShiftKey[i].code) {
            if (key.is_key_press()) {
                LShift = true;
                struct timeval tv;
                gettimeofday(&tv, NULL);
                long t = (tv.tv_sec * 1000000) + tv.tv_usec;
                if ((t - bkt) < (delay * 1000)) {
                    backspace();
                    return(inputEvent(bk));
                }
            }
            else if (key.is_key_release()) LShift = false;
            return true;
        }
    }
    return false;
}

