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

  rbgobj_enums.c -

  $Author: ktou $
  $Date: 2007/08/08 11:53:09 $
  created at: Sat Jul 27 16:56:01 JST 2002

  Copyright (C) 2004-2006  Ruby-GNOME2 Project Team
  Copyright (C) 2002,2003  Masahiro Sakai
**********************************************************************/

#include "rbgprivate.h"
#include <ctype.h>

VALUE rbgobj_cEnum, rbgobj_cFlags;

static ID id_new;
static ID id_module_eval;
static ID id_to_s;
static ID id_or;

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

typedef struct {
	char *original;
	char *replacement;
} constant_map;

static GSList *rbgobj_cmap = NULL;

static gchar *
nick_to_const_name(const gchar *nick)
{
    gchar *const_name;
    gchar *p;

    if (!nick)
        return NULL;

    const_name = g_strdup(nick);
    for (p = const_name; *p; p++) {
        if (*p == '-' || *p == ' ')
            *p = '_';
        else
            *p = g_ascii_toupper(*p);
    }
    return const_name;
}

static VALUE
resolve_enum_value(VALUE klass, VALUE nick)
{
    VALUE value = Qnil;
    gchar *const_nick;
    ID const_nick_id;

    if (RVAL2CBOOL(rb_obj_is_kind_of(nick, klass)))
        return nick;

    nick = rb_funcall(nick, id_to_s, 0);
    const_nick = nick_to_const_name(RVAL2CSTR(nick));
    const_nick_id = rb_intern(const_nick);
    if (rb_const_defined(klass, const_nick_id)) {
        value = rb_const_get(klass, const_nick_id);
    }
    g_free(const_nick);

    return value;
}

static VALUE
resolve_flags_value(VALUE klass, VALUE nick_or_nicks)
{
    int i, len;
    VALUE flags_value;

    if (!RVAL2CBOOL(rb_obj_is_kind_of(nick_or_nicks, rb_cArray)))
        return resolve_enum_value(klass, nick_or_nicks);

    len = RARRAY_LEN(nick_or_nicks);
    flags_value = rb_funcall(klass, id_new, 0);
    for (i = 0; i < len; i++) {
        VALUE value;

        value = resolve_enum_value(klass, RARRAY_PTR(nick_or_nicks)[i]);
        if (NIL_P(value))
            return Qnil;

	flags_value = rb_funcall(flags_value, id_or, 1, value);
    }

    return flags_value;
}

static gint
rbgobj_constant_find(constant_map *a, char *name)
{
      return strcmp(a->original, name);
}

void
rbgobj_constant_remap(const char *original, const char *replacement)
{
      constant_map *map = g_new(constant_map,1);
      
      map -> original     = g_strdup(original);
      map -> replacement  = g_strdup(replacement);
      
      rbgobj_cmap = g_slist_append(rbgobj_cmap, map);
}

static char *
rbgobj_constant_lookup(const char *name)
{
      GSList *p = rbgobj_cmap;
      
      p = g_slist_find_custom(rbgobj_cmap, name,
			      (GCompareFunc)rbgobj_constant_find);
      if (p)
      {
          char *replacement;
	  constant_map *map;
	  
	  map         = (constant_map*) p -> data;
	  rbgobj_cmap = g_slist_delete_link(rbgobj_cmap, p);
	  replacement = map -> replacement;
	  
	  g_free(map -> original);
	  g_free(map);
	  
	  return replacement;
      }
      return NULL;
}

void 
rbgobj_define_const(VALUE mod, const char *name,
			VALUE value)
{
     if (name[0] >= 'A' && name[0] <= 'Z')
     {
         rb_define_const(mod, name, value);
     }
     else
     {
         char *new_name = rbgobj_constant_lookup(name);
	 
	 if (new_name)
	 {
	     rb_define_const(mod, new_name, value);
	     g_free(new_name);
	 }
	 else
	 {
	     rb_warn("Invalid constant name '%s' - skipped", name);
	 }
     }
}

