/*
 * Copyright (c) 2002, 2003, 2004 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include <string.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gmodule.h>
#include "st-plugin.h"

/*** type definitions ********************************************************/

enum {
  PROP_0,
  PROP_FILENAME,
  PROP_ONLY_INFO
};

struct _STPluginPrivate
{
  char		*filename;
  gboolean	only_info;

  char		*name;
  char		*label;
  GdkPixbuf	*pixbuf;

  char		*error;
  gboolean	loaded;
};

/*** variable declarations ***************************************************/

static GObjectClass *parent_class = NULL;
STPlugin *st_plugin_current = NULL;

/*** function declarations ***************************************************/

static void st_plugin_class_init (STPluginClass *class);
static void st_plugin_init (STPlugin *plugin);
static GObject *st_plugin_constructor (GType type,
				       unsigned int n_construct_properties,
				       GObjectConstructParam *construct_params);
static void st_plugin_set_property (GObject *object,
				    unsigned int prop_id,
				    const GValue *value,
				    GParamSpec *pspec);

/*** API implementation ******************************************************/

GType
st_plugin_get_type (void)
{
  static GType plugin_type = 0;
  
  if (! plugin_type)
    {
      static const GTypeInfo plugin_info = {
	sizeof(STPluginClass),
	NULL,
	NULL,
	(GClassInitFunc) st_plugin_class_init,
	NULL,
	NULL,
	sizeof(STPlugin),
	0,
	(GInstanceInitFunc) st_plugin_init,
      };
      
      plugin_type = g_type_register_static(G_TYPE_OBJECT,
					   "STPlugin",
					   &plugin_info,
					   0);
    }
  
  return plugin_type;
}

/**
 * st_plugin_set_name:
 * @plugin: a plugin.
 * @name: the plugin name.
 *
 * Sets the plugin internal name.
 **/
void
st_plugin_set_name (STPlugin *plugin, const char *name)
{
  g_return_if_fail(ST_IS_PLUGIN(plugin));
  g_return_if_fail(name != NULL);
  g_return_if_fail(plugin->priv->name == NULL);

  plugin->priv->name = g_strdup(name);
}

/**
 * st_plugin_set_label:
 * @plugin: a plugin.
 * @label: the plugin label.
 *
 * Sets the plugin label, which will be used in the plugins
 * preferences, and in various other places of the user interface.
 **/
void
st_plugin_set_label (STPlugin *plugin, const char *label)
{
  g_return_if_fail(ST_IS_PLUGIN(plugin));
  g_return_if_fail(label != NULL);
  g_return_if_fail(plugin->priv->label == NULL);

  plugin->priv->label = g_strdup(label);
}

/**
 * st_plugin_set_icon_from_pixbuf:
 * @plugin: a plugin.
 * @pixbuf: an icon.
 *
 * Sets the plugin icon from a #GdkPixbuf.
 **/
void
st_plugin_set_icon_from_pixbuf (STPlugin *plugin, GdkPixbuf *pixbuf)
{
  g_return_if_fail(ST_IS_PLUGIN(plugin));
  g_return_if_fail(GDK_IS_PIXBUF(pixbuf));
  g_return_if_fail(plugin->priv->pixbuf == NULL);

  plugin->priv->pixbuf = g_object_ref(pixbuf);
}

/*** private implementation **************************************************/

