/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 *  Copyright (C) 2009 Hiroyuki Ikezoe
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <dbus/dbus-glib-bindings.h>

#include "kz-dbus-embed-delegate.h"
#include "kz-embed-process-client-bindings.h"
#include "kz-marshalers.h"
#include "kz-embed.h"
#include "kz-bookmark.h"

enum {
    PROP_0,
    PROP_ENGINE_NAME,
    PROP_SOCKET_ADDRESS,
    PROP_CONNECTION,
    PROP_PROCESS_ID
};

typedef struct _KzDBusEmbedDelegatePrivate	KzDBusEmbedDelegatePrivate;
struct _KzDBusEmbedDelegatePrivate
{
    gchar *socket_address;
    gchar *engine_name;
    DBusGConnection *connection;
    DBusGProxy *proxy;
    GPid process_id;
    guint watch_id;
    gchar *location;
    gboolean ready;
    gchar **titles;
    gchar **uris;
    guint current_position;
};

static GType           kz_type_dbus_embed_delegate = 0;
static GtkSocketClass *kz_dbus_embed_delegate_parent_class;

#define KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_DBUS_EMBED_DELEGATE, KzDBusEmbedDelegatePrivate))

static GObject *constructor  (GType type,
                              guint n_props,
                              GObjectConstructParam *props);
static void     dispose      (GObject      *object);
static void     finalize     (GObject      *object);
static void     set_property (GObject      *object,
                              guint         prop_id,
                              const GValue *value,
                              GParamSpec   *pspec);
static void     get_property (GObject      *object,
                              guint         prop_id,
                              GValue       *value,
                              GParamSpec   *pspec);

static void         load_uri         (KzEmbed      *embed,
                                      const gchar  *uri);
static void         reload           (KzEmbed      *embed,
                                      KzEmbedReloadFlag flags);
static void         stop_load        (KzEmbed      *embed);
static void         go_back          (KzEmbed      *embed);
static void         go_forward       (KzEmbed      *embed);
static gboolean     can_go_back      (KzEmbed      *embed);
static gboolean     can_go_forward   (KzEmbed      *embed);
static void         cut              (KzEmbed      *embed);
static void         copy             (KzEmbed      *embed);
static void         paste            (KzEmbed      *embed);
static gboolean     can_copy_selection
                                     (KzEmbed      *embed);
static gboolean     can_cut_selection(KzEmbed      *embed);
static gboolean     can_paste        (KzEmbed      *embed);
static gboolean     find             (KzEmbed      *embed,
                                      const gchar  *keyword,
                                      gboolean      backward);
static void         zoom             (KzEmbed      *embed,
                                      gdouble       zoom);
static void         set_history      (KzEmbed      *embed,
                                      const GList  *history,
                                      guint         current_position);
static void         get_history      (KzEmbed      *embed,
                                      GList       **history,
                                      guint        *current_position);
static void         set_encoding     (KzEmbed      *embed,
                                      const gchar  *encoding);
static void         get_encoding     (KzEmbed      *embed,
                                      gchar       **encoding,
                                      gboolean     *forced);

static gboolean kz_dbus_embed_delegate_ready(KzDBusEmbedDelegate *object,
                                             gint process_id, guint *embed_id, gchar **engine_name,
                                             GError **error);

#include "kz-dbus-embed-server-bindings.h"

static guint32
kz_dbus_embed_delegate_get_embed_id (KzDBusEmbedDelegate *delegate)
{
    return gtk_socket_get_id(GTK_SOCKET(delegate));
}

static void
child_watch_func (GPid pid, gint status, gpointer data)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(data);

    if (WIFSIGNALED(status)) {
g_print("crash signals!\n");
    } else if (WIFEXITED(status)){
    } else {
g_print("crash!\n");
    }
    g_spawn_close_pid(priv->process_id);
    priv->process_id = -1;
    priv->watch_id = 0;
    priv->process_id = -1;
}

static void
spawn_kz_embed_process (KzDBusEmbedDelegate *delegate)
{
    gboolean success;
    GError *error = NULL;
    gchar *argv[2] = {0};
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(delegate);

    argv[0] = "kz-embed-process";
    argv[1] = priv->socket_address;

    success = g_spawn_async(NULL,
                            argv,
                            NULL,
                            G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
                            NULL,
                            NULL,
                            &priv->process_id,
                            &error);
    if (error) {
        g_print("Error: %s\n", error->message);
        g_error_free(error);
    }
    priv->watch_id = g_child_watch_add(priv->process_id, child_watch_func, delegate);
}