static void
rbgobj_enum_add_constants(VALUE mod, GType enum_type,
                          const gchar *strip_prefix)
{
    GEnumClass *gclass;
    int i;
    int prefix_len = strlen(strip_prefix);

    gclass = G_ENUM_CLASS(g_type_class_ref(enum_type));

    for (i = 0; i < gclass->n_values; i++) {
        const GEnumValue* value = &gclass->values[i];

        if (strncmp(value->value_name, strip_prefix, prefix_len)) {
            g_warning("\"%s\" doesn't have prefix \"%s\"",
                      value->value_name, strip_prefix);
        } else {
            const char* name = value->value_name + prefix_len;
            rbgobj_define_const(mod, name,
                                rbgobj_make_enum(value->value, enum_type));
        }
    }

    g_type_class_unref(gclass);
}

static void
rbgobj_flags_add_constants(VALUE mod, GType flags_type,
                           const gchar *strip_prefix)
{
    GFlagsClass *gclass;
    int i;
    int prefix_len = strlen(strip_prefix);

    gclass = G_FLAGS_CLASS(g_type_class_ref(flags_type));

    for (i = 0; i < gclass->n_values; i++) {
        const GFlagsValue* value = &gclass->values[i];

        if (strncmp(value->value_name, strip_prefix, prefix_len)) {
            g_warning("\"%s\" doesn't have prefix \"%s\"",
                      value->value_name, strip_prefix);
        } else {
            const char* name = value->value_name + prefix_len;
            rbgobj_define_const(mod, name,
                                rbgobj_make_flags(value->value, flags_type));

        }
    }

    g_type_class_unref(gclass);
}

void
rbgobj_add_constants(VALUE mod, GType type, const gchar *strip_prefix)
{
    if (G_TYPE_IS_ENUM(type)) {
        rbgobj_enum_add_constants(mod, type, strip_prefix);
    } else if (G_TYPE_IS_FLAGS(type)) {
        rbgobj_flags_add_constants(mod, type, strip_prefix);
    } else {
        g_warning("`%s' is not an enum/flags type", g_type_name(type));
    }
}

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

typedef struct {
    GEnumClass* gclass;
    gint value;
    GEnumValue* info;
} enum_holder;

static void
enum_free(enum_holder* p)
{
    g_type_class_unref(p->gclass);
    free(p);
}

static enum_holder*
enum_get_holder(VALUE obj)
{
    enum_holder* p;
    Data_Get_Struct(obj, enum_holder, p);
    return p;
}

static VALUE
make_enum(gint n, VALUE klass)
{
    return rb_funcall(klass, id_new, 1, INT2NUM(n));
}

VALUE
rbgobj_make_enum(gint n, GType gtype)
{
    return make_enum(n, GTYPE2CLASS(gtype));
}

gint
rbgobj_get_enum(VALUE obj, GType gtype)
{
    VALUE klass;

    if (!g_type_is_a(gtype, G_TYPE_ENUM))
        rb_raise(rb_eTypeError, "%s is not a %s: %s",
                 g_type_name(gtype), g_type_name(G_TYPE_ENUM),
		 RBG_INSPECT(obj));

    /* for compatibility */
    if (rb_obj_is_kind_of(obj, rb_cInteger))
        obj = rbgobj_make_enum(NUM2INT(obj), gtype);

    klass = GTYPE2CLASS(gtype);

    if (!rb_obj_is_kind_of(obj, klass)) {
        VALUE enum_value;

        enum_value = resolve_enum_value(klass, obj);
        if (!NIL_P(enum_value))
            obj = enum_value;
    }

    if (rb_obj_is_kind_of(obj, klass))
        return enum_get_holder(obj)->value;
    else
        rb_raise(rb_eTypeError, "not a %s: %s",
		 rb_class2name(klass), RBG_INSPECT(obj));
}

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

void
rbgobj_init_enum_class(VALUE klass)
{
    GEnumClass* gclass = g_type_class_ref(CLASS2GTYPE(klass));
    int i;

    for (i = 0; i < gclass->n_values; i++) {
        GEnumValue* entry = &(gclass->values[i]);
        gchar *const_nick_name;

        const_nick_name = nick_to_const_name(entry->value_nick);

#if 0
        {
            ID id = rb_intern(const_nick_name);
            if (rb_is_const_id(id)) {
                VALUE value;

		value = make_enum(entry->value, klass);
                rb_define_const(klass, const_nick_name, value);
            }
        }
#else
        {
            if (const_nick_name) {
                VALUE value;

		value = make_enum(entry->value, klass);
                rbgobj_define_const(klass, const_nick_name, value);
            }
        }
#endif

        g_free(const_nick_name);
    }

    g_type_class_unref(gclass);
}

