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

/*
 *  Copyright (C) 2003 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 <stdio.h>
#include <glib/gi18n.h>

#include "gobject-utils.h"
#include "kz-downloader.h"

enum {
	START_SIGNAL,
	COMPLETED_SIGNAL,
	ERROR_SIGNAL,
	IO_IN_SIGNAL,
	LAST_SIGNAL
};

enum {
	PROP_0,
	PROP_URI,
	PROP_FILENAME
};

static void     kz_downloader_class_init   (KzDownloaderClass *klass);
static void     kz_downloader_init         (KzDownloader *downloader);
static void     kz_downloader_dispose      (GObject *object);
static GObject *kz_downloader_constructor  (GType type,
					    guint n_props,
					    GObjectConstructParam *props);
static void     kz_downloader_set_property (GObject *object,
	            			    guint prop_id,
	            			    const GValue *value,
	            			    GParamSpec *pspec);
static void     kz_downloader_get_property (GObject *object,
					    guint prop_id,
					    GValue *value,
					    GParamSpec *pspec);

static void kz_downloader_connect_signals    (KzDownloader *downloader);
static void kz_downloader_disconnect_signals (KzDownloader *downloader);

static void kz_downloader_real_cancel      (KzDownloader *downloader);
static gint kz_downloader_real_get_percent (KzDownloader *downloader);

static void cb_completed                   (KzIO *io,
					    GError *error,
					    KzDownloader *downloader);
static void cb_io_in                       (KzIO *io,
					    guint len,
					    const gchar *buf,
					    KzDownloader *downloader);

static GObjectClass *parent_class = NULL;
static gint kz_downloader_signals[LAST_SIGNAL] = {0};

KZ_OBJECT_GET_TYPE(kz_downloader, "KzDownloader", KzDownloader,
		   kz_downloader_class_init, kz_downloader_init,
		   G_TYPE_OBJECT)

static void
kz_downloader_class_init (KzDownloaderClass *klass)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent (klass);
	object_class = (GObjectClass *) klass;

	object_class->constructor  = kz_downloader_constructor;
	object_class->dispose      = kz_downloader_dispose;
	object_class->set_property = kz_downloader_set_property;
	object_class->get_property = kz_downloader_get_property;

	klass->start     = NULL;
	klass->completed = NULL;
	klass->error     = NULL;
	klass->io_in     = NULL;

	klass->kz_downloader_cancel      = kz_downloader_real_cancel;
	klass->kz_downloader_get_percent = kz_downloader_real_get_percent;

	g_object_class_install_property(
		object_class,
		 PROP_URI,
		 g_param_spec_string(
			 "uri",
			 _("URI"),
			 _("The URI of Target"),
			 NULL,
			 G_PARAM_READWRITE |
			 G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property(
		object_class,
		 PROP_FILENAME,
		 g_param_spec_string(
			 "filename",
			 _("Filename"),
			 _("The Local Filename"),
			 NULL,
			 G_PARAM_READWRITE));

	kz_downloader_signals[START_SIGNAL]
		= g_signal_new ("start",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzDownloaderClass, start),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);
	kz_downloader_signals[COMPLETED_SIGNAL]
		= g_signal_new ("completed",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzDownloaderClass, completed),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);
	kz_downloader_signals[ERROR_SIGNAL]
		= g_signal_new ("error",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzDownloaderClass, error),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);
	kz_downloader_signals[IO_IN_SIGNAL]
		= g_signal_new ("io_in",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzDownloaderClass, io_in),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);
}


static void
kz_downloader_init (KzDownloader *downloader)
{
	downloader->io       = NULL;
	downloader->uri      = NULL;
	downloader->filename = NULL;
}


static GObject*
kz_downloader_constructor (GType                  type,
			   guint                  n_props,
			   GObjectConstructParam *props)
{
	KzDownloader *dl;
	KzIO *io;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(parent_class);

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

	dl = KZ_DOWNLOADER(object);

	io = kz_io_new(dl->uri);

	if(!io)
		return object;
	
	dl->io = io;

	return object;
}

static void
kz_downloader_dispose (GObject *object)
{
	KzDownloader *downloader;

	downloader = KZ_DOWNLOADER(object);
	if (downloader->io)
		g_object_unref(downloader->io);
	if (downloader->uri)
		g_free(downloader->uri);
	if (downloader->filename)
		g_free(downloader->filename);
	downloader->io       = NULL;
	downloader->uri      = NULL;
	downloader->filename = NULL;

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


static void
kz_downloader_set_property (GObject *object,
			    guint prop_id,
			    const GValue *value,
			    GParamSpec *pspec)
{
	KzDownloader *downloader = KZ_DOWNLOADER(object);

	switch (prop_id)
	{
	case PROP_URI:
		downloader->uri = g_value_dup_string(value);
		break;
	case PROP_FILENAME:
		if (downloader->filename)
			g_free(downloader->filename);
		downloader->filename = g_value_dup_string(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
kz_downloader_get_property (GObject *object,
			    guint prop_id,
			    GValue *value,
			    GParamSpec *pspec)
{
	KzDownloader *downloader = KZ_DOWNLOADER(object);

	switch (prop_id)
	{
	case PROP_URI:
		g_value_set_string(value, downloader->uri);
		break;
	case PROP_FILENAME:
		g_value_set_string(value, downloader->filename);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


KzDownloader *
kz_downloader_new (const gchar *uri)
{
	KzDownloader *dl;
	
	dl = g_object_new(KZ_TYPE_DOWNLOADER,
		 	  "uri", uri,
			  NULL);
	
	/* FIXME! */
	if (!dl->io)
	{
		g_object_unref(dl);
		return NULL;
	}

	return dl;
}