static void
cb_async (DBusGProxy *proxy, GError *error, gpointer data)
{
}

static gboolean
kz_dbus_embed_delegate_ready(KzDBusEmbedDelegate *object,
                             gint process_id, guint *embed_id, gchar **engine_name,
                             GError **error)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    *embed_id = kz_dbus_embed_delegate_get_embed_id(object);
    *engine_name = g_strdup("gecko");

    priv->ready = TRUE;
    if (priv->location)
        load_uri(KZ_EMBED(object), priv->location);
    if (priv->uris) {
        org_kazehakase_Embed_set_history_async(priv->proxy,
                                               (const gchar**)priv->titles,
                                               (const gchar **)priv->uris,
                                               priv->current_position,
                                               cb_async, NULL);
        g_strfreev(priv->titles);
        g_strfreev(priv->uris);
        priv->titles = NULL;
        priv->uris = NULL;
    }
    return TRUE;
}

static KzEmbedEventMouse *
create_kz_embed_event_mouse (gint event_context,
                             gchar *link_uri,
                             gchar *link_text,
                             gchar *image_uri,
                             gchar *frame_src,
                             gint button,
                             gint modifier,
                             gint x,
                             gint y)
{
    KzEmbedEventMouse *kzevent;

    kzevent = (KzEmbedEventMouse *) kz_embed_event_new(KZ_EMBED_EVENT_MOUSE);
    kzevent->cinfo.context = event_context;
    kzevent->cinfo.link = g_strdup(link_uri);
    kzevent->cinfo.linktext = g_strdup(link_text);
    kzevent->cinfo.img = g_strdup(image_uri);
    kzevent->cinfo.frame_src = g_strdup(frame_src);
    kzevent->button = button;
    kzevent->modifier = modifier;
    kzevent->x = x;
    kzevent->y = y;

    return kzevent;
}

static gboolean
emit_dom_mouse_event (KzDBusEmbedDelegate *object,
                      const gchar *signal_name,
                      gint event_context,
                      gchar *link_uri,
                      gchar *link_text,
                      gchar *image_uri,
                      gchar *frame_src,
                      gint button,
                      gint modifier,
                      gint x,
                      gint y)
{
    KzEmbedEventMouse *kzevent;
    gboolean ret = FALSE;

    kzevent = create_kz_embed_event_mouse(event_context,
                                          link_uri,
                                          link_text,
                                          image_uri,
                                          frame_src,
                                          button,
                                          modifier,
                                          x,
                                          y);
    g_signal_emit_by_name(object, signal_name, kzevent, &ret);
    kz_embed_event_free((KzEmbedEvent *) kzevent);
    return ret;
}

static void
cb_location (DBusGProxy *proxy, const gchar *location, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-location", location);
}

static void
cb_title (DBusGProxy *proxy, const gchar *title, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-title", title);
}

static void
cb_net_start (DBusGProxy *proxy, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-net-start");
}

static void
cb_net_stop (DBusGProxy *proxy, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-net-stop");
}

static void
cb_progress (DBusGProxy *proxy, gdouble ratio, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-progress", ratio);
}

static void
cb_link_message (DBusGProxy *proxy, const gchar *link_message, KzDBusEmbedDelegate *object)
{
    g_signal_emit_by_name(object, "kz-link-message", link_message);
}

static void
cb_dom_mouse_down (DBusGProxy *proxy,
                    gint event_context,
                    gchar *link_uri,
                    gchar *link_text,
                    gchar *image_uri,
                    gchar *frame_src,
                    gint button,
                    gint modifier,
                    gint x,
                    gint y,
                    KzDBusEmbedDelegate *object)
{
    emit_dom_mouse_event(object,
                         "kz-dom-mouse-down",
                         event_context,
                         link_uri,
                         link_text,
                         image_uri,
                         frame_src,
                         button,
                         modifier,
                         x,
                         y);
}

