/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  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.
 *
 */

#include "kz-xmlrpc-bookmark.h"

#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "glib-utils.h"
#include "utils.h"

#include "kz-io.h"
#include "kz-xmlrpc.h"
#include "kz-bookmark-separator.h"

enum {
	PROP_0,
	PROP_INTERFACE_URI,
	PROP_USER_NAME,
	PROP_PASSWORD
};


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 insert_child       (KzBookmarkFolder  *folder,
				KzBookmark        *child,
				KzBookmark        *sibling);
static void remove_child       (KzBookmarkFolder  *folder,
				KzBookmark        *child);

static GQuark interface_uri_quark = 0;
static GQuark user_name_quark     = 0;
static GQuark password_quark      = 0;

G_DEFINE_TYPE(KzXMLRPCBookmark, kz_xmlrpc_bookmark, KZ_TYPE_BOOKMARK_FILE)

static void
kz_xmlrpc_bookmark_class_init (KzXMLRPCBookmarkClass *klass)
{
	GObjectClass *object_class;
	KzBookmarkFolderClass *bookmark_folder_class;

	object_class = G_OBJECT_CLASS(klass);
	bookmark_folder_class = KZ_BOOKMARK_FOLDER_CLASS(klass);

	object_class->set_property = set_property;
	object_class->get_property = get_property;

	bookmark_folder_class->insert_child = insert_child;
	bookmark_folder_class->remove_child = remove_child;

	g_object_class_install_property(
		object_class,
		 PROP_INTERFACE_URI,
		 g_param_spec_string(
			 "interface-uri",
			 _("XMLRPC Interface URI"),
			 _("The interface address of the XML-RPC for shared xmlrpc_bookmark"),
			 NULL,
			 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
	g_object_class_install_property(
		object_class,
		 PROP_USER_NAME,
		 g_param_spec_string(
			 "user-name",
			 _("XMLRPC User name"),
			 _("The user name for XMLRPC"),
			 NULL,
			 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
	g_object_class_install_property(
		object_class,
		 PROP_PASSWORD,
		 g_param_spec_string(
			 "password",
			 _("XMLRPC Password"),
			 _("The password for XMLRPC"),
			 NULL,
			 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
	interface_uri_quark = g_quark_from_string("KzXMLRPCBookmark::InterfaceURI");
	user_name_quark     = g_quark_from_string("KzXMLRPCBookmark::UserName");
	password_quark      = g_quark_from_string("KzXMLRPCBookmark::Password");
}


static void
kz_xmlrpc_bookmark_init (KzXMLRPCBookmark *xmlrpc_bookmark)
{
}

#define CHANGE_STR(obj, quark, value) \
{ \
	g_object_set_qdata_full((obj), (quark), (value), \
				(GDestroyNotify) g_free); \
}

static void
set_property (GObject *object,
              guint prop_id,
              const GValue *value,
              GParamSpec *pspec)
{
	switch (prop_id) {
	case PROP_INTERFACE_URI:
		CHANGE_STR(object, interface_uri_quark, g_value_dup_string(value));
		break;
	case PROP_USER_NAME:
		CHANGE_STR(object, user_name_quark, g_value_dup_string(value));
		break;
	case PROP_PASSWORD:
		CHANGE_STR(object, password_quark, g_value_dup_string(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)
{
	gchar *str;

	switch (prop_id) {
	case PROP_INTERFACE_URI:
		str = g_object_get_qdata(object, interface_uri_quark);
		g_value_set_string(value, str);
		break;
	case PROP_USER_NAME:
		str = g_object_get_qdata(object, user_name_quark);
		g_value_set_string(value, str);
		break;
	case PROP_PASSWORD:
		str = g_object_get_qdata(object, password_quark);
		g_value_set_string(value, str);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


KzXMLRPCBookmark *
kz_xmlrpc_bookmark_new (const gchar *location,
		        const gchar *title,
		        const gchar *interface_uri,
                        const gchar *user_name,
                        const gchar *password)
{
	KzXMLRPCBookmark *xmlrpc_bookmark;

	location = location ? location : "";

	xmlrpc_bookmark = g_object_new(KZ_TYPE_XMLRPC_BOOKMARK,
                                       "location",               location,
				       "title",                  title,
				       "interface-uri",          interface_uri,
				       "user-name",		 user_name,
				       "password",		 password,
				       NULL);

	return xmlrpc_bookmark;
}

const gchar *
kz_xmlrpc_bookmark_get_interface_uri (KzXMLRPCBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), interface_uri_quark);
}


const gchar *
kz_xmlrpc_bookmark_get_user_name (KzXMLRPCBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), user_name_quark);
}


const gchar *
kz_xmlrpc_bookmark_get_password (KzXMLRPCBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), password_quark);
}


void
kz_xmlrpc_bookmark_set_interface_uri (KzXMLRPCBookmark *bookmark, const gchar *uri)
{
	g_return_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark));
	g_object_set(bookmark, "interface-uri", uri, NULL);
}

void
kz_xmlrpc_bookmark_set_user_name (KzXMLRPCBookmark *bookmark, const gchar *user_name)
{
	g_return_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark));
	g_object_set(bookmark, "user-name", user_name, NULL);
}

void
kz_xmlrpc_bookmark_set_password (KzXMLRPCBookmark *bookmark, const gchar *password)
{
	g_return_if_fail(KZ_IS_XMLRPC_BOOKMARK(bookmark));
	g_object_set(bookmark, "password", password, NULL);
}

static void
cb_xml_rpc_completed(KzXMLRPC *xmlrpc, GError *error, gpointer data)
{
	const GList *results;
	KzBookmark *file;

	g_signal_handlers_disconnect_by_func(xmlrpc,
					     G_CALLBACK(cb_xml_rpc_completed),
					     data);
	results = kz_xml_rpc_get_results(xmlrpc);
	if (results)
	{
		const gchar *id;
		id = (const gchar*)g_list_nth_data((GList*)results, 0);
		if (data && KZ_IS_BOOKMARK(data))
		{
			kz_bookmark_set_id(KZ_BOOKMARK(data), id);
			file = kz_bookmark_get_parent_file(KZ_BOOKMARK(data));
			/* fake signal */
			g_signal_emit_by_name(file, "load_completed", 0);
			kz_bookmark_file_set_state(KZ_BOOKMARK_FILE(file),
						   KZ_BOOKMARK_FILE_STATE_NORMAL);
		}
	}
	g_object_unref(xmlrpc);
}

static void
cb_bookmark_insert_child (KzBookmark *bookmark,
			  KzBookmark *child, KzBookmark *sibling,
			  gpointer data)
{
}


static void
cb_bookmark_remove_child (KzBookmark *bookmark, KzBookmark *child, gpointer data)
{
}


static void
connect_bookmark_signals (KzBookmark *bookmark, gpointer data)
{
	if (KZ_IS_BOOKMARK_FILE(bookmark)) return;
	if (!kz_bookmark_is_folder(bookmark)) return;

	g_signal_connect_after(bookmark, "insert-child",
			       G_CALLBACK(cb_bookmark_insert_child),
			       data);
	g_signal_connect_after(bookmark, "remove-child",
			       G_CALLBACK(cb_bookmark_remove_child),
			       data);

	/* for children */
	kz_bookmark_folder_foreach_child(KZ_BOOKMARK_FOLDER(bookmark),
					 (GFunc)connect_bookmark_signals, data);
}

static void
disconnect_bookmark_signals (KzBookmark *bookmark, gpointer data)
{
	if (KZ_IS_BOOKMARK_FILE(bookmark)) return;
	if (!kz_bookmark_is_folder(bookmark)) return;

	g_signal_handlers_disconnect_by_func
		(bookmark,
		 G_CALLBACK(cb_bookmark_insert_child), data);
	g_signal_handlers_disconnect_by_func
		(bookmark,
		 G_CALLBACK(cb_bookmark_remove_child), data);

	/* for children */
	kz_bookmark_folder_foreach_child(KZ_BOOKMARK_FOLDER(bookmark),
					 (GFunc)disconnect_bookmark_signals, data);
}

static void
insert_child (KzBookmarkFolder *folder,
	      KzBookmark *sibling,
	      KzBookmark *child)
{
	KzXMLRPC *xmlrpc;
	const gchar *xmlrpc_uri;
	const gchar *title, *link, *desc;
	const gchar *folder_id = NULL, *sibling_id = NULL;
	const gchar *type;

        if (kz_bookmark_file_get_state(KZ_BOOKMARK_FILE(folder)) != KZ_BOOKMARK_FILE_STATE_NORMAL)
                return;
	xmlrpc_uri = kz_xmlrpc_bookmark_get_interface_uri(KZ_XMLRPC_BOOKMARK(folder));
	if (!xmlrpc_uri) return;

	folder_id = kz_bookmark_get_id(KZ_BOOKMARK(folder));
	if (!folder_id) folder_id = "0";
	if (sibling)
		sibling_id = kz_bookmark_get_id(sibling);
	if (!sibling_id) sibling_id = "0";

	if (KZ_IS_BOOKMARK_SEPARATOR(child))
		type = "separator";
	else if (KZ_IS_BOOKMARK_FOLDER(child))
		type = "folder";
	else 
		type = "bookmark";

	title = kz_bookmark_get_title(child);
	link  = kz_bookmark_get_link(child);
	desc  = kz_bookmark_get_description(child);

        connect_bookmark_signals(child, folder);

	xmlrpc = kz_xml_rpc_new(xmlrpc_uri);
	g_signal_connect(xmlrpc, "xml_rpc_completed", 
			 G_CALLBACK(cb_xml_rpc_completed), child);
	kz_xml_rpc_call(xmlrpc, "bookmark.insert",
			kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(folder)), 
			"user-name", "pass",
			folder_id, sibling_id,
			type,
			"title", title, 
			"link", link, 
			"desc", desc,
			NULL);
	/* fake signal */
	kz_bookmark_file_set_state(KZ_BOOKMARK_FILE(folder), KZ_BOOKMARK_FILE_STATE_LOADING);
	g_signal_emit_by_name(folder, "load-start", 0);
}

static void
remove_child (KzBookmarkFolder *folder,
	      KzBookmark *child)
{
	KzXMLRPC *xmlrpc;
	const gchar *xmlrpc_uri;
	const gchar *id = NULL;

        if (kz_bookmark_file_get_state(KZ_BOOKMARK_FILE(folder)) != KZ_BOOKMARK_FILE_STATE_NORMAL)
                return;

	xmlrpc_uri = kz_xmlrpc_bookmark_get_interface_uri(KZ_XMLRPC_BOOKMARK(folder));
	if (!xmlrpc_uri) return;

	id = kz_bookmark_get_id(child);
        disconnect_bookmark_signals(child, folder);

	xmlrpc = kz_xml_rpc_new(xmlrpc_uri);
	g_signal_connect(xmlrpc, "xml_rpc_completed", 
			 G_CALLBACK(cb_xml_rpc_completed), NULL);
	kz_xml_rpc_call(xmlrpc, "bookmark.remove",
			kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(folder)), 
			"user-name", "pass",
			id,
			NULL);
}

