/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */

/*
 *  Copyright (C) 2007-2009 Kouhei Sutou <kou@cozmixng.org>
 *  Copyright (C) 2006 Eriko Sato
 *
 *  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, 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 <stdio.h>
#include <glib/gi18n.h>
#include "kz-rb-ext.h"

gchar *_kz_rb_ext_version = NULL;

#ifdef POSIX_SIGNAL
#define ruby_signal(sig, handle) posix_signal((sig), (handle))
#else
#define ruby_signal(sig, handle) signal((sig), (handle))
#endif

#define INIT_PRE_RB "kazehakase-init-pre"
#define INIT_RB "kazehakase-init"

extern VALUE rb_load_path;
void Init_stack _((VALUE *));

static VALUE mKz;

ID rbgobj_id_children;

static VALUE
rb_kz_gettext(VALUE self, VALUE msgid)
{
    return CSTR2RVAL(_(RVAL2CSTR(msgid)));
}

static VALUE
rb_kz_get_backends(VALUE self)
{
    GList *backends, *node;
    VALUE rb_backends;

    rb_backends = rb_ary_new();
    backends = kz_embed_engine_names();
    for (node = backends; node; node = g_list_next(node)) {
        gchar *name = node->data;

        rb_ary_push(rb_backends, rb_str_new2(name));
        g_free(name);
    }
    g_list_free(backends);

    return rb_backends;
}

static void
Init_kz (void)
{
    const gchar *user_dir;
    gchar *actions_dir, *sidebars_dir;

    mKz = rb_define_module("Kz");

    rb_define_const(mKz, "VERSION", rb_str_new2(VERSION));
    rb_define_const(mKz, "URI", rb_str_new2(KAZEHAKASE_URI));
    rb_define_const(mKz, "PACKAGE", rb_str_new2(PACKAGE));

    user_dir = KZ_GET_USER_DIR;
    actions_dir = g_build_filename(user_dir, "actions", NULL);
    sidebars_dir = g_build_filename(user_dir, "sidebars", NULL);
    rb_define_const(mKz, "USER_DIR", rb_str_new2(user_dir));
    rb_define_const(mKz, "ACTIONS_DIR", rb_str_new2(actions_dir));
    rb_define_const(mKz, "SIDEBARS_DIR", rb_str_new2(sidebars_dir));
    g_free(actions_dir);
    g_free(sidebars_dir);

    rb_define_module_function(mKz, "gettext", rb_kz_gettext, 1);
    rb_define_module_function(mKz, "backends", rb_kz_get_backends, 0);
}

static void
kz_rb_ext_ruby_init(void)
{
    RETSIGTYPE (*sigint_handler)_((int));
#ifdef SIGHUP
    RETSIGTYPE (*sighup_handler)_((int));
#endif
#ifdef SIGQUIT
    RETSIGTYPE (*sigquit_handler)_((int));
#endif
#ifdef SIGTERM
    RETSIGTYPE (*sigterm_handler)_((int));
#endif
#ifdef SIGSEGV
    RETSIGTYPE (*sigsegv_handler)_((int));
#endif

    sigint_handler = signal(SIGINT, SIG_DFL);
#ifdef SIGHUP
    sighup_handler = signal(SIGHUP, SIG_DFL);
#endif
#ifdef SIGQUIT
    sigquit_handler = signal(SIGQUIT, SIG_DFL);
#endif
#ifdef SIGTERM
    sigterm_handler = signal(SIGTERM, SIG_DFL);
#endif
#ifdef SIGSEGV
    sigsegv_handler = signal(SIGSEGV, SIG_DFL);
#endif

    ruby_init();

    ruby_signal(SIGINT, sigint_handler);
#ifdef SIGHUP
    ruby_signal(SIGHUP, sighup_handler);
#endif
#ifdef SIGQUIT
    ruby_signal(SIGQUIT, sigquit_handler);
#endif
#ifdef SIGTERM
    ruby_signal(SIGTERM, sigterm_handler);
#endif
#ifdef SIGSEGV
    ruby_signal(SIGSEGV, sigsegv_handler);
#endif
}

static VALUE
_require_init_pre_rb (VALUE user_data)
{
    rb_funcall(Qnil, rb_intern("require"), 1, rb_str_new2(INIT_PRE_RB));
    return Qnil;
}

static VALUE
_require_init_pre_rb_rescue (VALUE user_data, VALUE error)
{
    VALUE class_name, message;

    rb_define_const(mKz, "ENABLE", Qfalse);

    g_warning("failed to load: %s", INIT_PRE_RB);

    class_name = rb_funcall(rb_funcall(error, rb_intern("class"), 0),
                            rb_intern("name"), 0);
    message = rb_funcall(error, rb_intern("message"), 0);
    g_warning("                %s: %s",
              StringValueCStr(class_name), StringValueCStr(message));

    return Qnil;
}

static void
require_init_pre_rb (void)
{
    rb_rescue2(_require_init_pre_rb, Qnil,
               _require_init_pre_rb_rescue, Qnil, rb_eException,
               NULL);
}

void
kz_rb_ext_init (gpointer initial_address)
{
    char *argv[] = {"kazehakase"};

    kz_rb_ext_ruby_init();

    Init_stack(initial_address);

    ruby_init_loadpath();

    ruby_script("kazehakase");
    ruby_set_argv(1, argv);

    if (!_kz_rb_ext_version) {
        VALUE version;
        version = rb_const_get(rb_cObject, rb_intern("VERSION"));
        _kz_rb_ext_version = g_strdup(StringValueCStr(version));
    }

    rb_ary_unshift(rb_load_path, rb_str_new2(KZ_GET_SYSTEM_RUBY_EXT_DIR));
    rb_ary_unshift(rb_load_path, rb_str_new2(KZ_GET_SYSTEM_RUBY_EXT_DATA_DIR));

    Init_kz();
    require_init_pre_rb();
    if (RTEST(rb_const_get(mKz, rb_intern("ENABLE")))) {
        _kz_rb_app_init(mKz, kz_app_get());
        _kz_rb_window_init(mKz);
        _kz_rb_notebook_init(mKz);
        _kz_rb_statusbar_init(mKz);
        _kz_rb_embed_init(mKz);
        _kz_rb_embed_event_init(mKz);
        _kz_rb_conf_init(mKz);
        _kz_rb_downloader_init(mKz);
        _kz_rb_downloader_group_init(mKz);
        _kz_rb_bookmark_init(mKz);
        _kz_rb_web_init(mKz);
        _kz_rb_sidebar_init(mKz);

        rb_funcall(Qnil, rb_intern("require"), 1, rb_str_new2(INIT_RB));
    }
}

void
kz_rb_ext_setup (KzWindow *kz)
{
    if (RTEST(rb_const_get(mKz, rb_intern("ENABLE")))) {
        rb_funcall(mKz, rb_intern("setup"), 1, GOBJ2RVAL(kz));
        _kz_rb_setup_sidebar(kz);
    }
}

void
kz_rb_ext_cleanup (void)
{
#ifndef G_OS_WIN32
    struct sigaction act;
#endif

    rb_funcall(mKz, rb_intern("exit"), 0);

#ifndef G_OS_WIN32
    /* ruby_cleanup sets SIGINT to default */
    sigaction(SIGINT,	NULL, &act);
#endif
    
    ruby_cleanup(0);
    
#ifndef G_OS_WIN32
    /* again set original action of SIGINT */
    sigaction(SIGINT, &act, NULL);
#endif

    g_free(_kz_rb_ext_version);
    _kz_rb_ext_version = NULL;
}
