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

  rbglib.c -

  $Author: sakai $
  $Date: 2007/07/14 13:42:08 $

  Copyright (C) 2002,2003  Masahiro Sakai

**********************************************************************/

#include <locale.h>
#include "rbgprivate.h"
#include "rbglib.h"

static ID id_inspect;

VALUE mGLib;

extern void Init_gutil();
extern void Init_gutil_callback();
extern void Init_glib_int64();
extern void Init_glib_error();
extern void Init_glib_threads();
extern void Init_glib_convert();
extern void Init_glib_messages();
extern void Init_glib_spawn();
extern void Init_glib_fileutils();
extern void Init_glib_utils();
extern void Init_glib_i18n();
extern void Init_glib_win32();
extern void Init_gobject();
extern void Init_glib_main_loop();
extern void Init_glib_main_context();
extern void Init_glib_source();
extern void Init_glib_poll_fd();
extern void Init_glib_io_channel();
extern void Init_glib_shell();
extern void Init_glib_completion();
extern void Init_glib_timer();
extern void Init_glib_unicode();
extern void Init_glib_keyfile();
extern void Init_glib_bookmark_file();

gchar *
rbg_rval2cstr(VALUE *str)
{
    StringValue(*str);
#ifdef HAVE_RUBY_ENCODING_H
    if (rb_enc_get(*str) != rb_utf8_encoding()) {
        *str = rb_str_export_to_enc(*str, rb_utf8_encoding());
    }
#endif
    return RSTRING_PTR(*str);
}

const gchar *
rbg_rval_inspect(VALUE object)
{
    VALUE inspected;

    inspected = rb_funcall(object, id_inspect, 0);
    return RVAL2CSTR(inspected);
}

char *
rbg_string_value_ptr(volatile VALUE *ptr)
{
    return rb_string_value_ptr(ptr);
}

gchar *
rbg_rval2cstr_accept_nil(VALUE *str)
{
    return NIL_P(*str) ? NULL : RVAL2CSTR(*str);
}

VALUE
rbg_cstr2rval(const gchar* str)
{
    if (!str)
        return Qnil;

    return CSTR2RVAL_LEN(str, strlen(str));
}

VALUE
rbg_cstr2rval_len(const gchar* str, gsize len)
{
    if (!str)
        return Qnil;

#ifdef HAVE_RUBY_ENCODING_H
    return rb_external_str_new_with_enc(str, len, rb_utf8_encoding());
#else
    return rb_str_new(str, len);
#endif
}

VALUE
rbg_cstr2rval_with_encoding(const gchar* str, const gchar *encoding)
{
    if (!str)
        return Qnil;

    return CSTR2RVAL_LEN_ENC(str, strlen(str), encoding);
}

VALUE
rbg_cstr2rval_len_with_encoding(const gchar* str, gsize len,
                                const gchar *encoding)
{
    if (!str)
        return Qnil;

#ifdef HAVE_RUBY_ENCODING_H
    {
        rb_encoding *rb_encoding;

        if (encoding) {
            rb_encoding = rb_enc_find(encoding);
        } else {
            rb_encoding = rb_utf8_encoding();
        }
        return rb_external_str_new_with_enc(str, len, rb_encoding);
    }
#else
    return rb_str_new(str, len);
#endif
}

static VALUE
rbg_cstr2rval_with_free_body(VALUE str)
{
    return CSTR2RVAL((const gchar *)str);
}

static VALUE
rbg_cstr2rval_with_free_ensure(VALUE str)
{
    g_free((gchar *)str);

    return Qnil;
}

VALUE
rbg_cstr2rval_with_free(gchar *str)
{
    return str ? rb_ensure(rbg_cstr2rval_with_free_body, (VALUE)str,
                           rbg_cstr2rval_with_free_ensure, (VALUE)str) : Qnil;
}

#ifdef HAVE_RUBY_ENCODING_H
static rb_encoding *filename_encoding_if_not_utf8;
#endif

