/*
 * Copyright (c) 2003 Benedikt Meurer (benedikt.meurer@unix-ag.uni-siegen.de)
 *               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 /* !HAVE_CONFIG_H */

#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <libxfce4util/libxfce4util.h>
#include <libxfcegui4/libxfcegui4.h>

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

#define PRINTER_QUEUE_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
                                              PRINTER_QUEUE_WINDOW_TYPE, PrinterQueueWindowPrivate))

struct _PrinterQueueWindowPrivate
{
  PrintingSystem *ps;
  gchar *printer;

  GtkIconTheme *icontheme;
  GtkUIManager *ui_manager;
  GtkActionGroup *action_group;

  GtkWidget *treeview;

  guint timeout_id;
};

/* Prototypes */
static void printer_queue_window_class_init (PrinterQueueWindowClass * klass);
static void printer_queue_window_init (PrinterQueueWindow * obj);

static void action_close_cb (GtkAction * action, PrinterQueueWindow * win);
static void action_remove_job_cb (GtkAction * action, PrinterQueueWindow * win);
static void action_refresh_cb (GtkAction * action, PrinterQueueWindow * win);

static void icon_theme_changed_cb (GtkIconTheme * icon_theme, PrinterQueueWindow * win);
static void tree_selection_changed_cb (GtkTreeSelection * selection, PrinterQueueWindow * win);
static gboolean tree_button_pressed_cb (GtkWidget * widget, GdkEventButton * event, PrinterQueueWindow * win);
static gboolean timeout_fct (PrinterQueueWindow * win);

/* globals */
static GtkWindowClass *parent_class = NULL;
static GtkActionEntry action_entries[] = {
  {"printer-menu", NULL, N_("Printer"), NULL,},
  {"close", GTK_STOCK_CLOSE, N_("Close"), NULL, N_("Close queue window"), G_CALLBACK (action_close_cb),},
  {"jobs-menu", NULL, N_("Jobs"), NULL,},
  {"remove-job", GTK_STOCK_REMOVE, N_("Remove job"), "Delete", N_("Remove the selected job from the queue"),
   G_CALLBACK (action_remove_job_cb),},
  {"refresh", GTK_STOCK_REFRESH, N_("Refresh"), "F5", N_("Refresh queue"), G_CALLBACK (action_refresh_cb),},
};

/*********/
/* class */
/*********/
GType
printer_queue_window_get_type ()
{
  static GType type = 0;

  if (type == 0) {
    static const GTypeInfo our_info = {
      sizeof (PrinterQueueWindowClass),
      NULL,
      NULL,
      (GClassInitFunc) printer_queue_window_class_init,
      NULL,
      NULL,
      sizeof (PrinterQueueWindow),
      0,
      (GInstanceInitFunc) printer_queue_window_init,
    };

    type = g_type_register_static (GTK_TYPE_WINDOW, "PrinterQueueWindow", &our_info, 0);
  }

  return type;
}