static VALUE
enum_s_range(VALUE self)
{
    GEnumClass* gclass = g_type_class_ref(CLASS2GTYPE(self));
    VALUE result = rb_range_new(INT2NUM(gclass->minimum),
                                INT2NUM(gclass->maximum),
                                FALSE);
    g_type_class_unref(gclass);
    return result;
}

static VALUE
enum_s_values(VALUE self)
{
    GEnumClass *gclass;
    VALUE result;
    int i;

    gclass = g_type_class_ref(CLASS2GTYPE(self));;
    result = rb_ary_new();
    for (i = 0; i < gclass->n_values; i++) {
        GEnumValue *p = &(gclass->values[i]);
        rb_ary_push(result, make_enum(p->value, self));
    }

    g_type_class_unref(gclass);
    return result;
}

static VALUE
enum_s_allocate(VALUE self)
{
    GType gtype = CLASS2GTYPE(self);

    if (G_TYPE_IS_ABSTRACT(gtype)) {
        rb_raise(rb_eTypeError, "abstract class");
    } else {
        enum_holder* p;
        VALUE result = Data_Make_Struct(self, enum_holder, NULL, enum_free, p);
        p->gclass = g_type_class_ref(gtype);
        p->info   = NULL;
        return result;
    }
}

static VALUE
enum_initialize(VALUE self, VALUE arg)
{
    enum_holder* p = enum_get_holder(self);

    if (rb_respond_to(arg, rb_intern("to_str"))) {
        const char* str = StringValuePtr(arg);
        p->info = g_enum_get_value_by_name(p->gclass, str);
        if (! p->info)
            p->info = g_enum_get_value_by_nick(p->gclass, str);
        if (! p->info)
            rb_raise(rb_eArgError, "invalid argument");
    } else {
        p->value = NUM2INT(arg);
        p->info  = g_enum_get_value(p->gclass, p->value);
    }

    return Qnil;
}

static VALUE
enum_to_i(VALUE self)
{
    enum_holder* p = enum_get_holder(self);
    return INT2NUM(p->value);
}

static VALUE
enum_name(VALUE self)
{
    enum_holder* p = enum_get_holder(self);
    return p->info ? rb_str_new2(p->info->value_name) : Qnil;
}

static VALUE
enum_nick(VALUE self)
{
    enum_holder* p = enum_get_holder(self);
    return p->info ? rb_str_new2(p->info->value_nick) : Qnil;
}

static VALUE
enum_inspect(VALUE self)
{
    const char* cname = rb_class2name(CLASS_OF(self));
    enum_holder* p = enum_get_holder(self);
    gchar* str;
    VALUE result;

    if (p->info)
        str = g_strdup_printf("#<%s %s>",
                              cname, p->info->value_nick);
    else
        str = g_strdup_printf("#<%s %d>",
                              cname, p->value);
    result = rb_str_new2(str);
    g_free(str);

    return result;
}

static VALUE
enum_eqv(VALUE self, VALUE rhs)
{
    enum_holder* p = enum_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;
    return CBOOL2RVAL(rbgobj_get_enum(self, gtype) == rbgobj_get_enum(rhs, gtype));
}

static VALUE
enum_hash(VALUE self)
{
    enum_holder* p = enum_get_holder(self);
    return UINT2NUM(p->value ^ G_TYPE_FROM_CLASS(p->gclass));
}

static VALUE
enum_coerce(VALUE self, VALUE other)
{
    enum_holder *holder;
    GType gtype;

    if (!rb_obj_is_kind_of(other, rb_cInteger))
        rb_raise(rb_eTypeError, "can't coerce");

    holder = enum_get_holder(self);
    gtype = G_TYPE_FROM_CLASS(holder->gclass);
    other = rbgobj_make_enum(NUM2INT(other), gtype);
    return rb_ary_new3(2, other, self);
}

