/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
/*
 *  Copyright (C) 2011  Ruby-GNOME2 Project Team
 *  Copyright (C) 2004,2006 Ruby-GNOME2 Project
 *  Copyright (C) 2002,2003 OGASAWARA, Takeshi
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA  02110-1301  USA
 */

#include "global.h"

#define RG_TARGET_NAMESPACE cClipboard
#define _SELF(s) RVAL2CLIPBOARD(s)

#ifndef GTK_TYPE_CLIPBOARD
static GtkClipboard*
clipboard_copy(const GtkClipboard *clipboard)
{
    /* I don't know how to copy this object ... */
    return (GtkClipboard*)clipboard;
}

GType
rbgtk_clipboard_get_type()
{
    static GType our_type = 0;
    if(our_type == 0)
        our_type = g_boxed_type_register_static("GtkClipboard",
                                                (GBoxedCopyFunc)clipboard_copy,
                                                (GBoxedFreeFunc)g_free);
    return our_type;
}

GtkClipboard*
rbgtk_get_clipboard(VALUE obj)
{
    return GTK_CLIPBOARD(RVAL2GOBJ(obj));
}

VALUE
rbgtk_make_clipboard(GtkClipboard *gobj)
{
    return GOBJ2RVAL(gobj);
}
#endif

static VALUE
rg_s_get(int argc, VALUE *argv, G_GNUC_UNUSED VALUE self)
{
    GtkClipboard *clipboard;

    if (argc == 1){
        VALUE selection;
        rb_scan_args(argc, argv, "10", &selection);
        clipboard = gtk_clipboard_get(RVAL2ATOM(selection));
    } else {
        VALUE display, selection;
        rb_scan_args(argc, argv, "20", &display, &selection);
        clipboard = gtk_clipboard_get_for_display(GDK_DISPLAY_OBJECT(RVAL2GOBJ(display)),
                                                  RVAL2ATOM(selection));
    } 
    return CLIPBOARD2RVAL(clipboard);
}

static VALUE
rg_display(VALUE self)
{
    return GOBJ2RVAL(gtk_clipboard_get_display(_SELF(self)));
}

static void
clipboard_get_func(GtkClipboard *clipboard, GtkSelectionData *selection_data, G_GNUC_UNUSED guint info, gpointer func)
{
    rb_funcall((VALUE)func, id_call, 2, CLIPBOARD2RVAL(clipboard),
               BOXED2RVAL(selection_data, GTK_TYPE_SELECTION_DATA));
}

struct clipboard_set_args {
    VALUE self;
    VALUE targets;
    VALUE func;
};

static VALUE
clipboard_set_body(VALUE value)
{
    struct clipboard_set_args *args = (struct clipboard_set_args *)value;
    GtkClipboard *clipboard = _SELF(args->self);
    long n;
    GtkTargetEntry *targets = RVAL2GTKTARGETENTRIES(args->targets, &n);
    gboolean result = gtk_clipboard_set_with_data(clipboard,
                                                  targets,
                                                  n,
                                                  (GtkClipboardGetFunc)clipboard_get_func,
                                                  (GtkClipboardClearFunc)NULL,
                                                  (gpointer)args->func);

    g_free(targets);

    return CBOOL2RVAL(result);
}

static G_GNUC_NORETURN VALUE
clipboard_set_rescue(VALUE value)
{
    struct clipboard_set_args *args = (struct clipboard_set_args *)value;

    G_CHILD_REMOVE(args->self, args->func);

    rb_exc_raise(rb_errinfo());
}

static VALUE
rg_set(VALUE self, VALUE targets)
{
    struct clipboard_set_args args = { self, targets, rb_block_proc() };

    G_CHILD_ADD(self, args.func);

    return rb_rescue(clipboard_set_body, (VALUE)&args,
                     clipboard_set_rescue, (VALUE)&args);
}

/*
Do not implement this. Use Gtk::Clipboard#set_with_data instead.
gboolean    gtk_clipboard_set_with_owner    (GtkClipboard *clipboard,
                                             const GtkTargetEntry *targets,
                                             guint n_targets,
                                             GtkClipboardGetFunc get_func,
                                             GtkClipboardClearFunc clear_func,
                                             GObject *owner);

static VALUE
rg_owner(VALUE self)
{
    GObject *gobj;
    gobj = gtk_clipboard_get_owner(_SELF(self));
    return gobj ? GOBJ2RVAL(gobj) : Qnil;
}
*/