VALUE
rbg_filename_to_ruby_free(gchar *filename)
{
#ifdef HAVE_RUBY_ENCODING_H
    gchar *filename_utf8;
    GError *error = NULL;
    gsize written;
    VALUE rb_filename = Qnil;

    if (filename == NULL)
        return Qnil;
       
    /* convert filename to UTF-8 if needed */
    if (filename_encoding_if_not_utf8 != NULL) {
        filename_utf8 = g_filename_to_utf8(filename, -1, NULL, &written, &error);
        g_free(filename);
        if (error != NULL)
            RAISE_GERROR(error);
    } else {
        filename_utf8 = filename;
    }

    /* create Ruby String in UTF-8 */
    rb_filename = rb_external_str_new_with_enc(filename_utf8,
                                               strlen(filename_utf8),
                                               rb_utf8_encoding());
    g_free(filename_utf8);

    /* if needed, change encoding of Ruby String to filename encoding, so that
       upcoming File operations will work properly */
    if (filename_encoding_if_not_utf8 != NULL) {
        return rb_str_export_to_enc(rb_filename, filename_encoding_if_not_utf8);
    } else {
        return rb_filename;
    }

#else
    return CSTR2RVAL_FREE(filename);
#endif
}

gchar *
rbg_filename_from_ruby(VALUE filename)
{
#ifdef HAVE_RUBY_ENCODING_H
    GError *error = NULL;
    gchar *retval;
    gsize written;

    /* if needed, change encoding of Ruby String to UTF-8 */
    StringValue(filename);
    if (rb_enc_get(filename) != rb_utf8_encoding()) {
        filename = rb_str_export_to_enc(filename, rb_utf8_encoding());
    }

    /* convert it to filename encoding if needed */
    if (filename_encoding_if_not_utf8 != NULL) {
        retval = g_filename_from_utf8(RSTRING_PTR(filename), -1, NULL, &written, &error);
        if (error != NULL)
            RAISE_GERROR(error);
    } else {
        retval = RSTRING_PTR(filename);
    }

    return retval;

#else
    return RVAL2CSTR(filename);
#endif
}

VALUE
rbg_filename_gslist_to_array_free(GSList *list)
{
    GSList *l;
    VALUE ary = rb_ary_new();
    for (l = list; l != NULL; l = g_slist_next(l)) {
        rb_ary_push(ary, rbg_filename_to_ruby_free(l->data));
    }
    g_slist_free(list);
    return ary;
}


#if 0
/*
2004-04-15 Commented out by Masao.

These functions replace g_malloc/g_realloc/g_free of GLib.
When g_malloc is called and the memory area can not reserved,
rb_gc() will be called. It makes Ruby-GNOME2 uses memory efficiently.

But rb_gc() does not work under multithread.
So they occur "cross-thread violation".
*/

static gpointer
my_malloc(gsize n_bytes)
{
    /* Should we rescue NoMemoryError? */ 
    return ruby_xmalloc(n_bytes);
}

static gpointer
my_realloc(gpointer mem, gsize n_bytes)
{
    /* Should we rescue NoMemoryError? */ 
    return ruby_xrealloc(mem, n_bytes);
}

static void
my_free(gpointer mem)
{
    return ruby_xfree(mem);
}

static void
Init_mem()
{
    GMemVTable mem_table = {
        my_malloc,
        my_realloc,
        my_free,
        NULL,
        NULL,
        NULL,
    };
    g_mem_set_vtable(&mem_table);
}
#endif

static VALUE
rbg_s_os_win32(VALUE self)
{
#ifdef G_OS_WIN32
    return Qtrue;
#else
    return Qfalse;
#endif
}

static VALUE
rbg_s_os_beos(VALUE self)
{
#ifdef G_OS_BEOS
    return Qtrue;
#else
    return Qfalse;
#endif
}

static VALUE
rbg_s_os_unix(VALUE self)
{
#ifdef G_OS_UNIX
    return Qtrue;
#else
    return Qfalse;
#endif
}

