/*
 * Copyright (C) 2010 Intel, Inc
 *
 * 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 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.
 *
 * Author: Thomas Wood <thomas.wood@intel.com>
 *
 */

#include <config.h>

#include "cc-background-panel.h"
#include "bg-wallpapers-source.h"
#include "bg-pictures-source.h"
#include "bg-colors-source.h"

#ifdef HAVE_LIBSOCIALWEB
#include "bg-flickr-source.h"
#endif

#include "gnome-wp-xml.h"
#include "gnome-wp-item.h"

#include <string.h>
#include <glib/gi18n-lib.h>

enum {
  COL_SOURCE_NAME,
  COL_SOURCE_TYPE,
  COL_SOURCE_READONLY,
  COL_SOURCE,
  NUM_COLS
};

G_DEFINE_DYNAMIC_TYPE (CcBackgroundPanel, cc_background_panel, CC_TYPE_PANEL)

#define BACKGROUND_PANEL_PRIVATE(o) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_BACKGROUND_PANEL, CcBackgroundPanelPrivate))

struct _CcBackgroundPanelPrivate
{
  GtkBuilder *builder;

  BgWallpapersSource *wallpapers_source;
  BgPicturesSource *pictures_source;
  BgColorsSource *colors_source;

#ifdef HAVE_LIBSOCIALWEB
  BgFlickrSource *flickr_source;
#endif

  GSettings *settings;

  GnomeDesktopThumbnailFactory *thumb_factory;

  GnomeWPItem *current_background;
  gboolean current_source_readonly;

  GCancellable *copy_cancellable;

  GtkWidget *spinner;

  GdkPixbuf *display_base;
  GdkPixbuf *display_overlay;
};

enum
{
  SOURCE_WALLPAPERS,
  SOURCE_PICTURES,
  SOURCE_COLORS,
#ifdef HAVE_LIBSOCIALWEB
  SOURCE_FLICKR
#endif
};


#define WID(y) (GtkWidget *) gtk_builder_get_object (priv->builder, y)

