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

  rbglib_completion.c -

  $Author: sakai $
  $Date: 2007/07/08 02:40:12 $

  Copyright (C) 2005,2006  Masao Mutoh
************************************************/

#include "rbgprivate.h"

static ID id_compfunc;
static ID id_call;
static ID id_to_s;
static ID id_clear;
static ID id_items_internal;

/*****************************************/
static GCompletion*
completion_copy(GCompletion *comp)
{
  GCompletion* new_comp;
  g_return_val_if_fail (comp != NULL, NULL);

  new_comp = g_new(GCompletion, 1);
  *new_comp = *comp;
  return new_comp;
}

GType
g_completion_get_type(void)
{
    static GType our_type = 0;
    if (our_type == 0)
        our_type = g_boxed_type_register_static ("GCompletion",
                                                 (GBoxedCopyFunc)completion_copy,
                                                 (GBoxedFreeFunc)g_completion_free);
    return our_type;
}
/*****************************************/

#define G_TYPE_COMPLETION (g_completion_get_type())

#define _SELF(s) ((GCompletion*)RVAL2BOXED(s, G_TYPE_COMPLETION))

/* data should be [self, data] */
static gchar*
comp_func(gpointer compdata)
{
    VALUE ret;
    VALUE self = RARRAY_PTR((VALUE)compdata)[0];
    VALUE data = RARRAY_PTR((VALUE)compdata)[1];
    
    VALUE func = rb_ivar_get(self, id_compfunc);
    
    if (NIL_P(func)){
        ret = rb_funcall(data, id_to_s, 0);
    } else {
        ret = rb_funcall(func, id_call, 1, data);
    }
    return RVAL2CSTR(ret);
}

static VALUE
comp_initialize(VALUE self)
{
    VALUE block = Qnil;

    if (rb_block_given_p()) {
        block = rb_block_proc();
    }

    rb_ivar_set(self, id_compfunc, block);
    /* This is used for memory management(GC) */
    rb_ivar_set(self, id_items_internal, rb_hash_new());

    G_INITIALIZE(self, g_completion_new((GCompletionFunc)comp_func));

    return Qnil;
}

static VALUE
comp_add_items(VALUE self, VALUE items)
{
    gint i, len;
    GList* list = (GList*)NULL;
    VALUE items_internal = rb_ivar_get(self, id_items_internal);

    Check_Type(items, T_ARRAY);
    len = RARRAY_LEN(items);
    for (i = 0; i < len; i ++){
        VALUE data = RARRAY_PTR(items)[i];
        VALUE item = rb_assoc_new(self, data);
        list = g_list_append(list, (gpointer)item);
        rb_hash_aset(items_internal, data, item);
    }
    g_completion_add_items(_SELF(self), list);

    return self;
}

static VALUE
comp_remove_items(VALUE self, VALUE items)
{
    gint i, len;
    GList* list = (GList*)NULL;
    VALUE items_internal = rb_ivar_get(self, id_items_internal);

    Check_Type(items, T_ARRAY);
    len = RARRAY_LEN(items);
    for (i = 0; i < len; i ++){
        VALUE data = RARRAY_PTR(items)[i];
        VALUE item = rb_hash_aref(items_internal, data);
        list = g_list_append(list, (gpointer)item);
        rb_hash_delete(items_internal, data);
    }
    g_completion_remove_items(_SELF(self), list);

    return self;
}

static VALUE
comp_clear_items(VALUE self)
{
    VALUE items_internal = rb_ivar_get(self, id_items_internal);
    rb_funcall(items_internal, id_clear, 0);
    g_completion_clear_items(_SELF(self));
    return self;
}

static VALUE
comp_items(VALUE self)
{
    return rb_ivar_get(self, id_items_internal);
}

static VALUE
comp_complete(VALUE self, VALUE prefix)
{
    gchar* new_prefix;
    VALUE ary = rb_ary_new();
#if GLIB_CHECK_VERSION(2,4,0)
    GList* list = g_completion_complete_utf8(_SELF(self),
                                            (const gchar*)RVAL2CSTR(prefix),
                                            &new_prefix);
#else
    GList* list = g_completion_complete(_SELF(self),
                                        RVAL2CSTR(prefix),
                                        &new_prefix);
#endif
    while (list) {
        rb_ary_push(ary, RARRAY_PTR((VALUE)list->data)[1]);
        list = list->next;
    }

    return rb_assoc_new(ary, new_prefix ? CSTR2RVAL(new_prefix) : Qnil);
}

/* We don't use this.
void        g_completion_set_compare        (GCompletion *cmp,
                                             GCompletionStrncmpFunc strncmp_func);
*/

void
Init_glib_completion()
{
    VALUE comp = G_DEF_CLASS(G_TYPE_COMPLETION, "Completion", mGLib); 

    id_call = rb_intern("call");
    id_to_s = rb_intern("to_s");
    id_clear = rb_intern("clear");
    id_compfunc = rb_intern("completion_proc");
    id_items_internal = rb_intern("items_internal");

    rb_define_method(comp, "initialize", comp_initialize, 0);
    rb_define_method(comp, "add_items", comp_add_items, 1);
    rb_define_method(comp, "remove_items", comp_remove_items, 1);
    rb_define_method(comp, "clear_items", comp_clear_items, 0);
    rb_define_method(comp, "complete", comp_complete, 1);

    rb_define_method(comp, "items", comp_items, 0);
}