static void
printer_queue_window_init (PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv;
  GError *error = NULL;
  GtkAccelGroup *accel_group;
  GtkWidget *vbox;
  GtkWidget *menubar;
  GtkListStore *store;
  GtkWidget *scrollwin;
  GtkTreeSelection *selection;
  GtkCellRenderer *cell;
  GtkTreeViewColumn *column;

  priv = win->priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);

  priv->icontheme = gtk_icon_theme_get_default ();
  g_signal_connect (G_OBJECT (priv->icontheme), "changed", G_CALLBACK (icon_theme_changed_cb), win);

  gtk_window_set_title (GTK_WINDOW (win), _("Queue Manager"));
  gtk_window_set_icon_name (GTK_WINDOW (win), "printer");
  gtk_window_set_default_size (GTK_WINDOW (win), 450, 150);
  gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_NONE);

  /* ui manager */
  priv->action_group = gtk_action_group_new ("printer-queue-default");
  gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
  gtk_action_group_add_actions (priv->action_group, action_entries, G_N_ELEMENTS (action_entries), GTK_WIDGET (win));

  priv->ui_manager = gtk_ui_manager_new ();
  gtk_ui_manager_insert_action_group (priv->ui_manager, priv->action_group, 0);

  if (gtk_ui_manager_add_ui_from_string (priv->ui_manager,
                                         "<ui>"
                                         "<menubar name=\"main-menu\">"
                                         " <menu action=\"printer-menu\">"
                                         "   <placeholder name=\"printer-menu-additions\"/>"
                                         "   <separator/>"
                                         "   <menuitem action=\"close\"/>"
                                         " </menu>"
                                         " <menu action=\"jobs-menu\">"
                                         "   <placeholder name=\"jobs-menu-additions\"/>"
                                         "   <separator/>"
                                         "   <menuitem action=\"remove-job\"/>"
                                         "   <menuitem action=\"refresh\"/>"
                                         " </menu>"
                                         "</menubar>"
                                         "<popup action=\"popup-menu\">"
                                         "   <menuitem action=\"remove-job\"/>"
                                         "   <placeholder name=\"popup-menu-additions\"/>"
                                         "</popup>" "</ui>", -1, &error) == 0) {
    g_warning ("Unable to build the user interface correctly : %s", error->message);
    g_error_free (error);
  }

  gtk_ui_manager_ensure_update (priv->ui_manager);

  accel_group = gtk_ui_manager_get_accel_group (priv->ui_manager);
  gtk_window_add_accel_group (GTK_WINDOW (win), accel_group);

  /* contents */
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (win), vbox);

  /* menubar */
  menubar = gtk_ui_manager_get_widget (priv->ui_manager, "/main-menu");
  if (G_LIKELY (menubar != NULL)) {
    gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
    gtk_widget_show (menubar);
  }

  /* Add treeview */
  store = gtk_list_store_new (JOBS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
                              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
                              G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);

  scrollwin = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start (GTK_BOX (vbox), scrollwin, TRUE, TRUE, 0);

  priv->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
  gtk_container_add (GTK_CONTAINER (scrollwin), priv->treeview);
  g_signal_connect (G_OBJECT (priv->treeview), "button-press-event", G_CALLBACK (tree_button_pressed_cb), win);

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
  g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (tree_selection_changed_cb), win);

  /* Columns in treeview */
  cell = gtk_cell_renderer_pixbuf_new ();
  column = gtk_tree_view_column_new ();

  gtk_tree_view_column_pack_start (column, cell, FALSE);
  gtk_tree_view_column_set_attributes (column, cell, "pixbuf", JOBS_ICON_COLUMN, NULL);
  g_object_set (cell, "xalign", 0.0, "ypad", 0, NULL);

  cell = gtk_cell_renderer_text_new ();

  gtk_tree_view_column_pack_start (column, cell, TRUE);
  gtk_tree_view_column_set_attributes (column, cell, "markup", JOBS_NAME_COLUMN, NULL);
  g_object_set (cell, "ypad", 0, "yalign", 0.5, NULL);
  gtk_tree_view_column_set_title (column, _("Name"));
  gtk_tree_view_append_column (GTK_TREE_VIEW (priv->treeview), column);

  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_ID_COLUMN, _("ID"), gtk_cell_renderer_text_new (),
                                               "text", JOBS_ID_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_USER_COLUMN, _("User"), gtk_cell_renderer_text_new (),
                                               "text", JOBS_USER_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_STATE_COLUMN, _("State"), gtk_cell_renderer_text_new (),
                                               "text", JOBS_STATE_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_SIZE_COLUMN, _("Size"), gtk_cell_renderer_text_new (),
                                               "text", JOBS_SIZE_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_PRIORITY_COLUMN, _("Priority"), gtk_cell_renderer_text_new (),
                                               "text", JOBS_PRIORITY_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview),
                                               JOBS_CREATION_TIME_COLUMN, _("Creation time"),
                                               gtk_cell_renderer_text_new (), "text", JOBS_CREATION_TIME_COLUMN, NULL);
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->treeview), JOBS_PROCESSING_TIME_COLUMN,
                                               _("Processing time"), gtk_cell_renderer_text_new (), "text",
                                               JOBS_PROCESSING_TIME_COLUMN, NULL);

  gtk_widget_show_all (vbox);
}

static void
printer_queue_window_finalize (GObject * object)
{
  PrinterQueueWindow *cobj;
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (object);

  cobj = PRINTER_QUEUE_WINDOW (object);

  if (priv->timeout_id > 0)
    g_source_remove (priv->timeout_id);

  g_free (priv->printer);

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

static void
printer_queue_window_class_init (PrinterQueueWindowClass * klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  g_type_class_add_private (klass, sizeof (PrinterQueueWindowPrivate));

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = printer_queue_window_finalize;
}

/*****************************/
/* Manage icon theme changes */
/*****************************/
static gboolean
icon_theme_update_foreach_func (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter * iter, PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GdkPixbuf *icon;
  gint x, y;

  gtk_tree_model_get (model, iter, JOBS_ICON_COLUMN, &icon, -1);

  if (icon)
    g_object_unref (icon);

  if (gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &x, &y))
    icon = gtk_icon_theme_load_icon (priv->icontheme, "gnome-mime-application-postscript", x, 0, NULL);
  else
    icon = gtk_icon_theme_load_icon (priv->icontheme, "gnome-mime-application-postscript", 24, 0, NULL);

  gtk_list_store_set (GTK_LIST_STORE (model), iter, JOBS_ICON_COLUMN, icon, -1);
  g_object_unref (icon);

  return FALSE;
}

