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

/*
 *  Copyright (C) 2006 Kouhei Sutou
 *
 *  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-ext.h"

#include "gobject-utils.h"
#include "glib-utils.h"
#include "kz-marshalers.h"

#define EXT_NAME "kzext"
#define EXT_INIT_FUNC "kz_ext_init"
#define EXT_CLOSE_FUNC "kz_ext_close"

static void     kz_ext_class_init	(KzExtClass	*klass);
static void     kz_ext_init		(KzExt		*ext);
static void	kz_ext_dispose		(GObject	*object);


static GObjectClass *parent_class = NULL;

KZ_OBJECT_GET_TYPE(kz_ext, "KzExt", KzExt,
		   kz_ext_class_init, kz_ext_init,
		   G_TYPE_OBJECT)

static void
kz_ext_class_init (KzExtClass *klass)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent(klass);

	object_class  = (GObjectClass *)klass;

	object_class->dispose = kz_ext_dispose;
}

static void
kz_ext_init (KzExt *ext)
{
	ext->name = NULL;
	ext->path = NULL;
	ext->module = NULL;
}

static gboolean
kz_ext_load_func (GModule *module, const gchar *func_name, gpointer *symbol)
{
	if (g_module_symbol(module, func_name, symbol))
	{
		return TRUE;
	}
	else
	{
		gchar *name = g_strdup(g_module_name(module));
		g_warning("%s: %s", name, g_module_error());
		g_free(name);
		return FALSE;
	}
}

static void
kz_ext_close_module (GModule *module, gboolean success)
{
	gchar *name;
	KzExtCloseFunc close_func;
	KzExtCloseFunc *close_func_p;
	gpointer *p;

	close_func_p = &close_func;
	p = (gpointer *)close_func_p;
	if (kz_ext_load_func(module, EXT_CLOSE_FUNC, p))
		close_func(success);

	name = g_strdup(g_module_name(module));
	if (!g_module_close(module))
		g_warning("%s: %s", name, g_module_error());
	g_free(name);
}

static void
kz_ext_dispose (GObject *object)
{
	KzExt *ext = KZ_EXT(object);

	if (ext->name)
	{
		g_free(ext->name);
		ext->name = NULL;
	}

	if (ext->path)
	{
		g_free(ext->path);
		ext->path = NULL;
	}

	if (ext->module)
	{
		kz_ext_close_module(ext->module, TRUE);
		ext->module = NULL;
	}

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

static KzExt *
kz_ext_load_ext(const char *name, KzWindow *kz)
{
	gchar *mod_dir;
	gchar *mod_path;
	GModule *module;
	KzExt *ext = NULL;

	mod_dir = g_build_filename(EXTDIR, name, NULL);
	mod_path = g_module_build_path(mod_dir, EXT_NAME);
	module = g_module_open(mod_path, G_MODULE_BIND_LAZY);

	if (module)
	{
		KzExtInitFunc init_func;
		KzExtInitFunc *init_func_p;
		gpointer *p;

		init_func_p = &init_func;
		p = (gpointer *)init_func_p;
		if (kz_ext_load_func(module, EXT_INIT_FUNC, p))
		{
			init_func(kz);
			ext = g_object_new(KZ_TYPE_EXT, NULL);
			ext->name = g_strdup(name);
			ext->path = g_strdup(mod_path);
			ext->module = module;
		}
		else
		{
			kz_ext_close_module(module, FALSE);
		}
	}
	else
	{
		g_warning("%s", g_module_error());
	}

	g_free(mod_dir);
	g_free(mod_path);

	return ext;
}

GList *
kz_ext_load(KzWindow *kz)
{
	GDir *dir;
	GList *exts = NULL;

	dir = g_dir_open(EXTDIR, 0, NULL);

	if (dir) {
		const gchar *entry;
		KzExt *ext;

		while ((entry = g_dir_read_name(dir)))
		{
			ext = kz_ext_load_ext(entry, kz);
			if (ext)
				exts = g_list_append(exts, ext);
		}

		g_dir_close(dir);
	}

	return exts;
}