KzDownloader *
kz_downloader_new_with_filename (const gchar *uri, const gchar *filename)
{
	KzDownloader *dl;

	dl = g_object_new(KZ_TYPE_DOWNLOADER,
			  "uri",      uri,
			  "filename", filename,
			  NULL);

	/* FIXME! */
	if (!dl->io)
	{
		g_object_unref(dl);
		return NULL;
	}

	return dl;
}


static void
kz_downloader_connect_signals (KzDownloader *downloader)
{
	g_signal_connect(downloader->io, "io_completed",
			 G_CALLBACK(cb_completed), downloader);
	g_signal_connect(downloader->io, "io_progress",
			 G_CALLBACK(cb_io_in), downloader);
}


static void
kz_downloader_disconnect_signals (KzDownloader *downloader)
{
	g_signal_handlers_disconnect_by_func(downloader->io,
					     G_CALLBACK(cb_completed),
					     downloader);
	g_signal_handlers_disconnect_by_func(downloader->io,
					     G_CALLBACK(cb_io_in),
					     downloader);
}


static void 
cb_completed(KzIO *io, GError *error, KzDownloader *downloader)
{
	g_return_if_fail(KZ_IS_DOWNLOADER(downloader));

	kz_downloader_disconnect_signals(downloader);
	if (error)
	{
		g_signal_emit(downloader,
			      kz_downloader_signals[ERROR_SIGNAL],
			      0);
	}
	else
	{
		g_signal_emit(downloader,
			      kz_downloader_signals[COMPLETED_SIGNAL],
			      0);
	}
}


static void 
cb_io_in(KzIO *io, guint len, const gchar *buf, KzDownloader *downloader)
{
	g_return_if_fail(KZ_IS_DOWNLOADER(downloader));

	/* emit progress signal */
	g_signal_emit(downloader,
		      kz_downloader_signals[IO_IN_SIGNAL],
		      0);	
}


static	void
kz_downloader_real_cancel (KzDownloader *downloader)
{
	g_return_if_fail(KZ_IS_DOWNLOADER(downloader));

	kz_io_stop(downloader->io); 
}


static	gint
kz_downloader_real_get_percent (KzDownloader *downloader)
{
	glong total, current;
	gint percent;
	
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), -1);

	total   = kz_downloader_get_total_progress(downloader);
	if (total <= 0)
		return -1;
	current = kz_downloader_get_current_progress(downloader); 
	percent = current * 100 / total;

	return percent;
}


const gchar *
kz_downloader_get_string (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), NULL);
	g_return_val_if_fail(downloader->io, NULL);
	
	return kz_io_get_buffer(downloader->io);
}

gsize
kz_downloader_get_size (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), NULL);
	g_return_val_if_fail(downloader->io, NULL);
	
	return kz_io_get_file_size(downloader->io);
}


gint
kz_downloader_get_percent (KzDownloader *downloader)
{
	return KZ_DOWNLOADER_GET_CLASS(downloader)->kz_downloader_get_percent(downloader);
}


gsize
kz_downloader_get_current_progress (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), -1);
	return kz_io_get_loaded_size(downloader->io);
}


gsize
kz_downloader_get_total_progress (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), -1);
	return kz_io_get_file_size(downloader->io);
}


gboolean
kz_downloader_to_file (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), FALSE);
	
	kz_downloader_connect_signals(downloader);

	/* create filename from uri if not exist */
	if (!downloader->filename)
	{
		
	}
	else
		kz_io_load_to_file(downloader->io, downloader->filename);

	return TRUE;
}

gboolean
kz_downloader_to_buffer (KzDownloader *downloader)
{
	g_return_val_if_fail(KZ_IS_DOWNLOADER(downloader), FALSE);
	
	kz_downloader_connect_signals(downloader);

	kz_io_load_to_buffer(downloader->io);

	return TRUE;
}


void
kz_downloader_cancel (KzDownloader *downloader)
{
	KZ_DOWNLOADER_GET_CLASS(downloader)->kz_downloader_cancel(downloader);
}
