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

/*
 *  Copyright (C) 2003 Hiroyuki Ikezoe
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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 <glib/gi18n.h>
#include "kz-downloader-group.h"

enum {
	PROP_0,
	PROP_DOWNLOADER_ITEMS
};

enum {
	ADD_SIGNAL,
	REMOVE_SIGNAL,
	ERROR_SIGNAL,
	PROGRESS_SIGNAL,
	LAST_SIGNAL
};

KzDownloaderGroup *dlgrp_single = NULL;

static void dispose      (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 connect_signals                (KzDownloaderGroup *dlgrp,
					    KzDownloader      *dl);
static void disconnect_signals             (KzDownloaderGroup *dlgrp,
					    KzDownloader      *dl);

static void cb_download_complete (KzDownloader *dl, KzDownloaderGroup *dlgrp);
static void cb_download_error    (KzDownloader *dl, KzDownloaderGroup *dlgrp);
static void cb_download_progress (KzDownloader *dl, KzDownloaderGroup *dlgrp);

static gint kz_downloader_group_signals[LAST_SIGNAL] = {0};

G_DEFINE_TYPE(KzDownloaderGroup, kz_downloader_group, G_TYPE_OBJECT)

static void
kz_downloader_group_class_init (KzDownloaderGroupClass *klass)
{
	GObjectClass *gobject_class;

	kz_downloader_group_parent_class = g_type_class_peek_parent (klass);

	gobject_class = (GObjectClass *) klass;

	/* GObject class */
	gobject_class->dispose      = dispose;
	gobject_class->set_property = set_property;
	gobject_class->get_property = get_property;

	klass->add       = NULL;
	klass->remove    = NULL;
	klass->error     = NULL;
	klass->progress  = NULL;

	g_object_class_install_property
		(gobject_class,
		 PROP_DOWNLOADER_ITEMS,
		 g_param_spec_pointer ("kz-downloader-items",
				      _("KzDownloaderItems"),
				      _("The GSList of KzDownloader items"),
				      G_PARAM_READWRITE)); 

	kz_downloader_group_signals[ADD_SIGNAL]
		= g_signal_new ("add",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzDownloaderGroupClass, add),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				KZ_TYPE_DOWNLOADER);
	kz_downloader_group_signals[REMOVE_SIGNAL]
		= g_signal_new ("remove",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzDownloaderGroupClass, remove),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				KZ_TYPE_DOWNLOADER);
	kz_downloader_group_signals[ERROR_SIGNAL]
		= g_signal_new ("error",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzDownloaderGroupClass, error),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				KZ_TYPE_DOWNLOADER);
	kz_downloader_group_signals[PROGRESS_SIGNAL]
		= g_signal_new ("progress",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzDownloaderGroupClass, progress),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				KZ_TYPE_DOWNLOADER);
}


static void
kz_downloader_group_init (KzDownloaderGroup *dlgrp)
{
	dlgrp->items = NULL;
}


