/*
 *  Copyright © 2002 Marco Pesenti Gritti <mpeseng@tin.it>
 *  Copyright © 2005, 2006 Peter A. Harvey
 *  Copyright © 2006 Christian Persch
 *
 *  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, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include "ephy-bookmark-properties.h"

#include "ephy-bookmarks-ui.h"
#include "ephy-topics-entry.h"
#include "ephy-topics-palette.h"
#include "ephy-node-common.h"
#include "ephy-debug.h"
#include "ephy-shell.h"
#include "ephy-initial-state.h"
#include "ephy-gui.h"
#include "ephy-dnd.h"
#include "ephy-prefs.h"
#include "ephy-settings.h"
#include "ephy-uri-helpers.h"

#include <glib/gi18n.h>
#include <gtk/gtk.h>

#include <string.h>

struct _EphyBookmarkProperties {
  GtkDialog parent_instance;

  /* construct properties */
  EphyBookmarks *bookmarks;
  EphyNode *bookmark;
  gboolean creating;

  /* counters */
  gint duplicate_count;
  gint duplicate_idle;

  /* from UI file */
  GtkGrid           *grid;
  GtkEntry          *title_entry;
  GtkEntry          *adress_entry;
  GtkLabel          *topics_label;
  GtkExpander       *topics_expander;
  GtkTreeView       *topics_tree_view;
  GtkScrolledWindow *topics_scrolled_window;
  GtkLabel          *warning_label;

  /* model */
  EphyTopicsPalette *topics_model;
};

enum {
  PROP_0,
  PROP_BOOKMARKS,
  PROP_BOOKMARK,
  PROP_CREATING,
  LAST_PROP
};

static GParamSpec *obj_properties[LAST_PROP];

G_DEFINE_TYPE (EphyBookmarkProperties, ephy_bookmark_properties, GTK_TYPE_DIALOG)

static gboolean
update_warning (EphyBookmarkProperties *properties)
{
  char *label;

  properties->duplicate_idle = 0;
  properties->duplicate_count = ephy_bookmarks_get_similar
                                  (properties->bookmarks, properties->bookmark, NULL, NULL);

  /* Translators: This string is used when counting bookmarks that
   * are similar to each other */
  label = g_strdup_printf (ngettext ("%d bookmark is similar", "%d bookmarks are similar", properties->duplicate_count), properties->duplicate_count);
  gtk_label_set_text (properties->warning_label, label);
  g_free (label);

  return FALSE;
}

static void
update_warning_idle (EphyBookmarkProperties *properties)
{
  if (properties->duplicate_idle != 0) {
    g_source_remove (properties->duplicate_idle);
  }

  properties->duplicate_idle = g_timeout_add
                                 (500, (GSourceFunc)update_warning, properties);
  g_source_set_name_by_id (properties->duplicate_idle, "[epiphany] update_warning");
}

static void
node_added_cb (EphyNode               *bookmarks,
               EphyNode               *bookmark,
               EphyBookmarkProperties *properties)
{
  update_warning_idle (properties);
}

static void
node_changed_cb (EphyNode               *bookmarks,
                 EphyNode               *bookmark,
                 guint                   property,
                 EphyBookmarkProperties *properties)
{
  if (property == EPHY_NODE_BMK_PROP_LOCATION) {
    update_warning_idle (properties);
  }
}

static void
node_removed_cb (EphyNode               *bookmarks,
                 EphyNode               *bookmark,
                 guint                   index,
                 EphyBookmarkProperties *properties)
{
  update_warning_idle (properties);
}

static void
node_destroy_cb (EphyNode  *bookmark,
                 GtkWidget *dialog)
{
  EPHY_BOOKMARK_PROPERTIES (dialog)->creating = FALSE;
  gtk_widget_destroy (dialog);
}

