/* -*- c-file-style: "ruby" -*- */
/************************************************

  rbgtkarg.c -

  $Author: mutoh $
  $Date: 2002/08/19 11:20:14 $

  Copyright (C) 2002 KUBO Takehiro <kubo@jiubao.org>

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

#include "global.h"

static VALUE r2b_func_table;
static VALUE b2r_func_table;

void
rbgtk_register_r2b_func(type, func)
    GtkType type;
    RValueToBValueFunc func;
{
    VALUE obj = Data_Wrap_Struct(rb_cData, NULL, NULL, func);
    rb_hash_aset(r2b_func_table, INT2NUM(type), obj);
}

void
rbgtk_register_b2r_func(type, func)
    GtkType type;
    BValueToRValueFunc func;
{
    VALUE obj = Data_Wrap_Struct(rb_cData, NULL, NULL, func);
    rb_hash_aset(b2r_func_table, INT2NUM(type), obj);
}

/* initialize GtkArg according to GtkType and argument name. */
void
rbgtk_arg_init(arg, type, name)
    GtkArg *arg;
    GtkType type;
    char *name;
{
    GtkArgInfo *info;
    char *error;
    VALUE exc;

    error = gtk_object_arg_get_info(type, name, &info);
    if (error != NULL) {
	exc = rb_exc_new2(rb_eArgError, error);
	g_free(error);
	rb_exc_raise(exc);
    }
    arg->type = info->type;
    arg->name = info->name;
    memset(&(arg->d), 0, sizeof(arg->d));
}

void
rbgtk_arg_set(arg, value)
    GtkArg *arg;
    VALUE value;
{
    GtkType type;
    VALUE obj;
    void *func;

    switch (GTK_FUNDAMENTAL_TYPE(arg->type)) {
    case GTK_TYPE_NONE:
	return;
    case GTK_TYPE_CHAR:
	GTK_VALUE_CHAR(*arg) = NUM2CHR(value);
	return;
    case GTK_TYPE_UCHAR:
	GTK_VALUE_UCHAR(*arg) = (guchar)NUM2CHR(value);
	return;
    case GTK_TYPE_BOOL:
	GTK_VALUE_BOOL(*arg) = RTEST(value);
	return;
    case GTK_TYPE_INT:
	GTK_VALUE_INT(*arg) = NUM2INT(value);
	return;
    case GTK_TYPE_UINT:
	GTK_VALUE_UINT(*arg) = NUM2UINT(value);
	return;
    case GTK_TYPE_LONG:
	GTK_VALUE_LONG(*arg) = NUM2LONG(value);
	return;
    case GTK_TYPE_ULONG:
	GTK_VALUE_ULONG(*arg) = NUM2ULONG(value);
	return;
    case GTK_TYPE_FLOAT:
	GTK_VALUE_FLOAT(*arg) = NUM2DBL(value);
	return;
    case GTK_TYPE_DOUBLE:
	GTK_VALUE_DOUBLE(*arg) = NUM2DBL(value);
	return;
    case GTK_TYPE_STRING:
	GTK_VALUE_STRING(*arg) = NIL_P(value) ? NULL : STR2CSTR(value);
	return;
    case GTK_TYPE_ENUM:
	GTK_VALUE_ENUM(*arg) = NUM2INT(value);
	return;
    case GTK_TYPE_FLAGS:
	GTK_VALUE_FLAGS(*arg) = NUM2UINT(value);
	return;
    case GTK_TYPE_OBJECT:
	GTK_VALUE_OBJECT(*arg) = get_gobject(value);
	return;
    default:
	for (type = arg->type;type != GTK_TYPE_INVALID;type =  gtk_type_parent(type)) {
	    obj = rb_hash_aref(r2b_func_table, INT2NUM(type));
	    if (NIL_P(obj))
		continue;
	    Data_Get_Struct(obj, void, func);
	    GTK_VALUE_BOXED(*arg) = ((RValueToBValueFunc)func)(value);
	    return;
	}
    }
    rb_raise(rb_eRuntimeError, "unsupported arg type %s (fundamental type %s)",
	     gtk_type_name(arg->type),
	     gtk_type_name(GTK_FUNDAMENTAL_TYPE(arg->type)));
}