static void
cc_background_panel_get_property (GObject    *object,
                                  guint       property_id,
                                  GValue     *value,
                                  GParamSpec *pspec)
{
  switch (property_id)
    {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
}

static void
cc_background_panel_set_property (GObject      *object,
                                  guint         property_id,
                                  const GValue *value,
                                  GParamSpec   *pspec)
{
  switch (property_id)
    {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    }
}

static void
cc_background_panel_dispose (GObject *object)
{
  CcBackgroundPanelPrivate *priv = CC_BACKGROUND_PANEL (object)->priv;

  if (priv->builder)
    {
      g_object_unref (priv->builder);
      priv->builder = NULL;

      /* destroying the builder object will also destroy the spinner */
      priv->spinner = NULL;
    }

  if (priv->wallpapers_source)
    {
      g_object_unref (priv->wallpapers_source);
      priv->wallpapers_source = NULL;
    }

  if (priv->pictures_source)
    {
      g_object_unref (priv->pictures_source);
      priv->pictures_source = NULL;
    }

  if (priv->colors_source)
    {
      g_object_unref (priv->colors_source);
      priv->colors_source = NULL;
    }
#ifdef HAVE_LIBSOCIALWEB
  if (priv->flickr_source)
    {
      g_object_unref (priv->flickr_source);
      priv->flickr_source = NULL;
    }
#endif

  if (priv->settings)
    {
      g_object_unref (priv->settings);
      priv->settings = NULL;
    }

  if (priv->copy_cancellable)
    {
      /* cancel any copy operation */
      g_cancellable_cancel (priv->copy_cancellable);

      g_object_unref (priv->copy_cancellable);
      priv->copy_cancellable = NULL;
    }

  if (priv->thumb_factory)
    {
      g_object_unref (priv->thumb_factory);
      priv->thumb_factory = NULL;
    }

  if (priv->display_base)
    {
      g_object_unref (priv->display_base);
      priv->display_base = NULL;
    }

  if (priv->display_overlay)
    {
      g_object_unref (priv->display_overlay);
      priv->display_overlay = NULL;
    }

  G_OBJECT_CLASS (cc_background_panel_parent_class)->dispose (object);
}

static void
cc_background_panel_finalize (GObject *object)
{
  CcBackgroundPanelPrivate *priv = CC_BACKGROUND_PANEL (object)->priv;

  if (priv->current_background)
    {
      gnome_wp_item_free (priv->current_background);
      priv->current_background = NULL;
    }

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

static void
cc_background_panel_class_init (CcBackgroundPanelClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  g_type_class_add_private (klass, sizeof (CcBackgroundPanelPrivate));

  object_class->get_property = cc_background_panel_get_property;
  object_class->set_property = cc_background_panel_set_property;
  object_class->dispose = cc_background_panel_dispose;
  object_class->finalize = cc_background_panel_finalize;
}

static void
cc_background_panel_class_finalize (CcBackgroundPanelClass *klass)
{

}

static void
source_changed_cb (GtkComboBox              *combo,
                   CcBackgroundPanelPrivate *priv)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  GtkIconView *view;
  guint type;
  BgSource *source;

  gtk_combo_box_get_active_iter (combo, &iter);
  model = gtk_combo_box_get_model (combo);
  gtk_tree_model_get (model, &iter,
                      COL_SOURCE_TYPE, &type,
                      COL_SOURCE, &source, -1);

  view = (GtkIconView *) gtk_builder_get_object (priv->builder,
                                                 "backgrounds-iconview");

  gtk_icon_view_set_model (view,
                           GTK_TREE_MODEL (bg_source_get_liststore (source)));
}

static void
copy_finished_cb (GObject      *source_object,
                  GAsyncResult *result,
                  gpointer      pointer)
{
  GError *err = NULL;
  CcBackgroundPanel *panel = (CcBackgroundPanel *) pointer;
  CcBackgroundPanelPrivate *priv = panel->priv;

  if (!g_file_copy_finish (G_FILE (source_object), result, &err))
    {
      if (err->code != G_IO_ERROR_CANCELLED)
        g_warning ("Failed to copy image to cache location: %s", err->message);

      g_error_free (err);
    }

  /* the panel may have been destroyed before the callback is run, so be sure
   * to check the widgets are not NULL */

  if (priv->spinner)
    {
      gtk_widget_destroy (GTK_WIDGET (priv->spinner));
      priv->spinner = NULL;
    }

  if (priv->builder)
    gtk_widget_queue_draw (WID ("preview-area"));

  /* remove the reference taken when the copy was set up */
  g_object_unref (panel);
}

static void
update_preview (CcBackgroundPanelPrivate *priv,
                GnomeWPItem              *item,
                gboolean                  redraw_preview)
{
  gchar *markup;

  if (item && priv->current_background)
    {
      if (priv->current_background->pcolor)
        gdk_color_free (priv->current_background->pcolor);
      priv->current_background->pcolor = gdk_color_copy (item->pcolor);

      if (priv->current_background->scolor)
        gdk_color_free (priv->current_background->scolor);
      priv->current_background->scolor = gdk_color_copy (item->scolor);



      g_free (priv->current_background->filename);
      priv->current_background->filename = g_strdup (item->filename);

      g_free (priv->current_background->name);
      priv->current_background->name = g_strdup (item->name);

      priv->current_background->options = item->options;
    }


  if (!priv->current_source_readonly)
    gtk_widget_show (WID ("edit-hbox"));
  else
    gtk_widget_hide (WID ("edit-hbox"));

  if (priv->current_background)
    {
      markup = g_strdup_printf ("<b>%s</b>", priv->current_background->name);
      gtk_label_set_markup (GTK_LABEL (WID ("background-label")), markup);
      g_free (markup);

      gtk_color_button_set_color (GTK_COLOR_BUTTON (WID ("style-color")),
                                  priv->current_background->pcolor);

      gtk_combo_box_set_active (GTK_COMBO_BOX (WID ("style-combobox")),
                                priv->current_background->options);
    }

  if (redraw_preview)
    gtk_widget_queue_draw (WID ("preview-area"));
}

static void
backgrounds_changed_cb (GtkIconView       *icon_view,
                        CcBackgroundPanel *panel)
{
  GtkTreeIter iter;
  GList *list;
  GtkTreeModel *model;
  GnomeWPItem *item;
  gchar *pcolor, *scolor;
  CcBackgroundPanelPrivate *priv = panel->priv;
  gboolean draw_preview = TRUE;

  list = gtk_icon_view_get_selected_items (icon_view);

  if (!list)
    return;

  /* check if the current source is read only, i.e. the image placement and
   * color is predefined */
  model = gtk_combo_box_get_model (GTK_COMBO_BOX (WID ("sources-combobox")));
  gtk_combo_box_get_active_iter (GTK_COMBO_BOX (WID ("sources-combobox")),
                                 &iter);
  gtk_tree_model_get (model, &iter, COL_SOURCE_READONLY, &priv->current_source_readonly, -1);


  model = gtk_icon_view_get_model (icon_view);

  gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) list->data);

  g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
  g_list_free (list);

  gtk_tree_model_get (model, &iter, 1, &item, -1);

  /* Do all changes in one 'transaction' */
  g_settings_delay (priv->settings);

  if (!g_strcmp0 (item->filename, "(none)"))
    {
      g_settings_set_enum (priv->settings, WP_OPTIONS_KEY, G_DESKTOP_BACKGROUND_STYLE_NONE);
      g_settings_set_string (priv->settings, WP_FILE_KEY, "");
    }
  else if (item->source_url)
    {
      GFile *source, *dest;
      gchar *cache_path;
      GdkPixbuf *pixbuf;

      cache_path = g_build_filename (g_get_user_cache_dir (),
                                     "gnome-background",
                                     NULL);

      source = g_file_new_for_uri (item->source_url);
      dest = g_file_new_for_path (cache_path);

      /* create a blank image to use until the source image is ready */
      pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
      gdk_pixbuf_fill (pixbuf, 0x00000000);
      gdk_pixbuf_save (pixbuf, cache_path, "png", NULL, NULL);
      g_object_unref (pixbuf);

      if (priv->copy_cancellable)
        {
          g_cancellable_cancel (priv->copy_cancellable);
          g_cancellable_reset (priv->copy_cancellable);

        }

      if (priv->spinner)
        {
          gtk_widget_destroy (GTK_WIDGET (priv->spinner));
          priv->spinner = NULL;
        }

      /* create a spinner while the file downloads */
      priv->spinner = gtk_spinner_new ();
      gtk_spinner_start (GTK_SPINNER (priv->spinner));
      gtk_box_pack_start (GTK_BOX (WID ("bottom-hbox")), priv->spinner, FALSE,
                          FALSE, 6);
      gtk_widget_show (priv->spinner);

      /* reference the panel in case it is removed before the copy is
       * finished */
      g_object_ref (panel);
      g_file_copy_async (source, dest, G_FILE_COPY_OVERWRITE,
                         G_PRIORITY_DEFAULT, priv->copy_cancellable,
                         NULL, NULL,
                         copy_finished_cb, panel);

      g_settings_set_string (priv->settings, WP_FILE_KEY, cache_path);
      g_settings_set_enum (priv->settings, WP_OPTIONS_KEY, item->options);
      g_free (item->filename);
      item->filename = cache_path;

      /* delay the updated drawing of the preview until the copy finishes */
      draw_preview = FALSE;
    }
  else
    {
       gchar *uri;

       if (g_utf8_validate (item->filename, -1, NULL))
         uri = g_strdup (item->filename);
       else
         uri = g_filename_to_utf8 (item->filename, -1, NULL, NULL, NULL);

       if (uri == NULL)
         {
           g_warning ("Failed to convert filename to UTF-8: %s",
                      item->filename);
         }
       else
         {
	   g_settings_set_string (priv->settings, WP_FILE_KEY, uri);
           g_free (uri);
         }

       g_settings_set_enum (priv->settings, WP_OPTIONS_KEY, item->options);
    }

  g_settings_set_enum (priv->settings, WP_SHADING_KEY, item->shade_type);

  pcolor = gdk_color_to_string (item->pcolor);
  scolor = gdk_color_to_string (item->scolor);
  g_settings_set_string (priv->settings, WP_PCOLOR_KEY, pcolor);
  g_settings_set_string (priv->settings, WP_SCOLOR_KEY, scolor);
  g_free (pcolor);
  g_free (scolor);

  /* Apply all changes */
  g_settings_apply (priv->settings);

  /* update the preview information */
  update_preview (priv, item, draw_preview);
}