static void
Init_enum()
{
    VALUE cEnum;

    rbgobj_cEnum = G_DEF_CLASS(G_TYPE_ENUM, "Enum", mGLib);
    cEnum = rbgobj_cEnum;

    rb_define_singleton_method(cEnum, "gtype", generic_s_gtype, 0);
    rb_define_method(cEnum, "gtype", generic_gtype, 0);

    rb_define_singleton_method(cEnum, "range", enum_s_range, 0);
    rb_define_singleton_method(cEnum, "values", enum_s_values, 0);

    rb_define_alloc_func(cEnum, enum_s_allocate);

    rb_define_method(cEnum, "initialize", enum_initialize, 1);
    rb_define_method(cEnum, "to_i", enum_to_i, 0);
    rb_define_method(cEnum, "name", enum_name, 0);
    rb_define_method(cEnum, "nick", enum_nick, 0);

    rb_define_method(cEnum, "inspect", enum_inspect, 0);
    rb_define_method(cEnum, "==", enum_eqv, 1);
    rb_define_method(cEnum, "hash", enum_hash, 0);
    rb_define_method(cEnum, "eql?", enum_eqv, 1);

    /* for compatibility */
    rb_define_method(cEnum, "coerce", enum_coerce, 1);
    rb_define_alias(cEnum, "to_int", "to_i");
}

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

typedef struct {
    GFlagsClass* gclass;
    guint value;
    GFlagsValue* info;
} flags_holder;

static void
flags_free(enum_holder* p)
{
    g_type_class_unref(p->gclass);
    free(p);
}

static flags_holder*
flags_get_holder(VALUE obj)
{
    flags_holder* p;
    Data_Get_Struct(obj, flags_holder, p);
    return p;
}

static VALUE
make_flags(guint n, VALUE klass)
{
    return rb_funcall(klass, id_new, 1, UINT2NUM(n));
}

VALUE
rbgobj_make_flags(guint n, GType gtype)
{
    return make_flags(n, GTYPE2CLASS(gtype));
}

guint
rbgobj_get_flags(VALUE obj, GType gtype)
{
    VALUE klass;

    if (!g_type_is_a(gtype, G_TYPE_FLAGS))
        rb_raise(rb_eTypeError, "%s is not a %s",
                 g_type_name(gtype), g_type_name(G_TYPE_FLAGS));

    /* for compatibility */
    if (rb_obj_is_kind_of(obj, rb_cInteger))
        obj = rbgobj_make_flags(NUM2UINT(obj), gtype);

    klass = GTYPE2CLASS(gtype);

    if (!rb_obj_is_kind_of(obj, klass)) {
        VALUE flags_value = Qnil;

        flags_value = resolve_flags_value(klass, obj);
        if (!NIL_P(flags_value))
            obj = flags_value;
    }

    if (rb_obj_is_kind_of(obj, klass))
        return flags_get_holder(obj)->value;
    else
        rb_raise(rb_eTypeError, "not a %s: %s",
		 rb_class2name(klass), RBG_INSPECT(obj));
}

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

void
rbgobj_init_flags_class(VALUE klass)
{
    GFlagsClass* gclass = g_type_class_ref(CLASS2GTYPE(klass));
    GString* source = g_string_new(NULL);
    int i;

    for (i = 0; i < gclass->n_values; i++) {
        GFlagsValue* entry = &(gclass->values[i]);
        gchar* nick;
        gchar* p;
        gchar* replace_nick;

        replace_nick = rbgobj_constant_lookup(entry->value_nick);
        if (replace_nick){
            nick = g_strdup(replace_nick);
        } else {
            nick = g_strdup(entry->value_nick);
        }

        for (p = nick; *p; p++)
            if (*p == '-' || *p == ' ')
                *p = '_';
            else
                *p = tolower(*p);

        g_string_append_printf(
            source,
            "def %s%s?; self >= self.class.new(%d); end\n",
            g_ascii_isdigit(nick[0]) ? "_" : "",
            nick, entry->value);

        for (p = nick; *p; p++)
            *p = g_ascii_toupper(*p);

#if 0
        {
            ID id = rb_intern(nick);
            if (rb_is_const_id(id)) {
                rb_define_const(klass, nick, make_flags(entry->value, klass));
            }
        }
#else
        {
            rbgobj_define_const(klass, nick, make_flags(entry->value, klass));
        }
#endif

        g_free(nick);
    }

    rb_funcall(klass, id_module_eval, 1, rb_str_new2(source->str));
    g_string_free(source, TRUE);

    g_type_class_unref(gclass);
}