void
rbgtk_arg_set_retval(arg, value)
    GtkArg *arg;
    VALUE value;
{
    GtkType type;
    VALUE obj;
    void *func;

    switch (GTK_FUNDAMENTAL_TYPE(arg->type)) {
    case GTK_TYPE_NONE:
	return;
    case GTK_TYPE_CHAR:
	*GTK_RETLOC_CHAR(*arg) = NUM2CHR(value);
	return;
    case GTK_TYPE_UCHAR:
	*GTK_RETLOC_UCHAR(*arg) = (guchar)NUM2CHR(value);
	return;
    case GTK_TYPE_BOOL:
	*GTK_RETLOC_BOOL(*arg) = RTEST(value);
	return;
    case GTK_TYPE_INT:
	*GTK_RETLOC_INT(*arg) = NUM2INT(value);
	return;
    case GTK_TYPE_UINT:
	*GTK_RETLOC_UINT(*arg) = NUM2UINT(value);
	return;
    case GTK_TYPE_LONG:
	*GTK_RETLOC_LONG(*arg) = NUM2LONG(value);
	return;
    case GTK_TYPE_ULONG:
	*GTK_RETLOC_ULONG(*arg) = NUM2ULONG(value);
	return;
    case GTK_TYPE_FLOAT:
	*GTK_RETLOC_FLOAT(*arg) = NUM2DBL(value);
	return;
    case GTK_TYPE_DOUBLE:
	*GTK_RETLOC_DOUBLE(*arg) = NUM2DBL(value);
	return;
    case GTK_TYPE_STRING:
	*GTK_RETLOC_STRING(*arg) = NIL_P(value) ? NULL : STR2CSTR(value);
	return;
    case GTK_TYPE_ENUM:
	*GTK_RETLOC_ENUM(*arg) = NUM2INT(value);
	return;
    case GTK_TYPE_FLAGS:
	*GTK_RETLOC_FLAGS(*arg) = NUM2UINT(value);
	return;
    case GTK_TYPE_OBJECT:
	*GTK_RETLOC_OBJECT(*arg) = get_gobject(value);
	return;
    default:
	for (type = arg->type;type != GTK_TYPE_INVALID;type =  gtk_type_parent(type)) {
	    obj = rb_hash_aref(r2b_func_table, INT2NUM(type));
	    if (NIL_P(obj))
		continue;
	    Data_Get_Struct(obj, void, func);
	    *GTK_RETLOC_BOXED(*arg) = ((RValueToBValueFunc)func)(value);
	    return;
	}
    }
    rb_raise(rb_eRuntimeError, "unsupported arg type %s (fundamental type %s)",
	     gtk_type_name(arg->type),
	     gtk_type_name(GTK_FUNDAMENTAL_TYPE(arg->type)));
}

VALUE
rbgtk_arg_get(arg)
    GtkArg *arg;
{
    GtkType type;
    VALUE obj;
    void *func;

    switch (GTK_FUNDAMENTAL_TYPE(arg->type)) {
    case GTK_TYPE_NONE:
	return Qnil;
    case GTK_TYPE_CHAR:
	return INT2NUM(GTK_VALUE_CHAR(*arg));
    case GTK_TYPE_UCHAR:
	return UINT2NUM(GTK_VALUE_UCHAR(*arg));
    case GTK_TYPE_BOOL:
	return GTK_VALUE_BOOL(*arg) ? Qtrue : Qfalse;
    case GTK_TYPE_INT:
	return INT2NUM(GTK_VALUE_INT(*arg));
    case GTK_TYPE_UINT:
	return UINT2NUM(GTK_VALUE_UINT(*arg));
    case GTK_TYPE_LONG:
	return INT2NUM(GTK_VALUE_LONG(*arg));
    case GTK_TYPE_ULONG:
	return UINT2NUM(GTK_VALUE_ULONG(*arg));
    case GTK_TYPE_FLOAT:
	return rb_float_new(GTK_VALUE_FLOAT(*arg));
    case GTK_TYPE_DOUBLE:
	return rb_float_new(GTK_VALUE_DOUBLE(*arg));
    case GTK_TYPE_STRING:
	return GTK_VALUE_STRING(*arg) ? rb_str_new2(GTK_VALUE_STRING(*arg)) : Qnil;
    case GTK_TYPE_ENUM:
	return INT2NUM(GTK_VALUE_ENUM(*arg));
    case GTK_TYPE_FLAGS:
	return UINT2NUM(GTK_VALUE_FLAGS(*arg));
    case GTK_TYPE_OBJECT:
	return GTK_VALUE_OBJECT(*arg) ? get_value_from_gobject(GTK_VALUE_OBJECT(*arg)) : Qnil;
    case GTK_TYPE_BOXED:
	for (type = arg->type;type != GTK_TYPE_INVALID;type =  gtk_type_parent(type)) {
	    obj = rb_hash_aref(b2r_func_table, INT2NUM(type));
	    if (NIL_P(obj))
		continue;
	    Data_Get_Struct(obj, void, func);
	    return ((BValueToRValueFunc)func)(GTK_VALUE_BOXED(*arg));
	}
    }
    rb_raise(rb_eRuntimeError, "unsupported arg type %s (fundamental type %s)",
	     gtk_type_name(arg->type),
	     gtk_type_name(GTK_FUNDAMENTAL_TYPE(arg->type)));
}