static void
icon_theme_changed_cb (GtkIconTheme * icon_theme, PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkTreeModel *model;

  model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));

  if (model)
    gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) & icon_theme_update_foreach_func, win);
}

static gboolean
tree_button_pressed_cb (GtkWidget * widget, GdkEventButton * event, PrinterQueueWindow * win)
{
  /* Right click draws the context menu */
  if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) {
    PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
    GtkTreePath *path;

    if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->treeview), event->x, event->y, &path, NULL, NULL, NULL)) {
      GtkTreeSelection *selection;
      GtkWidget *menu;

      selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
      gtk_tree_selection_unselect_all (selection);
      gtk_tree_selection_select_path (selection, path);

      menu = gtk_ui_manager_get_widget (priv->ui_manager, "/popup-menu");
      if (G_LIKELY (menu != NULL)) {
        gtk_widget_show (menu);
        gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time ());

        return TRUE;
      }
    }
  }

  return FALSE;
}

static void
tree_selection_changed_cb (GtkTreeSelection * selection, PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkAction *action = NULL;
  GtkTreeModel *model;
  GtkTreeIter iter;

  gtk_action_group_get_action (priv->action_group, "remove-job");
  if (G_LIKELY (action)) {
    if (gtk_tree_selection_get_selected (selection, &model, &iter))
      gtk_action_set_sensitive (action, TRUE);
    else
      gtk_action_set_sensitive (action, FALSE);
  }
}

static gboolean
timeout_fct (PrinterQueueWindow * win)
{
  action_refresh_cb (NULL, win);

  return TRUE;
}

/*---------*/
/* actions */
/*---------*/
static void
action_close_cb (GtkAction * action, PrinterQueueWindow * win)
{
  gtk_widget_destroy (GTK_WIDGET (win));
}

static void
action_remove_job_cb (GtkAction * action, PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreeSelection *selection;

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));

  if (selection && gtk_tree_selection_get_selected (selection, &model, &iter)) {
    gint response = GTK_RESPONSE_NONE;
    gchar *string_id;
    gchar *string_title;
    gchar *dialog_message;

    gtk_tree_model_get (model, &iter, JOBS_NAME_COLUMN, &string_title, JOBS_ID_COLUMN, &string_id, -1);

    dialog_message = g_strdup_printf ("%s%s (%s) ?", _("Are you sure you want to remove the job "),
                                      string_id, string_title);

    response = xfce_message_dialog (GTK_WINDOW (win), _("Remove job"),
                                    GTK_STOCK_DIALOG_QUESTION,
                                    dialog_message, NULL,
                                    XFCE_CUSTOM_STOCK_BUTTON, _("Don't remove job"), GTK_STOCK_CANCEL,
                                    GTK_RESPONSE_CANCEL, XFCE_CUSTOM_STOCK_BUTTON, _("Remove job"), GTK_STOCK_REMOVE,
                                    GTK_RESPONSE_OK, NULL);
    if (response == GTK_RESPONSE_OK) {
      if (printing_system_remove_job (priv->ps, priv->printer, atoi (string_id)))
        action_refresh_cb (NULL, win);
      else
        xfce_err (_("An error occurred while removing job !"));
    }

    g_free (string_id);
    g_free (string_title);
    g_free (dialog_message);
  }
}