void 
Init_glib2()
{
#ifdef HAVE_RUBY_ENCODING_H
    const gchar **filename_charsets;
#endif

    id_inspect = rb_intern("inspect");

    mGLib = rb_define_module("GLib");

    setlocale (LC_CTYPE, "");
#if LC_MESSAGES
    setlocale (LC_MESSAGES, "");
#endif

    /* Version Information */
    rb_define_const(mGLib, "VERSION",
                    rb_ary_new3(3,
                                INT2FIX(glib_major_version),
                                INT2FIX(glib_minor_version),
                                INT2FIX(glib_micro_version)));
    rb_define_const(mGLib, "MAJOR_VERSION", INT2FIX(glib_major_version));
    rb_define_const(mGLib, "MINOR_VERSION", INT2FIX(glib_minor_version));
    rb_define_const(mGLib, "MICRO_VERSION", INT2FIX(glib_micro_version));
    rb_define_const(mGLib, "INTERFACE_AGE", INT2FIX(glib_interface_age));
    rb_define_const(mGLib, "BINARY_AGE", INT2FIX(glib_binary_age));

    rb_define_const(mGLib, "BINDING_VERSION",
                    rb_ary_new3(3,
                                INT2FIX(RBGLIB_MAJOR_VERSION),
                                INT2FIX(RBGLIB_MINOR_VERSION),
                                INT2FIX(RBGLIB_MICRO_VERSION)));

    rb_define_const(mGLib, "BUILD_VERSION",
                    rb_ary_new3(3,
                                INT2FIX(GLIB_MAJOR_VERSION),
                                INT2FIX(GLIB_MINOR_VERSION),
                                INT2FIX(GLIB_MICRO_VERSION)));

    /* Limits of Basic Types */
    rb_define_const(mGLib, "MININT", INT2FIX(G_MININT));
    rb_define_const(mGLib, "MAXINT", INT2NUM(G_MAXINT));
    rb_define_const(mGLib, "MAXUINT", UINT2NUM(G_MAXUINT));
    
    rb_define_const(mGLib, "MINSHORT", INT2FIX(G_MINSHORT));
    rb_define_const(mGLib, "MAXSHORT", INT2FIX(G_MAXSHORT));
    rb_define_const(mGLib, "MAXUSHORT", UINT2NUM(G_MAXUSHORT));

    rb_define_const(mGLib, "MINLONG", INT2FIX(G_MINLONG));
    rb_define_const(mGLib, "MAXLONG", INT2NUM(G_MAXLONG));
    rb_define_const(mGLib, "MAXULONG", UINT2NUM(G_MAXULONG));

#if GLIB_CHECK_VERSION(2,4,0)
    rb_define_const(mGLib, "MININT8", INT2FIX(G_MININT8));
    rb_define_const(mGLib, "MAXINT8", INT2FIX(G_MAXINT8));
    rb_define_const(mGLib, "MAXUINT8", UINT2NUM(G_MAXUINT8));

    rb_define_const(mGLib, "MININT16", INT2FIX(G_MININT16));
    rb_define_const(mGLib, "MAXINT16", INT2FIX(G_MAXINT16));
    rb_define_const(mGLib, "MAXUINT16", UINT2NUM(G_MAXUINT16));

    rb_define_const(mGLib, "MININT32", INT2FIX(G_MININT32));
    rb_define_const(mGLib, "MAXINT32", INT2NUM(G_MAXINT32));
    rb_define_const(mGLib, "MAXUINT32", UINT2NUM(G_MAXUINT32));
#endif
    rb_define_const(mGLib, "MININT64", INT2FIX(G_MININT64));
    rb_define_const(mGLib, "MAXINT64", LL2NUM(G_MAXINT64));
    rb_define_const(mGLib, "MAXUINT64", ULL2NUM(G_MAXUINT64));
#if GLIB_CHECK_VERSION(2,4,0)
    rb_define_const(mGLib, "MAXSIZE", UINT2NUM(G_MAXSIZE));
#endif
    rb_define_const(mGLib, "MINFLOAT", INT2FIX(G_MINFLOAT));
    rb_define_const(mGLib, "MAXFLOAT", DBL2NUM(G_MAXFLOAT));

    rb_define_const(mGLib, "MINDOUBLE", INT2FIX(G_MINDOUBLE));
    rb_define_const(mGLib, "MAXDOUBLE", DBL2NUM(G_MAXDOUBLE));

    /* Standard Macros */
    rb_define_module_function(mGLib, "os_win32?", rbg_s_os_win32, 0);
    rb_define_module_function(mGLib, "os_beos?", rbg_s_os_beos, 0);
    rb_define_module_function(mGLib, "os_unix?", rbg_s_os_unix, 0);

    rb_define_const(mGLib, "DIR_SEPARATOR", CSTR2RVAL(G_DIR_SEPARATOR_S));
    rb_define_const(mGLib, "SEARCHPATH_SEPARATOR", CSTR2RVAL(G_SEARCHPATH_SEPARATOR_S));

    /* discover and store glib filename encoding */
#ifdef HAVE_RUBY_ENCODING_H
    if (g_get_filename_charsets(&filename_charsets)
        || filename_charsets == NULL
        || filename_charsets[0] == NULL
        || !strcmp(filename_charsets[0], "UTF-8")
        || rb_enc_find(filename_charsets[0]) == rb_enc_find("ASCII-8BIT")) {
        /* set to NULL, mean do not perform transcoding, either filename
           encoding is unknown, UTF-8, or unsupported */
        filename_encoding_if_not_utf8 = NULL;
    } else {
        filename_encoding_if_not_utf8 = rb_enc_find(filename_charsets[0]);
    }
#endif

/* Don't implement them.
#define     G_DIR_SEPARATOR_S
#define     G_IS_DIR_SEPARATOR              (c)
#define     G_SEARCHPATH_SEPARATOR
#define     TRUE
#define     FALSE
#define     NULL
#define     MIN                             (a, b)
#define     MAX                             (a, b)
#define     ABS                             (a)
#define     CLAMP                           (x, low, high)
#define     G_STRUCT_MEMBER                 (member_type, struct_p, struct_offset)
#define     G_STRUCT_MEMBER_P               (struct_p, struct_offset)
#define     G_STRUCT_OFFSET                 (struct_type, member)
#define     G_MEM_ALIGN
#define     G_CONST_RETURN
*/

    /* Numerical Definitions */
/* Don't implement them.
#define     G_IEEE754_FLOAT_BIAS
#define     G_IEEE754_DOUBLE_BIAS
union       GFloatIEEE754;
union       GDoubleIEEE754;
*/
    rb_define_const(mGLib, "E", CSTR2RVAL(G_STRINGIFY(G_E)));
    rb_define_const(mGLib, "LN2", CSTR2RVAL(G_STRINGIFY(G_LN2)));
    rb_define_const(mGLib, "LN10", CSTR2RVAL(G_STRINGIFY(G_LN10)));
    rb_define_const(mGLib, "PI", CSTR2RVAL(G_STRINGIFY(G_PI)));
    rb_define_const(mGLib, "PI_2", CSTR2RVAL(G_STRINGIFY(G_PI_2)));
    rb_define_const(mGLib, "PI_4", CSTR2RVAL(G_STRINGIFY(G_PI_4)));
    rb_define_const(mGLib, "SQRT2", CSTR2RVAL(G_STRINGIFY(G_SQRT2)));
    rb_define_const(mGLib, "LOG_2_BASE_10", CSTR2RVAL(G_STRINGIFY(G_LOG_2_BASE_10)));

    /* From "The Main Event Loop" */
    rb_define_const(mGLib, "PRIORITY_HIGH", INT2FIX(G_PRIORITY_HIGH));
    rb_define_const(mGLib, "PRIORITY_DEFAULT", INT2FIX(G_PRIORITY_DEFAULT));
    rb_define_const(mGLib, "PRIORITY_HIGH_IDLE", INT2FIX(G_PRIORITY_HIGH_IDLE));
    rb_define_const(mGLib, "PRIORITY_DEFAULT_IDLE", INT2FIX(G_PRIORITY_DEFAULT_IDLE));
    rb_define_const(mGLib, "PRIORITY_LOW", INT2FIX(G_PRIORITY_LOW));


/*    Init_mem(); */
    Init_gutil();
    Init_gutil_callback();
    Init_glib_int64();
    Init_glib_error();
    Init_glib_threads();
    Init_glib_convert();
    Init_glib_messages();
    Init_glib_fileutils();
    Init_glib_i18n();
    Init_glib_win32();

    Init_gobject();

    /* Require GBoxed/GObject */
    Init_glib_utils();
    Init_glib_spawn();
    Init_glib_main_loop();
    Init_glib_source();
    Init_glib_main_context();
    Init_glib_poll_fd();
    Init_glib_io_channel();
    Init_glib_shell();
    Init_glib_completion();
    Init_glib_timer();
    Init_glib_unicode();
    Init_glib_keyfile();
    Init_glib_bookmark_file();

    /* This is called here once. */
    G_DEF_SETTERS(mGLib);
}