static void
cb_dom_mouse_up (DBusGProxy *proxy,
                    gint event_context,
                    gchar *link_uri,
                    gchar *link_text,
                    gchar *image_uri,
                    gchar *frame_src,
                    gint button,
                    gint modifier,
                    gint x,
                    gint y,
                    KzDBusEmbedDelegate *object)
{
    emit_dom_mouse_event(object,
                         "kz-dom-mouse-up",
                         event_context,
                         link_uri,
                         link_text,
                         image_uri,
                         frame_src,
                         button,
                         modifier,
                         x,
                         y);
}

static void
cb_dom_mouse_click (DBusGProxy *proxy,
                    gint event_context,
                    gchar *link_uri,
                    gchar *link_text,
                    gchar *image_uri,
                    gchar *frame_src,
                    gint button,
                    gint modifier,
                    gint x,
                    gint y,
                    KzDBusEmbedDelegate *object)
{
    emit_dom_mouse_event(object,
                         "kz-dom-mouse-click",
                         event_context,
                         link_uri,
                         link_text,
                         image_uri,
                         frame_src,
                         button,
                         modifier,
                         x,
                         y);
}

static void
cb_dom_mouse_over (DBusGProxy *proxy,
                    gint event_context,
                    gchar *link_uri,
                    gchar *link_text,
                    gchar *image_uri,
                    gchar *frame_src,
                    gint button,
                    gint modifier,
                    gint x,
                    gint y,
                    KzDBusEmbedDelegate *object)
{
    emit_dom_mouse_event(object,
                         "kz-dom-mouse-over",
                         event_context,
                         link_uri,
                         link_text,
                         image_uri,
                         frame_src,
                         button,
                         modifier,
                         x,
                         y);
}

static void
kz_embed_iface_init (KzEmbedIFace *iface)
{
    iface->load_uri               = load_uri;
    iface->view_source            = NULL;
    iface->view_current_page_source_in_new 
                                  = NULL;
    iface->can_cut_selection      = can_cut_selection; 
    iface->can_copy_selection     = can_copy_selection; 
    iface->can_paste              = can_paste; 
    iface->cut_selection          = cut;
    iface->copy_selection         = copy;
    iface->paste                  = paste;
    iface->select_all             = NULL;
    iface->get_selection_string   = NULL;
    iface->find                   = find;
    iface->incremental_search     = NULL;
    iface->selection_is_collapsed = NULL;
    iface->get_links              = NULL;
    iface->copy_page              = NULL; 
    iface->shistory_get_pos       = NULL; 
    iface->shistory_get_nth       = NULL; 
    iface->reload                 = reload;
    iface->stop_load              = stop_load;
    iface->go_back                = go_back;
    iface->go_forward             = go_forward;
    iface->can_go_back            = can_go_back;
    iface->can_go_forward         = can_go_forward;
    iface->can_go_nav_link        = NULL;
    iface->go_nav_link            = NULL;
    iface->append_nav_link        = NULL;
    iface->set_nav_link           = NULL;
    iface->set_nth_nav_link       = NULL;
    iface->get_nav_link           = NULL;
    iface->get_nth_nav_link       = NULL;
    iface->get_nav_links          = NULL;
    iface->go_history_index       = NULL;
    iface->get_body_text          = NULL;
    iface->set_encoding           = set_encoding; 
    iface->get_encoding           = get_encoding; 
    iface->print                  = NULL; 
    iface->print_preview          = NULL; 
    iface->get_printer_list       = NULL;
    iface->create_thumbnail       = NULL;
    iface->save_with_content      = NULL;
    iface->set_text_into_textarea = NULL;
    iface->get_text_from_textarea = NULL;
    iface->zoom                   = zoom;
    iface->get_zoom_ratio         = NULL;
    iface->set_text_size          = NULL;
    iface->get_text_size          = NULL;
    iface->get_html_with_contents = NULL;
    iface->set_history            = set_history;
    iface->get_history            = get_history; 
    iface->get_last_modified      = NULL;
    iface->fine_scroll            = NULL;
    iface->page_up                = NULL;
    iface->page_down              = NULL; 
    iface->get_allow_javascript   = NULL;
    iface->set_allow_javascript   = NULL; 
    iface->get_allow_images       = NULL;
    iface->set_allow_images       = NULL;
}