static void
ephy_bookmark_properties_set_bookmark (EphyBookmarkProperties *properties,
                                       EphyNode               *bookmark)
{
  LOG ("Set bookmark");

  if (properties->bookmark) {
    ephy_node_signal_disconnect_object (properties->bookmark,
                                        EPHY_NODE_DESTROY,
                                        (EphyNodeCallback)node_destroy_cb,
                                        G_OBJECT (properties));
  }

  properties->bookmark = bookmark;

  ephy_node_signal_connect_object (properties->bookmark,
                                   EPHY_NODE_DESTROY,
                                   (EphyNodeCallback)node_destroy_cb,
                                   G_OBJECT (properties));
}

static void
ephy_bookmark_properties_destroy_cb (GtkDialog *dialog,
                                     gpointer   data)
{
  EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (dialog);

  if (properties->creating) {
    ephy_node_unref (properties->bookmark);
    properties->creating = FALSE;
  }

  if (properties->duplicate_idle != 0) {
    g_source_remove (properties->duplicate_idle);
    properties->duplicate_idle = 0;
  }
}

static void
ephy_bookmark_properties_response_cb (GtkDialog *dialog,
                                      int        response_id,
                                      gpointer   data)
{
  EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (dialog);

  switch (response_id) {
    case GTK_RESPONSE_ACCEPT:
      properties->creating = FALSE;
      break;
    default:
      break;
  }

  gtk_widget_destroy (GTK_WIDGET (dialog));
}

static void
update_entry (EphyBookmarkProperties *properties,
              GtkWidget              *entry,
              guint                   prop)
{
  GValue value = { 0, };
  char *text;

  text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
  g_value_init (&value, G_TYPE_STRING);
  g_value_take_string (&value, text);
  ephy_node_set_property (properties->bookmark,
                          prop,
                          &value);
  g_value_unset (&value);
}

static void
update_window_title (EphyBookmarkProperties *properties)
{
  char *title;
  const char *tmp;

  tmp = ephy_node_get_property_string (properties->bookmark,
                                       EPHY_NODE_BMK_PROP_TITLE);

  if (properties->creating)
    title = g_strdup (_("Add Bookmark"));
  else
    title = g_strdup_printf (_("“%s” Properties"), tmp);

  gtk_window_set_title (GTK_WINDOW (properties), title);
  g_free (title);
}

static void
title_entry_changed_cb (GtkWidget              *entry,
                        EphyBookmarkProperties *properties)
{
  update_entry (properties, entry, EPHY_NODE_BMK_PROP_TITLE);
  update_window_title (properties);
}

static void
on_topic_edited (GtkCellRendererText    *renderer,
                 const char             *path_str,
                 const char             *new_text,
                 EphyBookmarkProperties *properties)
{
  if (*new_text != 0) {
    EphyNode *node;
    node = ephy_bookmarks_add_keyword (properties->bookmarks, new_text);
    ephy_bookmarks_set_keyword (properties->bookmarks, node, properties->bookmark);
  } else {
    ephy_topics_palette_update_list (properties->topics_model);
  }
}

static void
on_topic_toggled (GtkCellRendererToggle  *cell_renderer,
                  gchar                  *path,
                  EphyBookmarkProperties *properties)
{
  EphyNode     *topic;
  GtkTreeModel *model;
  GtkTreeIter iter;

  model = gtk_tree_view_get_model (properties->topics_tree_view);

  g_return_if_fail (gtk_tree_model_get_iter_from_string (model, &iter, path));

  gtk_tree_model_get (model, &iter, EPHY_TOPICS_PALETTE_COLUMN_NODE, &topic, -1);

  /* Protect against toggling separators. */
  if (topic == NULL)
    return;

  if (ephy_node_has_child (topic, properties->bookmark)) {
    ephy_bookmarks_unset_keyword (properties->bookmarks,
                                  topic,
                                  properties->bookmark);
  } else {
    ephy_bookmarks_set_keyword (properties->bookmarks,
                                topic,
                                properties->bookmark);
  }
}

static gboolean
is_separator (GtkTreeModel *model,
              GtkTreeIter  *iter,
              gpointer      data)
{
  EphyNode *node;
  gtk_tree_model_get (model, iter, EPHY_TOPICS_PALETTE_COLUMN_NODE, &node, -1);
  return (node == NULL);
}