static VALUE
rg_clear(VALUE self)
{
    gtk_clipboard_clear(_SELF(self));
    return self;
}

static VALUE
rg_set_text(VALUE self, VALUE text)
{
    StringValue(text);
    gtk_clipboard_set_text(_SELF(self), RVAL2CSTR(text), RSTRING_LEN(text));
    return self;
}

static VALUE
rg_set_image(VALUE self, VALUE pixbuf)
{
    gtk_clipboard_set_image(_SELF(self), GDK_PIXBUF(RVAL2GOBJ(pixbuf)));
    return self;
}

static void
clipboard_received_func(GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer func)
{
    rb_funcall((VALUE)func, id_call, 2, CLIPBOARD2RVAL(clipboard),
               BOXED2RVAL(selection_data, GTK_TYPE_SELECTION_DATA));
}

static VALUE
rg_request_contents(VALUE self, VALUE target)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);
    gtk_clipboard_request_contents(_SELF(self), RVAL2ATOM(target),
                                   (GtkClipboardReceivedFunc)clipboard_received_func,
                                   (gpointer)func);
    return self;
}

static void
clipboard_text_received_func(GtkClipboard *clipboard, const gchar *text,
                             gpointer func)
{
    VALUE vtext = Qnil;
    if (text) {
        vtext = CSTR2RVAL(text);
    }

    rb_funcall((VALUE)func, id_call, 2, CLIPBOARD2RVAL(clipboard), vtext);
}

static VALUE
rg_request_text(VALUE self)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);
    gtk_clipboard_request_text(_SELF(self),
                               (GtkClipboardTextReceivedFunc)clipboard_text_received_func,
                               (gpointer)func);
    return self;
}

static void
clipboard_image_received_func(GtkClipboard *clipboard, GdkPixbuf *pixbuf, gpointer func)
{
    rb_funcall((VALUE)func, id_call, 2, CLIPBOARD2RVAL(clipboard),
               GOBJ2RVAL(pixbuf));
}

static VALUE
rg_request_image(VALUE self)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);
    gtk_clipboard_request_image(_SELF(self),
                               (GtkClipboardImageReceivedFunc)clipboard_image_received_func,
                               (gpointer)func);
    return self;
}

static void
clipboard_target_received_func(GtkClipboard *clipboard, GdkAtom *atoms, gint n_atoms, gpointer func)
{
    gint i;
    VALUE ary = rb_ary_new();
    for (i = 0; i < n_atoms; i++){
        rb_ary_push(ary, BOXED2RVAL(atoms[i], GDK_TYPE_ATOM));
    }

    rb_funcall((VALUE)func, id_call, 2, CLIPBOARD2RVAL(clipboard), ary);
}

static VALUE
rg_request_targets(VALUE self)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);

    gtk_clipboard_request_targets(_SELF(self),
                                  (GtkClipboardTargetsReceivedFunc)clipboard_target_received_func, 
                                  (gpointer)func);
    return self;
}

static void
clipboard_rich_text_received_func(GtkClipboard *clipboard, GdkAtom format, const guint8 *text, gsize length, gpointer func)
{
    rb_funcall((VALUE)func, id_call, 3, CLIPBOARD2RVAL(clipboard), 
               BOXED2RVAL(format, GDK_TYPE_ATOM), rb_str_new((char*)text, length));
}

static VALUE
rg_request_rich_text(VALUE self, VALUE buffer)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);

    gtk_clipboard_request_rich_text(_SELF(self), GTK_TEXT_BUFFER(RVAL2GOBJ(buffer)),
                                    (GtkClipboardRichTextReceivedFunc)clipboard_rich_text_received_func,
                                    (gpointer)func);
    return self;
}

static VALUE
rg_wait_for_contents(VALUE self, VALUE target)
{
    return BOXED2RVAL(gtk_clipboard_wait_for_contents(
                          _SELF(self), 
                          RVAL2ATOM(target)), 
                      GTK_TYPE_SELECTION_DATA);
}

static VALUE
rg_wait_for_text(VALUE self)
{
    gchar *str;
    str = gtk_clipboard_wait_for_text(_SELF(self));
    return str ? CSTR2RVAL(str) : Qnil;
}

static VALUE
rg_wait_for_image(VALUE self)
{
    GdkPixbuf* pixbuf = gtk_clipboard_wait_for_image(_SELF(self));
    return GOBJ2RVAL(pixbuf);
}