static void
kz_dbus_embed_delegate_class_init (KzDBusEmbedDelegateClass *klass)
{
	GObjectClass *object_class;

	kz_dbus_embed_delegate_parent_class = GTK_SOCKET_CLASS(g_type_class_peek_parent(klass));

	object_class = G_OBJECT_CLASS(klass);
    object_class->constructor = constructor;
    object_class->dispose = dispose;
    object_class->finalize = finalize;
    object_class->set_property = set_property;
    object_class->get_property = get_property;

    g_object_class_install_property
        (object_class,
         PROP_SOCKET_ADDRESS,
         g_param_spec_string("socket-address",
             _("Socket Address"),
             _("Socket address"),
             NULL,
             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
    g_object_class_install_property
        (object_class,
         PROP_CONNECTION,
         g_param_spec_pointer("connection",
             _("Connection"),
             _("Connection"),
             G_PARAM_READWRITE));
    g_object_class_install_property
        (object_class,
         PROP_PROCESS_ID,
         g_param_spec_uint("process-id",
             _("Process ID"),
             _("The ID of embed process"),
             0, G_MAXUINT,
             0,
             G_PARAM_READWRITE));

	g_object_class_override_property(object_class, PROP_ENGINE_NAME, "engine-name");

    dbus_g_object_type_install_info(KZ_TYPE_DBUS_EMBED_DELEGATE, &dbus_glib_kz_dbus_embed_delegate_object_info);
	g_type_class_add_private(object_class, sizeof(KzDBusEmbedDelegatePrivate));
}

static GObject*
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(kz_dbus_embed_delegate_parent_class);

	object = klass->constructor(type, n_props, props);

    spawn_kz_embed_process(KZ_DBUS_EMBED_DELEGATE(object));

	return object;
}

static void
dispose (GObject *object)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    if (priv->connection) {
        dbus_g_connection_unref(priv->connection);
        priv->connection = NULL;
    }
    if (priv->proxy) {
        g_object_unref(priv->proxy);
        priv->proxy = NULL;
    }

    if (G_OBJECT_CLASS(kz_dbus_embed_delegate_parent_class)->dispose)
        G_OBJECT_CLASS(kz_dbus_embed_delegate_parent_class)->dispose(object);
}

static void
finalize (GObject *object)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    if (priv->process_id > 0) {
        kill(priv->process_id, SIGTERM);
        while (priv->process_id > 0)
            g_main_context_iteration(NULL, TRUE);
    }
    if (priv->watch_id > 0) {
        g_source_remove(priv->watch_id);
        priv->watch_id = 0;
    }

    g_free(priv->location);
    g_free(priv->socket_address);
    g_free(priv->engine_name);

    if (G_OBJECT_CLASS(kz_dbus_embed_delegate_parent_class)->finalize)
        G_OBJECT_CLASS(kz_dbus_embed_delegate_parent_class)->finalize(object);
}

static void
set_property (GObject         *object,
              guint            prop_id,
              const GValue    *value,
              GParamSpec      *pspec)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    switch (prop_id)
    {
    case PROP_ENGINE_NAME:
        priv->engine_name = g_value_dup_string(value);
        break;
    case PROP_SOCKET_ADDRESS:
        priv->socket_address = g_value_dup_string(value);
        break;
    case PROP_CONNECTION:
        priv->connection = dbus_g_connection_ref(g_value_get_pointer(value));
        break;
    case PROP_PROCESS_ID:
        priv->process_id = g_value_get_uint(value);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}


static void
get_property (GObject         *object,
              guint            prop_id,
              GValue          *value,
              GParamSpec      *pspec)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    switch (prop_id)
    {
    case PROP_ENGINE_NAME:
        g_value_set_string(value, priv->engine_name);
        break;
    case PROP_SOCKET_ADDRESS:
        g_value_set_string(value, priv->socket_address);
        break;
    case PROP_CONNECTION:
        g_value_set_pointer(value, priv->connection);
        break;
    case PROP_PROCESS_ID:
        g_value_set_uint(value, priv->process_id);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
kz_dbus_embed_delegate_init (KzDBusEmbedDelegate *object)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    priv->engine_name = NULL;
    priv->socket_address = NULL;
    priv->connection = NULL;
    priv->proxy = NULL;
    priv->process_id = -1;
    priv->location = NULL;
    priv->ready = FALSE;
}