static gboolean
preview_draw_cb (GtkWidget         *widget,
                 cairo_t           *cr,
                 CcBackgroundPanel *panel)
{
  GtkAllocation allocation;
  CcBackgroundPanelPrivate *priv = panel->priv;
  GdkPixbuf *pixbuf = NULL;
  const gint preview_width = 416;
  const gint preview_height = 248;
  const gint preview_x = 45;
  const gint preview_y = 84;
  GdkPixbuf *preview, *temp;
  gint size;

  gtk_widget_get_allocation (widget, &allocation);

  if (priv->current_background)
    {
      pixbuf = gnome_wp_item_get_thumbnail (priv->current_background,
                                            priv->thumb_factory,
                                            preview_width,
                                            preview_height);
    }

  if (!priv->display_base)
    return FALSE;


  preview = gdk_pixbuf_copy (priv->display_base);

  if (pixbuf)
    {
      gdk_pixbuf_composite (pixbuf, preview,
                            preview_x, preview_y,
                            preview_width, preview_height,
                            preview_x, preview_y, 1, 1,
                            GDK_INTERP_BILINEAR, 255);

      g_object_unref (pixbuf);
    }


  if (priv->display_overlay)
    {
      gdk_pixbuf_composite (priv->display_overlay, preview,
                            0, 0, 512, 512,
                            0, 0, 1, 1,
                            GDK_INTERP_BILINEAR, 255);
    }


  if (allocation.width < allocation.height)
    size = allocation.width;
  else
    size = allocation.height;

  temp = gdk_pixbuf_scale_simple (preview, size, size, GDK_INTERP_BILINEAR);

  gdk_cairo_set_source_pixbuf (cr,
                               temp,
                               allocation.width / 2 - (size / 2),
                               allocation.height / 2 - (size / 2));
  cairo_paint (cr);

  g_object_unref (temp);
  g_object_unref (preview);

  return TRUE;
}