static VALUE
rg_wait_for_rich_text(VALUE self, VALUE buffer)
{
    GdkAtom format;
    gsize length;
    guint8* data = gtk_clipboard_wait_for_rich_text(_SELF(self), 
                                                    GTK_TEXT_BUFFER(RVAL2GOBJ(buffer)),
                                                                    &format, &length);
    if (data){
        VALUE str = rb_str_new((char*)data, length);
        g_free(data);
        return rb_assoc_new(str, BOXED2RVAL(format, GDK_TYPE_ATOM));
    } else {
        return rb_assoc_new(Qnil, BOXED2RVAL(format, GDK_TYPE_ATOM));
    }

}

static VALUE
rg_wait_is_text_available_p(VALUE self)
{
    return CBOOL2RVAL(gtk_clipboard_wait_is_text_available(_SELF(self)));
}

static VALUE
rg_wait_is_image_available_p(VALUE self)
{
    return CBOOL2RVAL(gtk_clipboard_wait_is_image_available(_SELF(self)));
}

static VALUE
rg_wait_is_rich_text_available_p(VALUE self, VALUE buffer)
{
    return CBOOL2RVAL(gtk_clipboard_wait_is_rich_text_available(_SELF(self), GTK_TEXT_BUFFER(RVAL2GOBJ(buffer))));
}

static VALUE
rg_wait_for_targets(VALUE self)
{
    gint i;
    VALUE ary = Qnil;
    GdkAtom* targets;
    gint n_targets = 0;
    gboolean ret = gtk_clipboard_wait_for_targets(_SELF(self), &targets, &n_targets);

    if (ret){
        ary = rb_ary_new();
        for (i = 0; i < n_targets; i++){
            if ((GdkAtom)targets == (GdkAtom)GDK_NONE){
                rb_ary_push(ary, rb_eval_string("Gdk::Atom::NONE"));
            }
            else
                rb_ary_push(ary, BOXED2RVAL(targets, GDK_TYPE_ATOM));
            targets++;
        } 
        /* How can I this ?
        g_free(&o_targets);
        */
    }
    return ary;
}

static VALUE
rg_wait_is_target_available_p(VALUE self, VALUE target)
{
    return CBOOL2RVAL(gtk_clipboard_wait_is_target_available(_SELF(self), RVAL2ATOM(target)));
}

static VALUE
rg_set_can_store(VALUE self, VALUE rbtargets)
{
    GtkClipboard *clipboard = _SELF(self);
    long n = 0;
    GtkTargetEntry *targets = RVAL2GTKTARGETENTRIES_ACCEPT_NIL(rbtargets, &n);

    gtk_clipboard_set_can_store(clipboard, targets, n);

    g_free(targets);

    return self;
}

static VALUE
rg_store(VALUE self)
{
    gtk_clipboard_store(_SELF(self));

    return self;
}

void 
Init_gtk_clipboard(VALUE mGtk)
{
    VALUE RG_TARGET_NAMESPACE = G_DEF_CLASS(GTK_TYPE_CLIPBOARD, "Clipboard", mGtk);

    RG_DEF_SMETHOD(get, -1);
    RG_DEF_METHOD(display, 0);
    RG_DEF_METHOD(set, 1);
/*
  RG_DEF_METHOD(owner, 0);
*/
    RG_DEF_METHOD(clear, 0);
    RG_DEF_METHOD(set_text, 1);
    RG_DEF_METHOD(set_image, 1);
    RG_DEF_METHOD(request_contents, 1);
    RG_DEF_METHOD(request_text, 0);
    RG_DEF_METHOD(request_image, 0);
    RG_DEF_METHOD(request_targets, 0);
    RG_DEF_METHOD(request_rich_text, 1);
    RG_DEF_METHOD(wait_for_contents, 1);
    RG_DEF_METHOD(wait_for_text, 0);
    RG_DEF_METHOD(wait_for_image, 0);
    RG_DEF_METHOD(wait_for_rich_text, 1);
    RG_DEF_METHOD_P(wait_is_text_available, 0);
    RG_DEF_METHOD_P(wait_is_image_available, 0);
    RG_DEF_METHOD_P(wait_is_rich_text_available, 1);
    RG_DEF_METHOD(wait_for_targets, 0);
    RG_DEF_METHOD_P(wait_is_target_available, 1);
    RG_DEF_METHOD(set_can_store, 1);
    RG_DEF_METHOD(store, 0);

    G_DEF_SETTERS(RG_TARGET_NAMESPACE);
}