GtkWidget *
kz_dbus_embed_delegate_new (const gchar *socket_address)
{
    return GTK_WIDGET(g_object_new(KZ_TYPE_DBUS_EMBED_DELEGATE,
                                   "socket-address", socket_address,
                                   NULL));
}

static void
load_uri (KzEmbed *embed, const gchar *uri)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    if (!priv->ready) {
        priv->location = g_strdup(uri);
        return;
    }

    org_kazehakase_Embed_load_uri_async(priv->proxy, uri, cb_async, NULL);
}

static void
reload (KzEmbed *embed, KzEmbedReloadFlag flags)
{
    GError *error = NULL;
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    if (!org_kazehakase_Embed_reload(priv->proxy, &error)) {
        g_print("%s\n", error->message);
        g_error_free(error);
    }
}

#define DEFINE_SIMPLE_METHOD(method)                                                \
static void                                                                         \
method (KzEmbed *embed)                                                             \
{                                                                                   \
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);   \
    org_kazehakase_Embed_ ## method ## _async(priv->proxy, cb_async, NULL);         \
}

DEFINE_SIMPLE_METHOD(go_back)
DEFINE_SIMPLE_METHOD(go_forward)
DEFINE_SIMPLE_METHOD(cut)
DEFINE_SIMPLE_METHOD(copy)
DEFINE_SIMPLE_METHOD(paste)
DEFINE_SIMPLE_METHOD(stop_load)

#define DEFINE_CAN_GETTER(method)                                                   \
static gboolean                                                                     \
can_ ## method (KzEmbed *embed)                                                     \
{                                                                                   \
    GError *error = NULL;                                                           \
    gboolean possible = FALSE;                                                      \
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);   \
                                                                                    \
    if (!priv->proxy)                                                               \
        return FALSE;                                                               \
    if (!org_kazehakase_Embed_can_ ## method(priv->proxy, &possible,  &error)) {    \
        g_print("%s\n", error->message);                                            \
        g_error_free(error);                                                        \
    }                                                                               \
    return possible;                                                                \
}

DEFINE_CAN_GETTER(go_back)
DEFINE_CAN_GETTER(go_forward)
DEFINE_CAN_GETTER(copy_selection)
DEFINE_CAN_GETTER(cut_selection)
DEFINE_CAN_GETTER(paste)

static gboolean
find (KzEmbed *embed, const gchar *keyword, gboolean backward)
{
    GError *error = NULL;
    gboolean found = FALSE;
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    if (!org_kazehakase_Embed_find(priv->proxy, keyword, backward, &found, &error)) {
        g_print("%s\n", error->message);
        g_error_free(error);
        return FALSE;
    }
    return found;
}

static void
zoom (KzEmbed *embed, gdouble ratio)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    org_kazehakase_Embed_zoom_async(priv->proxy, ratio, cb_async, NULL);
}

static void
setup_dbus_signals (KzDBusEmbedDelegate *object)
{
    KzDBusEmbedDelegatePrivate *priv;

    priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(object);

    dbus_g_proxy_add_signal(priv->proxy, "Location", G_TYPE_STRING, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "Location", G_CALLBACK(cb_location), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "Title", G_TYPE_STRING, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "Title", G_CALLBACK(cb_title), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "NetStart", G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "NetStart", G_CALLBACK(cb_net_start), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "NetStop", G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "NetStop", G_CALLBACK(cb_net_stop), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "Progress", G_TYPE_DOUBLE, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "Progress", G_CALLBACK(cb_progress), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "LinkMessage", G_TYPE_STRING, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "LinkMessage", G_CALLBACK(cb_link_message), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "DomMouseDown",
                            G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                            G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "DomMouseDown", G_CALLBACK(cb_dom_mouse_down), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "DomMouseUp",
                            G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                            G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "DomMouseUp", G_CALLBACK(cb_dom_mouse_up), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "DomMouseClick",
                            G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                            G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "DomMouseClick", G_CALLBACK(cb_dom_mouse_click), object, NULL);
    dbus_g_proxy_add_signal(priv->proxy, "DomMouseOver",
                            G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                            G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID);
    dbus_g_proxy_connect_signal(priv->proxy, "DomMouseOver", G_CALLBACK(cb_dom_mouse_over), object, NULL);
}

