/*
 * Copyright (c) 2004-2006 Jean-François Wauthy (pollux@xfce.org)
 *
 * 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 of the License, 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 Library 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib.h>
#include <gmodule.h>

#include <libxfprint/common.h>
#include <libxfprint/printing-system.h>

#define PRINTING_SYSTEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PRINTING_SYSTEM_TYPE, PrintingSystemPrivate))

/* Prototypes */
static void printing_system_class_init (PrintingSystemClass * klass);
static void printing_system_init (PrintingSystem * sp);
static void printing_system_finalize (GObject * object);

enum
{
  PROP_0,
  PROP_PS_PATH,
};

struct _PrintingSystemPrivate
{
  gchar *path;
  gboolean plugin_loaded;

  GList *(*ps_get_printers) (void);
  Printer *(*ps_get_default_printer) (void);
  gint (*ps_get_printer_state) (const gchar * printer);
  gint (*ps_get_printer_jobs_count) (const gchar * printer);

  gboolean (*ps_remove_job) (const gchar * printer, gint id);
  GList *(*ps_get_jobs) (const gchar * printer);

  gboolean (*ps_print_file) (const gchar * printer, const gchar * original_name, const gchar * file, gboolean remove_file);

  void (*ps_customize_printer_list_window) (PrinterListWindow * win);
  void (*ps_customize_printer_queue_window) (PrinterQueueWindow * win);
};

static GObjectClass *parent_class = NULL;

/* class */