static VALUE
flags_s_mask(VALUE klass)
{
    GFlagsClass* gclass = g_type_class_ref(CLASS2GTYPE(klass));
    VALUE result = UINT2NUM(gclass->mask);
    g_type_class_unref(gclass);
    return result;
}

static VALUE
flags_s_values(VALUE klass)
{
    GFlagsClass *gclass;
    VALUE result;
    int i;

    gclass = g_type_class_ref(CLASS2GTYPE(klass));
    result = rb_ary_new();
    for (i = 0; i < gclass->n_values; i++) {
        GFlagsValue *p = &(gclass->values[i]);
        rb_ary_push(result, make_flags(p->value, klass));
    }
    g_type_class_unref(gclass);

    return result;
}

static VALUE
flags_s_allocate(VALUE self)
{
    GType gtype = CLASS2GTYPE(self);

    if (G_TYPE_IS_ABSTRACT(gtype)) {
        rb_raise(rb_eTypeError, "abstract class");
    } else {
        flags_holder* p;
        VALUE result = Data_Make_Struct(self, flags_holder, NULL, flags_free, p);
        p->gclass = g_type_class_ref(gtype);
        p->value  = 0;
        p->info   = NULL;
        return result;
    }
}

static VALUE
flags_initialize(int argc, VALUE* argv, VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    VALUE arg;

    rb_scan_args(argc, argv, "01", &arg);

    if (argc == 0) {
        p->value = 0;
    } else {
        if (rb_respond_to(arg, rb_intern("to_str"))) {
            const char* str = StringValuePtr(arg);
            p->info = g_flags_get_value_by_name(p->gclass, str);
            if (!p->info)
                p->info = g_flags_get_value_by_nick(p->gclass, str);
            if (!p->info)
                rb_raise(rb_eArgError, "invalid argument");
            p->value = p->info->value;
        } else {
            p->value = NUM2UINT(arg);
        }
    }

    if (!p->info) {
        int i;
        for (i = 0; i < p->gclass->n_values; i++){
            GFlagsValue* val = &(p->gclass->values[i]);
            if (val->value == p->value){
                p->info = val;
                break;
            }
        }
    }

    return Qnil;
}

static VALUE
flags_to_i(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return UINT2NUM(p->value);
}

static VALUE
flags_name(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return p->info ? rb_str_new2(p->info->value_name) : Qnil;
}

static VALUE
flags_nick(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return p->info ? rb_str_new2(p->info->value_nick) : Qnil;
}

static VALUE
flags_compare(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);

    if (p->value == rhs_val)
        return INT2FIX(0);
    else if ((p->value & rhs_val) == rhs_val)
        return INT2FIX(1);
    else if ((p->value & rhs_val) == p->value)
        return INT2FIX(-1);
    else
        return Qnil;
}

static VALUE
flags_eqv(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);
    return CBOOL2RVAL(p->value == rhs_val);
}

static VALUE
flags_gt_eq(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);
    return CBOOL2RVAL((p->value & rhs_val) == rhs_val);
}

static VALUE
flags_lt_eq(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);
    return CBOOL2RVAL((p->value & rhs_val) == p->value);
}

static VALUE
flags_gt(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);
    return CBOOL2RVAL((p->value & rhs_val) == rhs_val &&
                      p->value != rhs_val);
}

static VALUE
flags_lt(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    guint rhs_val;

    if (CLASS_OF(rhs) != CLASS_OF(self) &&
        !rb_obj_is_kind_of(rhs, rb_cInteger))
        return Qnil;

    rhs_val = rbgobj_get_flags(rhs, gtype);
    return CBOOL2RVAL((p->value & rhs_val) == p->value &&
                      p->value != rhs_val);
}

static VALUE
flags_not(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    return rbgobj_make_flags((~ p->value) & p->gclass->mask,
                             G_TYPE_FROM_CLASS(p->gclass));
}

