/***************************************************************************
 *   Copyright (C) 2005 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 "primeprediction.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

#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/shm.h>


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

HonokaSetupCorePage *PrimePrediction::setup()
{
    bindtextdomain (GETTEXT_PACKAGE, HONOKA_LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    vector<String> l;
    HonokaSetupPage *page = new HonokaSetupPage(_("PrimePrediction-plugin"),"","");
    l.clear();
    l.push_back(String("lookup"));
    l.push_back(String("lookup_all"));
    l.push_back(String("lookup_compact"));
    l.push_back(String("lookup_compact_all"));
    l.push_back(String("lookup_direct"));
    l.push_back(String("lookup_direct_all"));
    l.push_back(String("lookup_exact"));
    l.push_back(String("lookup_expansion"));
    l.push_back(String("lookup_hybrid"));
    l.push_back(String("lookup_hybrid_all"));
    l.push_back(String("lookup_mixed"));
    page->append(new HonokaSetupSelectItem(
        _("_Lookup method: "),
        HONOKA_CONFIG_PRIME_PREDICTION_LM,
        _("select the lookup method."),
        HONOKA_DEFAULT_PRIME_PREDICTION_LM,
        l
    ));
    HonokaSetupPage *sc = new HonokaSetupPage(_("shortcut keys: "),"","");
    sc->append(new HonokaSetupKeyItem(
        _("Prime Prediction: "),
        String(HONOKA_CONFIG_KEY_PREDICTOR_PREFIX) + String("/PrimePrediction"),
        "",
        ""
    ));
    page->append(sc);

    return page;
};


PrimePrediction::PrimePrediction(ConfigPointer cfg) : Predictor(cfg)
{
    bindtextdomain (GETTEXT_PACKAGE, HONOKA_LOCALEDIR);
    bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    m_iconv.set_encoding ("EUC-JP");
    prime_pid = -1;
    mid = shmget(IPC_PRIVATE,sizeof(bool),IPC_CREAT | 0666);
    if (mid != -1) {
        con = (bool *)shmat(mid,0,0);
        con[0] = false;
        // shmat失敗時のエラー処理は？。
        // 同一プロセスで確実に同じIDなら失敗しようがない気もするが。
    }
    lookup_method = cfg->read(HONOKA_CONFIG_PRIME_PREDICTION_LM,String(HONOKA_DEFAULT_PRIME_PREDICTION_LM));
    if ((lookup_method != "lookup") &&
        (lookup_method != "lookup_all") &&
        (lookup_method != "lookup_compact") &&
        (lookup_method != "lookup_compact_all") &&
        (lookup_method != "lookup_direct") &&
        (lookup_method != "lookup_direct_all") &&
        (lookup_method != "lookup_exact") &&
        (lookup_method != "lookup_expansion") &&
        (lookup_method != "lookup_hybrid") &&
        (lookup_method != "lookup_hybrid_all") &&
        (lookup_method != "lookup_mixed")) lookup_method = String(HONOKA_DEFAULT_PRIME_PREDICTION_LM);
}


PrimePrediction::~PrimePrediction()
{
    disconnect();
    if (prime_pid != -1) {
        kill(prime_pid,SIGKILL);
        wait(NULL);
    }
    if (mid != -1) shmctl(mid,IPC_RMID,0);
}


/*!
    \fn PrimePrediction::parser(const String &str)
 */
ResultEntry PrimePrediction::parser(const String &str)
{
    vector<String> rl;
    String r;
    for(unsigned int i = 0;i < str.length();i ++) {
        if ((str[i] == '\t') || (str[i] == ' ')) {
            if (r.length()) {
                rl.push_back(r);
                r = "";
            }
        } else r += str[i];
    }
    if (r.length()) rl.push_back(r);
    
    if (rl.size() < 2) return ResultEntry();
    WideString w,a;
    for(unsigned int i = 2;i < rl.size();i ++) {
        if (rl[i].length() < 7) continue;
        if (rl[i].substr(0,6) == "usage=") m_iconv.convert(a,rl[i].substr(6));
    }
    m_iconv.convert(w,rl[1]);
    if (!a.length()) a = w;
    else a = w + utf8_mbstowcs(String(" (")) + a + utf8_mbstowcs(String(")"));
    return ResultEntry(w,a);
}

/*!
    \fn PrimePrediction::getPredictionList(const WideString &str)
 */
ResultList PrimePrediction::getPredictionList(const WideString &str)
{
    ResultList l;
    if (mid == -1) return l;
    if (!con[0]) return l;
    String s = lookup_method + "\t";
    String t;
    m_iconv.convert(t,str);
    s = s + t + String("\n");
    write(ifd,s.c_str(),s.length());
    char b[1024];
    vector<String> sl;
    s = "";
    while(-1) {
        int c = read(ofd,b,1023);
        s += String(b,c);
        if (s.substr(s.length() - 2,2) == "\n\n") break;
    }
    t = "";
    for(unsigned int i = 0;i < s.length();i ++) {
        if (s[i] == '\n') {
            sl.push_back(t);
            t = "";
        } else t += s[i];
    }
    if (t.length()) sl.push_back(t);
    l.Title = utf8_mbstowcs(String(_("lookup result")));
    l.kType = PREDICTION;
    for(unsigned int i = 1;i < sl.size();i ++) {
        ResultEntry e = parser(sl[i]);
        if (e.kanji.length()) l.kouho.push_back(e);
    }
    return l;
}


/*!
    \fn PrimePrediction::disconnect()
 */
void PrimePrediction::disconnect()
{
    if (mid == -1) return;
    if (con[0]) return;
    String s = "close\n";
    write(ifd,s.c_str(),s.length());
    close(ifd);
    close(ofd);
    con[0] = false;
    prime_pid = -1;
}


/*!
    \fn PrimePrediction::connect()
 */
bool PrimePrediction::connect()
{
    if (mid == -1) return false;
    
    // primeチェーック！
    int ps;
    if (fork() == 0) {
        execlp("prime","--help",NULL);
        _exit(0);
    }
    wait(&ps);
    if (!WIFEXITED(ps)) return false;
    if (WEXITSTATUS(ps) != 0) return false;
    // primeは動いてるっぽい。以下。
    
    int in_fd[2],out_fd[2],i;
    if (con[0]) return true;
    if (pipe(in_fd) == -1) return false;
    if (pipe(out_fd) == -1) return false;
    prime_pid = fork();
    switch(prime_pid) {
        case -1: {
            close(in_fd[0]);
            close(in_fd[1]);
            close(out_fd[0]);
            close(out_fd[1]);
            con[0] = false;
            return false;
        }
        case 0: {
            close(in_fd[1]);
            close(out_fd[0]);
            dup2(in_fd[0],0);
            dup2(out_fd[1],1);
            close(in_fd[0]);
            close(out_fd[1]);
            con[0] = true;
            execlp("prime",NULL);
            con[0] = false;
            _exit(0);
        }
    }
    close(in_fd[0]);
    close(out_fd[1]);
    ifd = in_fd[1];
    ofd = out_fd[0];
    return true;
}


/*!
    \fn PrimePrediction::isConnected()
 */
bool PrimePrediction::isConnected()
{
    if (mid == -1) return false;
    return con[0];
}


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

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

/*!
    \fn PrimePrediction::update(const WideString str,const WideString yomi)
 */
void PrimePrediction::update(const WideString str,const WideString yomi)
{
}