/* define static _get_xxx function if get_xxx is a macro. */
static gpointer _get_gdkcolor(VALUE obj) { return get_gdkcolor(obj); }

/* GtkWindow, GtkDrawable, GtkPixmap and GtkBitmap have same Gtk type. */
static VALUE _get_gdkdrawable(VALUE obj) { return (VALUE)get_gdkdrawable(obj); }
static VALUE _get_gdkwindow(VALUE obj) { return (VALUE)get_gdkwindow(obj); }
static gpointer _get_gdkwindow_variant(VALUE obj)
{
    return (gpointer)rb_rescue2(_get_gdkdrawable, obj, _get_gdkwindow, obj, rb_eTypeError, 0);
}

void Init_gtk_arg()
{
    b2r_func_table = rb_hash_new();
    r2b_func_table = rb_hash_new();
    rb_global_variable(&b2r_func_table);
    rb_global_variable(&r2b_func_table);

    rbgtk_register_r2b_func(GTK_TYPE_GDK_EVENT, (RValueToBValueFunc)get_gdkevent);
    rbgtk_register_b2r_func(GTK_TYPE_GDK_EVENT, (BValueToRValueFunc)make_gdkevent);

    rbgtk_register_r2b_func(GTK_TYPE_GDK_COLORMAP, (RValueToBValueFunc)get_gdkcmap);
    rbgtk_register_b2r_func(GTK_TYPE_GDK_COLORMAP, (BValueToRValueFunc)make_gdkcmap);

    rbgtk_register_r2b_func(GTK_TYPE_GDK_FONT, (RValueToBValueFunc)get_gdkfont);
    rbgtk_register_b2r_func(GTK_TYPE_GDK_FONT, (BValueToRValueFunc)make_gdkfont);

#if 0
    rbgtk_register_r2b_func(GTK_TYPE_GDK_PIXMAP, (RValueToBValueFunc)_get_gdkpixmap);
    rbgtk_register_b2r_func(GTK_TYPE_GDK_PIXMAP, (BValueToRValueFunc)make_gdkpixmap);
#endif

    rbgtk_register_r2b_func(GTK_TYPE_GDK_VISUAL, (RValueToBValueFunc)get_gdkvisual);
    rbgtk_register_b2r_func(GTK_TYPE_GDK_VISUAL, (BValueToRValueFunc)make_gdkvisual);

    rbgtk_register_r2b_func(GTK_TYPE_ACCEL_GROUP, (RValueToBValueFunc)get_gtkaccelgrp);
    rbgtk_register_b2r_func(GTK_TYPE_ACCEL_GROUP, (BValueToRValueFunc)make_gtkaccelgrp);

    rbgtk_register_r2b_func(GTK_TYPE_STYLE, (RValueToBValueFunc)get_gstyle);
    rbgtk_register_b2r_func(GTK_TYPE_STYLE, (BValueToRValueFunc)make_gstyle);

    rbgtk_register_b2r_func(GTK_TYPE_CTREE_NODE, (BValueToRValueFunc)make_ctree_node);

    rbgtk_register_r2b_func(GTK_TYPE_GDK_COLOR, (RValueToBValueFunc)_get_gdkcolor);
    rbgtk_register_r2b_func(GTK_TYPE_GDK_WINDOW, (RValueToBValueFunc)_get_gdkwindow_variant);
}