static void
action_refresh_cb (GtkAction * action, PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkTreeModel *model;
  GtkTreeSelection *selection = NULL;
  gchar *selected_id = NULL;
  GtkTreeIter iter;

  GList *jobs = NULL, *job = NULL;

  /* Remember selected ID */
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));

  if (selection && gtk_tree_selection_get_selected (selection, &model, &iter))
    gtk_tree_model_get (model, &iter, JOBS_ID_COLUMN, &selected_id, -1);
  else
    model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview));

  /* Empty the list */
  gtk_list_store_clear (GTK_LIST_STORE (model));

  /* Obtain job list from cups server */
  jobs = printing_system_get_jobs (priv->ps, priv->printer);

  /* Show job list */
  job = g_list_first (jobs);
  while (job) {
    gchar *string_id = NULL, *string_state = NULL, *string_size = NULL, *string_priority = NULL;
    GdkPixbuf *icon = NULL;
    gint x, y;

    Job *job_data = (Job *) job->data;

    if (gtk_icon_size_lookup (GTK_ICON_SIZE_LARGE_TOOLBAR, &x, &y))
      icon = gtk_icon_theme_load_icon (priv->icontheme, "gnome-mime-application-postscript", x, 0, NULL);
    else
      icon = gtk_icon_theme_load_icon (priv->icontheme, "gnome-mime-application-postscript", 24, 0, NULL);

    string_id = g_strdup_printf ("%u", job_data->id);
    switch (job_data->state) {
    case JOB_STATE_PENDING:
      string_state = g_strdup (_("pending"));
      break;
    case JOB_STATE_PRINTING:
      string_state = g_strdup (_("printing"));
      break;
    }
    string_size = g_strdup_printf ("%u %s", job_data->size, _("Kb"));
    string_priority = g_strdup_printf ("%u", job_data->priority);

    gtk_list_store_append (GTK_LIST_STORE (model), &iter);
    gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                        JOBS_ICON_COLUMN, icon,
                        JOBS_NAME_COLUMN, job_data->name ? job_data->name : "",
                        JOBS_ID_COLUMN, string_id,
                        JOBS_USER_COLUMN, job_data->user ? job_data->user : "",
                        JOBS_STATE_COLUMN, string_state,
                        JOBS_SIZE_COLUMN, string_size,
                        JOBS_PRIORITY_COLUMN, string_priority,
                        JOBS_CREATION_TIME_COLUMN, job_data->creation_time ? job_data->creation_time : "",
                        JOBS_PROCESSING_TIME_COLUMN, job_data->processing_time ? job_data->processing_time : "", -1);

    g_free (string_id);
    g_free (string_state);
    g_free (string_size);
    g_object_unref (G_OBJECT (icon));
    job = g_list_next (job);
  }
  jobs_free (jobs);

  /* Reselect previously selected job if still available */
  if (selected_id && gtk_tree_model_get_iter_first (model, &iter)) {
    gchar *current_id;

    gtk_tree_model_get (model, &iter, JOBS_ID_COLUMN, &current_id, -1);
    if (strcmp (current_id, selected_id) == 0) {
      gtk_tree_selection_select_iter (selection, &iter);
      g_free (current_id);
    }
    else {
      g_free (current_id);
      while (gtk_tree_model_iter_next (model, &iter)) {
        gtk_tree_model_get (model, &iter, JOBS_ID_COLUMN, &current_id, -1);
        if (strcmp (current_id, selected_id) == 0) {
          gtk_tree_selection_select_iter (selection, &iter);
          g_free (current_id);
          break;
        }
        g_free (current_id);
      }
    }
  }

  g_free (selected_id);
}

/********************/
/* public functions */
/********************/
GtkUIManager *
printer_queue_window_get_ui_manager (PrinterQueueWindow * win)
{
  return PRINTER_QUEUE_WINDOW_GET_PRIVATE (win)->ui_manager;
}

void
printer_queue_window_hide_column (PrinterQueueWindow * win, PrinterQueueWindowColumn column)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkTreeViewColumn *tree_column;

  tree_column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->treeview), column);
  gtk_tree_view_column_set_visible (tree_column, FALSE);
}

GtkWidget *
printer_queue_window_new (PrintingSystem * ps, const gchar * printer)
{
  GObject *obj;
  PrinterQueueWindowPrivate *priv;
  GList *printers = NULL;
  gchar *title = NULL;

  g_return_val_if_fail (ps, NULL);

  obj = g_object_new (PRINTER_QUEUE_WINDOW_TYPE, NULL);

  priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (obj);
  priv->ps = ps;
  priv->printer = g_strdup (printer);

  /* set title */
  printers = printing_system_get_printers (ps);
  title = g_strdup_printf ("%s - %s", _("Queue Manager"), (printer_lookup_byname (printers, printer))->alias);
  gtk_window_set_title (GTK_WINDOW (obj), title);
  g_free (title);
  printers_free (printers);

  /* load jobs in tree */
  action_refresh_cb (NULL, PRINTER_QUEUE_WINDOW (obj));

  /* refresh job list every 5 sec */
  priv->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 5000, (GSourceFunc) timeout_fct,
                                         PRINTER_QUEUE_WINDOW (obj), NULL);

  /* customize the dialog for the printing system */
  printing_system_customize_printer_queue_window (ps, PRINTER_QUEUE_WINDOW (obj));

  return GTK_WIDGET (obj);
}

guint
printer_queue_window_get_selected_job (PrinterQueueWindow * win)
{
  PrinterQueueWindowPrivate *priv = PRINTER_QUEUE_WINDOW_GET_PRIVATE (win);
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter;
  guint job = 0;

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
  if (selection && gtk_tree_selection_get_selected (selection, &model, &iter))
    gtk_tree_model_get (model, &iter, JOBS_ID_COLUMN, &job, -1);

  return job;
}