static void
st_plugin_class_init (STPluginClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS(class);

  parent_class = g_type_class_peek_parent(class);

  g_type_class_add_private(class, sizeof(STPluginPrivate));

  object_class->constructor = st_plugin_constructor;
  object_class->set_property = st_plugin_set_property;

  /* private properties */

  g_object_class_install_property(object_class,
				  PROP_FILENAME,
				  g_param_spec_string("filename",
						      NULL,
						      NULL,
						      NULL,
						      G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_ONLY_INFO,
				  g_param_spec_boolean("only-info",
						       NULL,
						       NULL,
						       FALSE,
						       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}

static void
st_plugin_init (STPlugin *plugin)
{
  plugin->priv = G_TYPE_INSTANCE_GET_PRIVATE(plugin, ST_TYPE_PLUGIN, STPluginPrivate);
}

static GObject *
st_plugin_constructor (GType type,
		       unsigned int n_construct_properties,
		       GObjectConstructParam *construct_params)
{
  GObject *object; 
  STPlugin *plugin;
  GModule *module;
  gpointer func;

  object = parent_class->constructor(type, n_construct_properties, construct_params);
  plugin = ST_PLUGIN(object);

  st_plugin_current = plugin;
  
  module = g_module_open(plugin->priv->filename, 0);
  if (module)
    {
      if (g_module_symbol(module, "plugin_get_info", &func))
	{
	  GError *err = NULL;

	  if (! ((STPluginGetInfoCallback) func)(plugin, &err))
	    {
	      plugin->priv->error = g_strdup(err->message);
	      g_error_free(err);
	    }
	}
    }
  else
    plugin->priv->error = g_strdup(g_module_error());
  
  if (! plugin->priv->name)
    {
      char *basename;
      char *extension;
      
      basename = g_path_get_basename(plugin->priv->filename);
      extension = strrchr(basename, '.');
      if (extension)
	{
	  plugin->priv->name = g_strndup(basename, extension - basename);
	  g_free(basename);
	}
      else
	plugin->priv->name = basename;
    }
  if (! plugin->priv->label)
    plugin->priv->label = g_strdup(plugin->priv->name);

  if (! plugin->priv->error && ! plugin->priv->only_info)
    {
      if (g_module_symbol(module, "plugin_init", &func))
	{
	  GError *err = NULL;
	  
	  if (((STPluginInitCallback) func)(&err))
	    plugin->priv->loaded = TRUE;
	  else
	    {
	      plugin->priv->error = g_strdup(err->message);
	      g_error_free(err);
	    }
	}
      else
	plugin->priv->error = g_strdup(_("unable to find plugin_init()"));
    }
  
  if (module && ! plugin->priv->loaded)
    g_module_close(module);

  st_plugin_current = NULL;

  return object;
}

static void
st_plugin_set_property (GObject *object,
			unsigned int prop_id,
			const GValue *value,
			GParamSpec *pspec)
{
  STPlugin *plugin = ST_PLUGIN(object);

  switch (prop_id)
    {
    case PROP_FILENAME:
      plugin->priv->filename = g_value_dup_string(value);
      break;

    case PROP_ONLY_INFO:
      plugin->priv->only_info = g_value_get_boolean(value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
    }
}

STPlugin *
st_plugin_new (const char *filename, gboolean only_info)
{
  g_return_val_if_fail(filename != NULL, NULL);

  return g_object_new(ST_TYPE_PLUGIN,
		      "filename", filename,
		      "only-info", only_info,
		      NULL);
}

const char *
st_plugin_get_filename (STPlugin *plugin)
{
  g_return_val_if_fail(ST_IS_PLUGIN(plugin), NULL);

  return plugin->priv->filename;
}

const char *
st_plugin_get_name (STPlugin *plugin)
{
  g_return_val_if_fail(ST_IS_PLUGIN(plugin), NULL);

  return plugin->priv->name;
}

const char *
st_plugin_get_label (STPlugin *plugin)
{
  g_return_val_if_fail(ST_IS_PLUGIN(plugin), NULL);

  return plugin->priv->label;
}

GdkPixbuf *
st_plugin_get_pixbuf (STPlugin *plugin)
{
  g_return_val_if_fail(ST_IS_PLUGIN(plugin), NULL);

  return plugin->priv->pixbuf;
}

const char *
st_plugin_get_error (STPlugin *plugin)
{
  g_return_val_if_fail(ST_IS_PLUGIN(plugin), NULL);

  return plugin->priv->error;
}