static void
location_entry_changed_cb (GtkWidget              *entry,
                           EphyBookmarkProperties *properties)
{
  ephy_bookmarks_set_address (properties->bookmarks,
                              properties->bookmark,
                              gtk_entry_get_text (GTK_ENTRY (entry)));
}

static void
ephy_bookmark_properties_init (EphyBookmarkProperties *properties)
{
}

static GObject *
ephy_bookmark_properties_constructor (GType                  type,
                                      guint                  n_construct_properties,
                                      GObjectConstructParam *construct_params)
{
  GObject                *object;
  EphyBookmarkProperties *properties;

  gboolean lockdown;
  const char   *tmp;
  char         *decoded_url;
  GtkWidget    *entry;

  object = G_OBJECT_CLASS (ephy_bookmark_properties_parent_class)->constructor (type,
                                                                                n_construct_properties,
                                                                                construct_params);
  properties = EPHY_BOOKMARK_PROPERTIES (object);

  gtk_widget_init_template (GTK_WIDGET (properties));

  if (!properties->creating) {
    ephy_initial_state_add_window (GTK_WIDGET (properties),
                                   "bookmark_properties",
                                   290, 280, FALSE,
                                   EPHY_INITIAL_STATE_WINDOW_SAVE_POSITION |
                                   EPHY_INITIAL_STATE_WINDOW_SAVE_SIZE);
  }
  /* Lockdown */
  lockdown = g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                                     EPHY_PREFS_LOCKDOWN_BOOKMARK_EDITING);

  update_window_title (properties);

  gtk_editable_set_editable (GTK_EDITABLE (properties->title_entry), !lockdown);
  tmp = ephy_node_get_property_string (properties->bookmark, EPHY_NODE_BMK_PROP_TITLE);
  gtk_entry_set_text (properties->title_entry, tmp);

  gtk_editable_set_editable (GTK_EDITABLE (properties->adress_entry), !lockdown);
  tmp = ephy_node_get_property_string (properties->bookmark, EPHY_NODE_BMK_PROP_LOCATION);
  decoded_url = ephy_uri_decode (tmp);
  gtk_entry_set_text (properties->adress_entry, decoded_url);
  g_free (decoded_url);

  entry = ephy_topics_entry_new (properties->bookmarks, properties->bookmark);
  gtk_editable_set_editable (GTK_EDITABLE (entry), !lockdown);
  gtk_label_set_mnemonic_widget (properties->topics_label, entry);
  gtk_widget_show (entry);
  gtk_grid_attach (properties->grid, entry, 1, 2, 1, 1);
  gtk_widget_set_hexpand (entry, TRUE);

  gtk_tree_view_set_row_separator_func (properties->topics_tree_view, is_separator, NULL, NULL);

  properties->topics_model = ephy_topics_palette_new (properties->bookmarks, properties->bookmark);
  gtk_tree_view_set_model (properties->topics_tree_view, GTK_TREE_MODEL (properties->topics_model));

  /* TODO bind; and the entry?! */
  gtk_widget_set_sensitive (GTK_WIDGET (properties->topics_scrolled_window), !lockdown);

  ephy_initial_state_add_expander (GTK_WIDGET (properties->topics_expander), "bookmark_properties_list", FALSE);

  if (properties->creating) {
    gtk_dialog_add_button (GTK_DIALOG (properties), _("_Cancel"), GTK_RESPONSE_CANCEL);
    gtk_dialog_add_button (GTK_DIALOG (properties), _("_Add"), GTK_RESPONSE_ACCEPT);
    gtk_dialog_set_default_response (GTK_DIALOG (properties), GTK_RESPONSE_ACCEPT);
  }

  update_warning (properties);

  return object;
}