static void
style_changed_cb (GtkComboBox       *box,
                  CcBackgroundPanel *panel)
{
  CcBackgroundPanelPrivate *priv = panel->priv;
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint value;

  if (!gtk_combo_box_get_active_iter (box, &iter))
    {
      return;
    }

  model = gtk_combo_box_get_model (box);

  gtk_tree_model_get (model, &iter, 1, &value, -1);

  g_settings_set_enum (priv->settings, WP_OPTIONS_KEY, value);

  if (priv->current_background)
    priv->current_background->options = gtk_combo_box_get_active (box);

  update_preview (priv, NULL, TRUE);
}

static void
color_changed_cb (GtkColorButton    *button,
                  CcBackgroundPanel *panel)
{
  CcBackgroundPanelPrivate *priv = panel->priv;
  GdkColor color;
  gchar *value;

  gtk_color_button_get_color (button, &color);

  if (priv->current_background)
    *priv->current_background->pcolor = color;

  value = gdk_color_to_string (&color);

  g_settings_set_string (priv->settings, WP_PCOLOR_KEY, value);
  g_settings_set_string (priv->settings, WP_SCOLOR_KEY, value);

  g_free (value);

  update_preview (priv, NULL, TRUE);
}

static void
cc_background_panel_init (CcBackgroundPanel *self)
{
  CcBackgroundPanelPrivate *priv;
  gchar *objects[] = { "backgrounds-liststore", "style-liststore",
      "sources-liststore", "background-panel", "sizegroup", NULL };
  GError *err = NULL;
  GtkWidget *widget;
  GtkListStore *store;
  gchar *filename;

  priv = self->priv = BACKGROUND_PANEL_PRIVATE (self);

  priv->builder = gtk_builder_new ();

  gtk_builder_add_objects_from_file (priv->builder,
                                     DATADIR"/background.ui",
                                     objects, &err);

  if (err)
    {
      g_warning ("Could not load ui: %s", err->message);
      g_error_free (err);
      return;
    }

  priv->settings = g_settings_new (WP_PATH_ID);

  store = (GtkListStore*) gtk_builder_get_object (priv->builder,
                                                  "sources-liststore");

  priv->wallpapers_source = bg_wallpapers_source_new ();
  gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
                                     COL_SOURCE_NAME, _("Wallpapers"),
                                     COL_SOURCE_TYPE, SOURCE_WALLPAPERS,
                                     COL_SOURCE_READONLY, TRUE,
                                     COL_SOURCE, priv->wallpapers_source,
                                     -1);

  priv->pictures_source = bg_pictures_source_new ();
  gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
                                     COL_SOURCE_NAME, _("Pictures Folder"),
                                     COL_SOURCE_TYPE, SOURCE_PICTURES,
                                     COL_SOURCE_READONLY, FALSE,
                                     COL_SOURCE, priv->pictures_source,
                                     -1);

  priv->colors_source = bg_colors_source_new ();
  gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
                                     COL_SOURCE_NAME, _("Colors"),
                                     COL_SOURCE_TYPE, SOURCE_COLORS,
                                     COL_SOURCE_READONLY, TRUE,
                                     COL_SOURCE, priv->colors_source,
                                     -1);