void
kz_dbus_embed_delegate_set_connection (KzDBusEmbedDelegate *delegate,
                                       DBusGConnection  *connection)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(delegate);
    priv->connection = dbus_g_connection_ref(connection);
    priv->proxy = dbus_g_proxy_new_for_peer(connection,
                                            KZ_EMBED_PROCESS_PATH,
                                            KZ_EMBED_PROCESS_INTERFACE);
    setup_dbus_signals(delegate);
}

void
kz_dbus_embed_delegate_register_type (GTypeModule *module)
{
    static const GTypeInfo kz_dbus_embed_delegate_info =
    {
        sizeof (KzDBusEmbedDelegateClass),
        NULL,		/* base_init */
        NULL,		/* base_finalize */
        (GClassInitFunc) kz_dbus_embed_delegate_class_init,
        NULL,		/* class_finalize */
        NULL,		/* class_data */
        sizeof (KzDBusEmbedDelegate),
        0,		/* n_preallocs */
        (GInstanceInitFunc) kz_dbus_embed_delegate_init,
    };

    const GInterfaceInfo kz_embed_info =
    {
        (GInterfaceInitFunc) kz_embed_iface_init,
        NULL,
        NULL
    };

    kz_type_dbus_embed_delegate = g_type_module_register_type(module,
                                                              GTK_TYPE_SOCKET,
                                                              "KzDBusEmbedDelegate",
                                                              &kz_dbus_embed_delegate_info,
                                                              (GTypeFlags)0);

    g_type_module_add_interface(module,
                                KZ_TYPE_DBUS_EMBED_DELEGATE,
                                KZ_TYPE_EMBED,
                                &kz_embed_info);
}

GType
kz_dbus_embed_delegate_get_type (void)
{
    return kz_type_dbus_embed_delegate;
}

static void
get_history (KzEmbed *embed, GList **history, guint *current_position)
{
    GError *error = NULL;
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);
    gchar **titles = NULL, **uris = NULL;
    gint i = 0;

    if (!priv->proxy)
        return;

    if (!org_kazehakase_Embed_get_history(priv->proxy, &titles, &uris, current_position, &error)) {
        g_print("%s\n", error->message);
        g_error_free(error);
    }

    while (uris[i]) {
        KzSite *site;
        site = kz_site_new(titles[i], uris[i]);
        *history = g_list_append(*history, site);
        i++;
    }
    g_strfreev(titles);
    g_strfreev(uris);
}

static void
set_history (KzEmbed *embed, const GList *history, guint current_position)
{
	GList *node;
    gint i, n_children;
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);
    gchar **titles = NULL, **uris = NULL;

    n_children = g_list_length((GList*)history);
    titles = g_new0(gchar*, n_children + 1);
    uris = g_new0(gchar*, n_children + 1);

    for (node = g_list_first((GList*)history), i = 0; node; node = g_list_next(node), i++) {
        KzSite *site = (KzSite*)node->data;
        titles[i] = g_strdup(site->title);
        uris[i] = g_strdup(site->uri);
    }

    if (!priv->proxy) {
        priv->titles = titles;
        priv->uris = uris;
        priv->current_position = current_position;
    } else {
        org_kazehakase_Embed_set_history_async(priv->proxy, (const gchar**)titles, (const gchar **)uris, current_position, cb_async, NULL);
    }
}

static void
set_encoding (KzEmbed *embed, const gchar *encoding)
{
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    org_kazehakase_Embed_set_encoding_async(priv->proxy, encoding, cb_async, NULL);
}

static void
get_encoding (KzEmbed *embed, gchar **encoding, gboolean *forced)
{
    GError *error = NULL;
    KzDBusEmbedDelegatePrivate *priv = KZ_DBUS_EMBED_DELEGATE_GET_PRIVATE(embed);

    if (!org_kazehakase_Embed_get_encoding(priv->proxy, encoding, &error)) {
        g_print("%s\n", error->message);
        g_error_free(error);
    }
    *forced = TRUE;
}

/* 
vi:ts=4:nowrap:ai:expandtab:sw=4
*/