static void
ephy_bookmark_properties_set_property (GObject      *object,
                                       guint         prop_id,
                                       const GValue *value,
                                       GParamSpec   *pspec)
{
  EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (object);
  EphyNode *bookmarks;

  switch (prop_id) {
    case PROP_BOOKMARKS:
      properties->bookmarks = g_value_get_object (value);
      bookmarks = ephy_bookmarks_get_bookmarks (properties->bookmarks);
      ephy_node_signal_connect_object (bookmarks,
                                       EPHY_NODE_CHILD_ADDED,
                                       (EphyNodeCallback)node_added_cb,
                                       object);
      ephy_node_signal_connect_object (bookmarks,
                                       EPHY_NODE_CHILD_REMOVED,
                                       (EphyNodeCallback)node_removed_cb,
                                       object);
      ephy_node_signal_connect_object (bookmarks,
                                       EPHY_NODE_CHILD_CHANGED,
                                       (EphyNodeCallback)node_changed_cb,
                                       object);
      break;
    case PROP_BOOKMARK:
      ephy_bookmark_properties_set_bookmark
        (properties, g_value_get_pointer (value));
      break;
    case PROP_CREATING:
      properties->creating = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
ephy_bookmark_properties_get_property (GObject    *object,
                                       guint       prop_id,
                                       GValue     *value,
                                       GParamSpec *pspec)
{
  EphyBookmarkProperties *properties = EPHY_BOOKMARK_PROPERTIES (object);

  switch (prop_id) {
    case PROP_BOOKMARK:
      g_value_set_object (value, properties);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
ephy_bookmark_properties_class_init (EphyBookmarkPropertiesClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);

  object_class->constructor = ephy_bookmark_properties_constructor;
  object_class->set_property = ephy_bookmark_properties_set_property;
  object_class->get_property = ephy_bookmark_properties_get_property;

  obj_properties[PROP_BOOKMARKS] =
    g_param_spec_object ("bookmarks",
                         "bookmarks",
                         "bookmarks",
                         EPHY_TYPE_BOOKMARKS,
                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY);

  obj_properties[PROP_BOOKMARK] =
    g_param_spec_pointer ("bookmark",
                          "bookmark",
                          "bookmark",
                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);

  obj_properties[PROP_CREATING] =
    g_param_spec_boolean ("creating",
                          "creating",
                          "creating",
                          FALSE,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);

  /* from UI file */
  gtk_widget_class_set_template_from_resource (widget_class,
                                               "/org/gnome/epiphany/bookmark-properties.ui");

  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, grid);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, title_entry);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, adress_entry);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_label);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_expander);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_tree_view);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, topics_scrolled_window);
  gtk_widget_class_bind_template_child (widget_class, EphyBookmarkProperties, warning_label);

  gtk_widget_class_bind_template_callback (widget_class, title_entry_changed_cb);
  gtk_widget_class_bind_template_callback (widget_class, on_topic_toggled);         /* TODO make the row activatable instead of a little togglebutton */
  gtk_widget_class_bind_template_callback (widget_class, on_topic_edited);          /* TODO topics’ names are not editable from there, they are synced with bookmarks... but this is used for... updating the entry \o/ yay \o/ */
  gtk_widget_class_bind_template_callback (widget_class, location_entry_changed_cb);
  gtk_widget_class_bind_template_callback (widget_class, ephy_bookmark_properties_response_cb);
  gtk_widget_class_bind_template_callback (widget_class, ephy_bookmark_properties_destroy_cb);
}

/* public API */

GtkWidget *
ephy_bookmark_properties_new (EphyBookmarks *bookmarks,
                              EphyNode      *bookmark,
                              gboolean       creating)
{
  g_assert (bookmarks != NULL);

  return GTK_WIDGET (g_object_new (EPHY_TYPE_BOOKMARK_PROPERTIES,
                                   "bookmarks", bookmarks,
                                   "bookmark", bookmark,
                                   "creating", creating,
                                   "use-header-bar", TRUE,
                                   NULL));
}

EphyNode *
ephy_bookmark_properties_get_node (EphyBookmarkProperties *properties)
{
  return properties->bookmark;
}