static gboolean
load_printing_system (PrintingSystem * ps)
{
  GModule *gm;
  char *fullpath;
  PrintingSystemPrivate *priv = PRINTING_SYSTEM_GET_PRIVATE (ps);
  gchar *path = priv->path;

  if (path != NULL && *path == '/') {
    fullpath = g_strdup (path);
  }
  else {
    fullpath = g_build_filename (PLUGIN_DIR, path, NULL);
  }
  DBG ("trying to load printing system module %s", fullpath);
  gm = g_module_open (fullpath, 0);
  g_free (fullpath);

  if (!gm)
    return FALSE;

  if (!g_module_symbol (gm, "name", (gpointer) & ps->name)) {
    g_module_close (gm);
    g_warning ("name symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "description", (gpointer) & ps->description)) {
    g_module_close (gm);
    g_warning ("description symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "version", (gpointer) & ps->version)) {
    g_module_close (gm);
    g_warning ("version symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "homepage", (gpointer) & ps->homepage)) {
    g_module_close (gm);
    g_warning ("homepage symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "author", (gpointer) & ps->author)) {
    g_module_close (gm);
    g_warning ("author symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "get_printer_state", (gpointer) & priv->ps_get_printer_state)) {
    g_module_close (gm);
    g_warning ("get_printer_state symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "get_printer_jobs_count", (gpointer) & priv->ps_get_printer_jobs_count)) {
    g_module_close (gm);
    g_warning ("get_printer_jobs_count symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "get_printers", (gpointer) & priv->ps_get_printers)) {
    g_module_close (gm);
    g_warning ("get_printers symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "remove_job", (gpointer) & priv->ps_remove_job)) {
    g_module_close (gm);
    g_warning ("remove_job symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "get_jobs", (gpointer) & priv->ps_get_jobs)) {
    g_module_close (gm);
    g_warning ("get_jobs symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "get_default_printer", (gpointer) & priv->ps_get_default_printer)) {
    g_module_close (gm);
    g_warning ("get_default_printer symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "print_file", (gpointer) & priv->ps_print_file)) {
    g_module_close (gm);
    g_warning ("print_file symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "customize_printer_list_window", (gpointer) & priv->ps_customize_printer_list_window)) {
    g_module_close (gm);
    g_warning ("customize_printer_list_window symbol not found");

    return FALSE;
  }
  if (!g_module_symbol (gm, "customize_printer_queue_window", (gpointer) & priv->ps_customize_printer_queue_window)) {
    g_module_close (gm);
    g_warning ("customize_printer_queue_window symbol not found");

    return FALSE;
  }

  DBG ("printing system module loaded");
  return TRUE;
}

GType
printing_system_get_type ()
{
  static GType type = 0;

  if (type == 0) {
    static const GTypeInfo our_info = {
      sizeof (PrintingSystemClass),
      NULL,
      NULL,
      (GClassInitFunc) printing_system_class_init,
      NULL,
      NULL,
      sizeof (PrintingSystem),
      0,
      (GInstanceInitFunc) printing_system_init,
    };

    type = g_type_register_static (G_TYPE_OBJECT, "PrintingSystem", &our_info, 0);
  }

  return type;
}

static void
printing_system_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
{
  PrintingSystemPrivate *priv = PRINTING_SYSTEM_GET_PRIVATE (object);

  switch (prop_id) {
  case PROP_PS_PATH:
    g_value_set_string (value, priv->path);
    break;

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

static void
printing_system_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
{
  PrintingSystemPrivate *priv = PRINTING_SYSTEM_GET_PRIVATE (object);

  switch (prop_id) {
  case PROP_PS_PATH:
    g_free (priv->path);
    priv->path = g_strdup (g_value_get_string (value));
    priv->plugin_loaded = load_printing_system (PRINTING_SYSTEM (object));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    break;
  }
}

static void
printing_system_class_init (PrintingSystemClass * klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GParamSpec *param_spec;

  g_type_class_add_private (klass, sizeof (PrintingSystemPrivate));

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = printing_system_finalize;
  object_class->get_property = printing_system_get_property;
  object_class->set_property = printing_system_set_property;

  param_spec = g_param_spec_string ("ps-path", "printing system module path", "set the printing system path",
                                    NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
  g_object_class_install_property (object_class, PROP_PS_PATH, param_spec);
}

static void
printing_system_init (PrintingSystem * ps)
{
  ps->priv = PRINTING_SYSTEM_GET_PRIVATE (ps);

  ps->priv->plugin_loaded = FALSE;
}

static void
printing_system_finalize (GObject * object)
{
  PrintingSystem *cobj;
  cobj = PRINTING_SYSTEM (object);

  g_free (cobj->priv->path);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

/**********/
/* public */
/**********/
GList *
printing_system_get_printers (PrintingSystem * ps)
{
  if (ps == NULL)
    return NULL;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_get_printers ();
}

Printer *
printing_system_get_default_printer (PrintingSystem * ps)
{
  if (ps == NULL)
    return NULL;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_get_default_printer ();
}

gint
printing_system_get_printer_state (PrintingSystem * ps, const gchar * printer)
{
  if (ps == NULL)
    return PRINTER_STATE_UNKNOWN;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_get_printer_state (printer);
}

gint
printing_system_get_jobs_count (PrintingSystem * ps, const gchar * printer)
{
  if (ps == NULL)
    return 0;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_get_printer_jobs_count (printer);
}

gboolean
printing_system_remove_job (PrintingSystem * ps, const gchar * printer, gint id)
{
  if (ps == NULL)
    return FALSE;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_remove_job (printer, id);
}

GList *
printing_system_get_jobs (PrintingSystem * ps, const gchar * printer)
{
  if (ps == NULL)
    return NULL;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_get_jobs (printer);
}

gboolean
printing_system_print_file (PrintingSystem * ps, const gchar * printer, const gchar * original_name, const gchar * file, gboolean remove_file)
{
  if (ps == NULL)
    return FALSE;

  return PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_print_file (printer, original_name, file, remove_file);
}

void
printing_system_customize_printer_list_window (PrintingSystem * ps, PrinterListWindow * win)
{
  if (ps == NULL)
    return;

  PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_customize_printer_list_window (win);
}

void
printing_system_customize_printer_queue_window (PrintingSystem * ps, PrinterQueueWindow * win)
{
  if (ps == NULL)
    return;

  PRINTING_SYSTEM_GET_PRIVATE (ps)->ps_customize_printer_queue_window (win);
}

PrintingSystem *
printing_system_new (const gchar * path)
{
  PrintingSystem *obj = NULL;

  g_return_val_if_fail (path, NULL);

  obj = PRINTING_SYSTEM (g_object_new (PRINTING_SYSTEM_TYPE, "ps-path", path, NULL));
  if (!obj->priv->plugin_loaded) {
    g_object_unref (G_OBJECT (obj));
    return NULL;
  }

  return obj;
}