#ifdef HAVE_LIBSOCIALWEB
  priv->flickr_source = bg_flickr_source_new ();
  gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
                                     COL_SOURCE_NAME, _("Flickr"),
                                     COL_SOURCE_TYPE, SOURCE_FLICKR,
                                     COL_SOURCE_READONLY, FALSE,
                                     COL_SOURCE, priv->flickr_source,
                                     -1);
#endif


  /* add the top level widget */
  widget = (GtkWidget*)
    gtk_builder_get_object (priv->builder, "background-panel");

  gtk_container_add (GTK_CONTAINER (self), widget);
  gtk_widget_show_all (GTK_WIDGET (self));

  /* connect to source change signal */
  widget = WID ("sources-combobox");
  g_signal_connect (widget, "changed", G_CALLBACK (source_changed_cb), priv);

  /* select first item */
  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);

  /* connect to the background iconview change signal */
  widget = WID ("backgrounds-iconview");
  g_signal_connect (widget, "selection-changed",
                    G_CALLBACK (backgrounds_changed_cb),
                    self);

  /* setup preview area */
  widget = WID ("preview-area");
  g_signal_connect (widget, "draw", G_CALLBACK (preview_draw_cb),
                    self);

  priv->display_base = gdk_pixbuf_new_from_file (DATADIR "/display-base.png",
                                                 NULL);
  priv->display_overlay = gdk_pixbuf_new_from_file (DATADIR
                                                    "/display-overlay.png",
                                                    NULL);

  g_signal_connect (WID ("style-combobox"), "changed",
                    G_CALLBACK (style_changed_cb), self);

  g_signal_connect (WID ("style-color"), "color-set",
                    G_CALLBACK (color_changed_cb), self);

  priv->copy_cancellable = g_cancellable_new ();

  priv->thumb_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);

  /* initalise the current background information from settings */
  filename = g_settings_get_string (priv->settings, WP_FILE_KEY);
  if (!filename || !g_strcmp0 (filename, ""))
    {
      g_free (filename);
      filename = g_strdup ("(none)");
    }

  priv->current_background = g_new0 (GnomeWPItem, 1);
  priv->current_background->filename = filename;
  priv->current_background->name = g_strdup ("");

  gnome_wp_item_update (priv->current_background);
  gnome_wp_item_ensure_gnome_bg (priv->current_background);

  update_preview (priv, NULL, TRUE);
}

void
cc_background_panel_register (GIOModule *module)
{
  cc_background_panel_register_type (G_TYPE_MODULE (module));
  g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT,
                                  CC_TYPE_BACKGROUND_PANEL,
                                  "background", 0);
}