void
dispose (GObject *object)
{
	KzDownloaderGroup *dlgrp = KZ_DOWNLOADER_GROUP(object);

	if (dlgrp->items)
	{
		g_slist_free(dlgrp->items);
		dlgrp->items = NULL;
	}

	if (dlgrp_single && dlgrp_single == dlgrp)
	{
		dlgrp_single = NULL;
	}

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


static void
set_property (GObject      *object,
              guint         prop_id,
              const GValue *value,
              GParamSpec   *pspec)
{
	KzDownloaderGroup *dlgrp = KZ_DOWNLOADER_GROUP (object);

	switch (prop_id)
       	{
	case PROP_DOWNLOADER_ITEMS:
		dlgrp->items = g_value_get_pointer(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)
{
	KzDownloaderGroup *dlgrp = KZ_DOWNLOADER_GROUP (object);

	switch (prop_id)
       	{
	case PROP_DOWNLOADER_ITEMS:
		g_value_set_pointer(value, dlgrp->items);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


KzDownloaderGroup *
kz_downloader_group_new (void)
{
	KzDownloaderGroup *dlgrp = g_object_new(KZ_TYPE_DOWNLOADER_GROUP, NULL);

	return dlgrp;
}


KzDownloaderGroup *
kz_downloader_group_get_instance (void)
{
	if (!dlgrp_single)
		dlgrp_single = kz_downloader_group_new();
	else
		g_object_ref(dlgrp_single);

	return dlgrp_single;
}


static void
disconnect_signals(KzDownloaderGroup *dlgrp, KzDownloader *dl)
{
	g_signal_handlers_disconnect_by_func(dl,
					     G_CALLBACK(cb_download_complete),
					     dlgrp);
	g_signal_handlers_disconnect_by_func(dl,
					     G_CALLBACK(cb_download_error),
					     dlgrp);
	g_signal_handlers_disconnect_by_func(dl,
					     G_CALLBACK(cb_download_progress),
					     dlgrp);
}


static void
connect_signals (KzDownloaderGroup *dlgrp, KzDownloader *dl)
{
	/* connect signals */
	g_signal_connect(dl, "completed",
			 G_CALLBACK(cb_download_complete), dlgrp);
	g_signal_connect(dl, "error",
			 G_CALLBACK(cb_download_error), dlgrp);	
	g_signal_connect(dl, "io_in",
			 G_CALLBACK(cb_download_progress), dlgrp);	

	g_signal_emit(dlgrp,
		      kz_downloader_group_signals[ADD_SIGNAL],
		      0, dl);
}


static void
cb_download_complete (KzDownloader *dl, KzDownloaderGroup *dlgrp)
{
	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));

	disconnect_signals(dlgrp, dl);

	kz_downloader_group_remove_item(dlgrp, dl);
}


static void
cb_download_error (KzDownloader *dl, KzDownloaderGroup *dlgrp)
{
	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));

	disconnect_signals(dlgrp, dl);

	kz_downloader_group_remove_item(dlgrp, dl);
}


static void
cb_download_progress (KzDownloader *dl, KzDownloaderGroup *dlgrp)
{
	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));

	g_signal_emit(dlgrp,
		      kz_downloader_group_signals[PROGRESS_SIGNAL],
		      0, dl);
}


void
kz_downloader_group_add_item (KzDownloaderGroup *dlgrp,
			      const gchar *uri)
{
	KzDownloader *dl;
	gchar *basename;
	GSList *list;

	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));
	g_return_if_fail(uri);

	basename = g_path_get_basename(uri);
	dl = kz_downloader_new_with_filename(uri, basename);
	if (!dl)
		return;

	list = g_slist_append(dlgrp->items, dl);
	g_object_set(G_OBJECT(dlgrp), "kz-downloader-items", list, NULL);
	connect_signals(dlgrp, dl);

	kz_downloader_to_file(dl);

	g_free(basename);
}


void
kz_downloader_group_add_item_with_filename (KzDownloaderGroup *dlgrp,
			      		    const gchar *uri,
					    const gchar *filename)
{
	KzDownloader *dl;
	GSList *list;

	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));
	g_return_if_fail(uri);

	dl = kz_downloader_new_with_filename(uri, filename);
	if (!dl)
		return;

	list = g_slist_append(dlgrp->items, dl);
	g_object_set(G_OBJECT(dlgrp), "kz-downloader-items", list, NULL);
	connect_signals(dlgrp, dl);
	
	kz_downloader_to_file(dl);
}


void
kz_downloader_group_add_downloader (KzDownloaderGroup *dlgrp,
				    KzDownloader *dl)
{
	GSList *list;
	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));
	g_return_if_fail(KZ_IS_DOWNLOADER(dl));

	g_object_ref(G_OBJECT(dl));
	list = g_slist_append(dlgrp->items, KZ_DOWNLOADER(dl));
	g_object_set(G_OBJECT(dlgrp), "kz-downloader-items", list, NULL);

	connect_signals(dlgrp, KZ_DOWNLOADER(dl));
}


static gboolean
idle_remove_item(gpointer data)
{
	KzDownloader *dl = data;

	g_object_unref(G_OBJECT(dl));

	return FALSE;
}


void
kz_downloader_group_remove_item (KzDownloaderGroup *dlgrp, KzDownloader *dl)
{
	GSList *list;
	g_return_if_fail(KZ_IS_DOWNLOADER_GROUP(dlgrp));
	g_return_if_fail(KZ_IS_DOWNLOADER(dl));
	g_signal_emit(dlgrp,
		      kz_downloader_group_signals[REMOVE_SIGNAL],
		      0, dl);
	list = g_slist_remove(dlgrp->items, dl);
	g_object_set(G_OBJECT(dlgrp), "kz-downloader-items", list, NULL);
	g_idle_add(idle_remove_item, dl);
}