#define LIFT_BINARY_OP(funcname, op) \
    static VALUE \
    funcname(VALUE self, VALUE rhs) \
    { \
        flags_holder* p = flags_get_holder(self); \
        GType gtype = G_TYPE_FROM_CLASS(p->gclass); \
        return rbgobj_make_flags(p->value op rbgobj_get_flags(rhs, gtype), \
                                 gtype); \
    }

LIFT_BINARY_OP(flags_and, &)
LIFT_BINARY_OP(flags_or, |)
LIFT_BINARY_OP(flags_xor, ^)

static VALUE
flags_minus(VALUE self, VALUE rhs)
{
    flags_holder* p = flags_get_holder(self);
    GType gtype = G_TYPE_FROM_CLASS(p->gclass);
    return rbgobj_make_flags(p->value & ~rbgobj_get_flags(rhs, gtype),
                             gtype);
}

static VALUE
flags_empty_p(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return CBOOL2RVAL(p->value == 0);
}

static VALUE
flags_hash(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return UINT2NUM(p->value ^ G_TYPE_FROM_CLASS(p->gclass));
}

static VALUE
flags_coerce(VALUE self, VALUE other)
{
    flags_holder *holder;
    GType gtype;

    if (rb_obj_is_kind_of(other, rb_cInteger))
        rb_raise(rb_eTypeError, "can't coerce");

    holder = flags_get_holder(self);
    gtype = G_TYPE_FROM_CLASS(holder->gclass);
    other = rbgobj_make_flags(NUM2UINT(other), gtype);
    return rb_ary_new3(2, other, self);
}

static VALUE
flags_nonzero_p(VALUE self)
{
    flags_holder* p = flags_get_holder(self);
    return CBOOL2RVAL(p->value != 0);
}

static void
Init_flags()
{
    VALUE cFlags;

    rbgobj_cFlags = G_DEF_CLASS(G_TYPE_FLAGS, "Flags", mGLib);
    cFlags = rbgobj_cFlags;

    rb_define_singleton_method(cFlags, "gtype", generic_s_gtype, 0);
    rb_define_method(cFlags, "gtype", generic_gtype, 0);

    rb_define_singleton_method(cFlags, "mask", flags_s_mask, 0);
    rb_define_singleton_method(cFlags, "values", flags_s_values, 0);

    rb_define_alloc_func(cFlags, flags_s_allocate);

    rb_define_method(cFlags, "initialize", flags_initialize, -1);

    rb_define_method(cFlags, "to_i", flags_to_i, 0);
    rb_define_alias(cFlags, "to_int", "to_i");
    rb_define_method(cFlags, "name", flags_name, 0);
    rb_define_method(cFlags, "nick", flags_nick, 0);

    /*
    rb_define_method(cFlags, "inspect", flags_inspect, 0);
    */

    rb_define_method(cFlags, "<=>", flags_compare, 1);
    rb_define_method(cFlags, "==", flags_eqv, 1);
    rb_define_method(cFlags, ">=", flags_gt_eq, 1);
    rb_define_method(cFlags, "<=", flags_lt_eq, 1);
    rb_define_method(cFlags, ">", flags_gt, 1);
    rb_define_method(cFlags, "<", flags_lt, 1);
    rb_define_method(cFlags, "~", flags_not, 0);
    rb_define_method(cFlags, "&", flags_and, 1);
    rb_define_method(cFlags, "|", flags_or, 1);
    rb_define_method(cFlags, "^", flags_xor, 1);
    rb_define_method(cFlags, "-", flags_minus, 1);

    rb_define_method(cFlags, "empty?", flags_empty_p, 0);

    rb_define_method(cFlags, "hash", flags_hash, 0);
    rb_define_method(cFlags, "eql?", flags_eqv, 1);

    /* for compatibility */
    rb_define_method(cFlags, "coerce", flags_coerce, 1);
    rb_define_method(cFlags, "zero?", flags_empty_p, 0);
    rb_define_method(cFlags, "nonzero?", flags_nonzero_p, 0);
}

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

void
Init_gobject_genums()
{
    id_module_eval = rb_intern("module_eval");
    id_new = rb_intern("new");
    id_to_s = rb_intern("to_s");
    id_or = rb_intern("|");

    Init_enum();
    Init_flags();
}
