/*
 * gedit plugin for GTK+ CSS
 *
 * ©2011 Carlos Garnacho <carlosg@gnome.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, 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.
 */

#include "config.h"
#include "cossa-window.h"
#include "cossa-tool-menu-action.h"

#include <glib/gi18n.h>

#define SAMPLES_PLACEHOLDER_PATH "/PreviewToolbar/UpdatePreview/SamplesMenu/samples-placeholder"

static void    cossa_window_finalize  (GObject   *object);

static void    zoom_in_preview_cb     (GtkAction *action,
                                       gpointer   user_data);
static void    zoom_out_preview_cb    (GtkAction *action,
                                       gpointer   user_data);
static void    zoom_normal_preview_cb (GtkAction *action,
                                       gpointer   user_data);
static void    update_preview_cb      (GtkAction *action,
                                       gpointer   user_data);

typedef struct _CossaWindowPrivate CossaWindowPrivate;

struct _CossaWindowPrivate
{
  GtkWidget *toolbar;
  GtkWidget *previewer;

  GtkUIManager *ui_manager;
  GtkActionGroup *action_group;
  guint merge_id;

  GtkRadioAction *radio_action;
};

enum {
  UPDATE,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static const GtkActionEntry action_entries[] = {
  { "Zoom1",         GTK_STOCK_ZOOM_100, N_("_Normal size"),    "<Ctrl>0",     N_("Normal size"),    G_CALLBACK (zoom_normal_preview_cb) },
  { "ZoomOut",       GTK_STOCK_ZOOM_OUT, N_("_Zoom out"),       "<Ctrl>minus", N_("Zoom out"),       G_CALLBACK (zoom_out_preview_cb) },
  { "ZoomIn",        GTK_STOCK_ZOOM_IN,  N_("_Zoom in"),        "<Ctrl>plus",  N_("Zoom in"),        G_CALLBACK (zoom_in_preview_cb) },
  { "SamplesMenu",   NULL,               N_("Samples"),         NULL,          NULL,                 NULL }
};

G_DEFINE_TYPE (CossaWindow, cossa_window, GTK_TYPE_WINDOW)

static void
cossa_window_class_init (CossaWindowClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = cossa_window_finalize;

  signals [UPDATE] =
    g_signal_new ("update",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (CossaWindowClass, update),
                  NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

  g_type_class_add_private (klass, sizeof (CossaWindowPrivate));
}

static void
select_sample_cb (GtkAction *action,
                  gpointer   user_data)
{
  CossaWindowPrivate *priv;
  GtkWidget *widget;

  widget = g_object_get_data (G_OBJECT (action), "cossa-sample-widget");
  priv = COSSA_WINDOW (user_data)->priv;

  cossa_previewer_select_sample (COSSA_PREVIEWER (priv->previewer), widget);
}

static void
add_sample_widget (CossaWindow *window,
                   GtkWidget   *widget,
                   gint         n_sample)
{
  CossaWindowPrivate *priv;
  GtkAction *action;
  const gchar *title;
  gchar *action_name;

  priv = window->priv;

  if (widget != NULL)
    {
      cossa_previewer_add_sample (COSSA_PREVIEWER (priv->previewer), widget);
      title = gtk_window_get_title (GTK_WINDOW (widget));
    }
  else
    title = _("All Samples");

  action_name = g_strdup_printf ("Sample%d", n_sample);

  /* Add item to menu */
  
  action = GTK_ACTION (gtk_radio_action_new (action_name, title, NULL, NULL, n_sample));
  g_object_set_data (G_OBJECT (action), "cossa-sample-widget", widget);
  g_signal_connect (action, "activate",
                    G_CALLBACK (select_sample_cb), window);

  if (!priv->radio_action)
    priv->radio_action = GTK_RADIO_ACTION (action);
  else
    gtk_radio_action_join_group (GTK_RADIO_ACTION (action),
                                 priv->radio_action);

  gtk_action_group_add_action (priv->action_group, action);

  gtk_ui_manager_add_ui (priv->ui_manager,
                         priv->merge_id,
                         SAMPLES_PLACEHOLDER_PATH,
                         title, action_name,
                         GTK_UI_MANAGER_MENUITEM,
                         FALSE);

  g_free (action_name);
}

static void
update_zoom_controls (CossaWindow *window)
{
  GtkAction *zoom_in_action, *zoom_out_action, *zoom_normal_action;
  CossaWindowPrivate *priv = window->priv;
  CossaZoomLevel zoom_level;

  zoom_level = cossa_previewer_get_zoom_level (COSSA_PREVIEWER (priv->previewer));
  zoom_in_action = gtk_ui_manager_get_action (priv->ui_manager, "/PreviewToolbar/ZoomIn");
  zoom_out_action = gtk_ui_manager_get_action (priv->ui_manager, "/PreviewToolbar/ZoomOut");
  zoom_normal_action = gtk_ui_manager_get_action (priv->ui_manager, "/PreviewToolbar/Zoom1");

  gtk_action_set_sensitive (zoom_in_action, (zoom_level != COSSA_ZOOM_4_1));
  gtk_action_set_sensitive (zoom_out_action, (zoom_level != COSSA_ZOOM_1_1));
  gtk_action_set_sensitive (zoom_normal_action, (zoom_level != COSSA_ZOOM_1_1));
}

static gint
compare_titles (gconstpointer a,
                gconstpointer b)
{
  GtkWindow *w1, *w2;

  w1 = GTK_WINDOW (a);
  w2 = GTK_WINDOW (b);

  return g_strcmp0 (gtk_window_get_title (w1),
                    gtk_window_get_title (w2));
}

static void
load_samples (CossaWindow *window)
{
  CossaWindowPrivate *priv;
  GList *list = NULL, *l;
  const gchar *name;
  gint i = 1;
  GDir *dir;
  GtkAction *active_action;

  priv = window->priv;
  dir = g_dir_open (SAMPLES_DIR, 0, NULL);

  if (!dir)
    return;

  while ((name = g_dir_read_name (dir)) != NULL)
    {
      GtkBuilder *builder;
      GtkWidget *widget;
      gchar *path;

      builder = gtk_builder_new ();

      path = g_build_filename (SAMPLES_DIR, name, NULL);
      gtk_builder_add_from_file (builder, path, NULL);
      g_free (path);

      widget = GTK_WIDGET (gtk_builder_get_object (builder, "preview-sample-toplevel"));

      g_object_unref (builder);

      if (!GTK_IS_OFFSCREEN_WINDOW (widget))
        continue;

      list = g_list_insert_sorted (list, widget, compare_titles);
    }

  g_dir_close (dir);

  /* Generic one for all samples */
  add_sample_widget (window, NULL, 0);

  for (l = list; l; l = l->next)
    add_sample_widget (window, l->data, i++);

  active_action = gtk_action_group_get_action (priv->action_group,
                                               "Sample0");
  gtk_action_activate (active_action);

  g_list_free (list);
}

static void
update_preview_cb (GtkAction *action,
                   gpointer   user_data)
{
  CossaWindow *window = user_data;

  g_signal_emit (window, signals[UPDATE], 0);
}

static void
cossa_window_init (CossaWindow *window)
{
  CossaWindowPrivate *priv;
  GtkActionGroup *action_group;
  GtkWidget *box, *sw;
  GtkAction *action;

  priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
                                                     COSSA_TYPE_WINDOW,
                                                     CossaWindowPrivate);

  priv->ui_manager = gtk_ui_manager_new ();

  priv->action_group = gtk_action_group_new ("CossaSamplesMenu");
  gtk_ui_manager_insert_action_group (priv->ui_manager, priv->action_group, -1);

  action_group = gtk_action_group_new ("CossaWindowToolbar");
  gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1);


  gtk_action_group_add_actions (action_group, action_entries,
                                G_N_ELEMENTS (action_entries),
                                window);

  action = g_object_new (COSSA_TYPE_TOOL_MENU_ACTION,
                         "name", "UpdatePreview",
                         "label", N_("_Update preview"),
                         "tooltip", N_("Update preview"),
                         "stock-id", GTK_STOCK_REFRESH,
                         "is-important", FALSE,
                         "sensitive", TRUE,
                         NULL);
  g_signal_connect (action, "activate",
                    G_CALLBACK (update_preview_cb), window);
  gtk_action_group_add_action_with_accel (action_group, action, "<Ctrl>F8");
  g_object_unref (action);

  gtk_ui_manager_add_ui_from_string (priv->ui_manager,
                                     "<ui>"
                                     "  <toolbar name='PreviewToolbar'>"
                                     "    <toolitem action='UpdatePreview'>"
                                     "      <menu action='SamplesMenu'>"
                                     "        <placeholder name='samples-placeholder' />"
                                     "      </menu>"
                                     "    </toolitem>"
                                     "    <separator />"
                                     "    <toolitem action='Zoom1' />"
                                     "    <toolitem action='ZoomOut' />"
                                     "    <toolitem action='ZoomIn' />"
                                     "  </toolbar>"
                                     "</ui>",
                                     -1, NULL);

  gtk_window_add_accel_group (GTK_WINDOW (window),
                              gtk_ui_manager_get_accel_group (priv->ui_manager));

  priv->toolbar = gtk_ui_manager_get_widget (priv->ui_manager, "/PreviewToolbar");
  priv->merge_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
  gtk_widget_show (priv->toolbar);

  priv->previewer = cossa_previewer_new ();
  gtk_widget_show (priv->previewer);

  sw = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), priv->previewer);
  gtk_widget_show (sw);

  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_box_pack_start (GTK_BOX (box), priv->toolbar, FALSE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (box), sw, TRUE, TRUE, 0);
  gtk_widget_show (box);

  gtk_container_add (GTK_CONTAINER (window), box);

  update_zoom_controls (window);
  load_samples (window);
}

static void
cossa_window_finalize (GObject *object)
{
  CossaWindowPrivate *priv;

  priv = COSSA_WINDOW (object)->priv;
  g_object_unref (priv->ui_manager);
  g_object_unref (priv->action_group);

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

static void
zoom_in_preview_cb (GtkAction *action,
                    gpointer   user_data)
{
  CossaWindow *window = COSSA_WINDOW (user_data);
  CossaWindowPrivate *priv = window->priv;
  CossaZoomLevel zoom_level;

  zoom_level = cossa_previewer_get_zoom_level (COSSA_PREVIEWER (priv->previewer));

  g_assert (zoom_level != COSSA_ZOOM_4_1);
  zoom_level *= 2;

  cossa_previewer_set_zoom_level (COSSA_PREVIEWER (priv->previewer), zoom_level);
  update_zoom_controls (window);
}

static void
zoom_out_preview_cb (GtkAction *action,
                     gpointer   user_data)
{
  CossaWindow *window = COSSA_WINDOW (user_data);
  CossaWindowPrivate *priv = window->priv;
  CossaZoomLevel zoom_level;

  zoom_level = cossa_previewer_get_zoom_level (COSSA_PREVIEWER (priv->previewer));

  g_assert (zoom_level != COSSA_ZOOM_1_1);
  zoom_level /= 2;

  cossa_previewer_set_zoom_level (COSSA_PREVIEWER (priv->previewer), zoom_level);
  update_zoom_controls (window);
}

static void
zoom_normal_preview_cb (GtkAction *action,
                        gpointer   user_data)
{
  CossaWindow *window = COSSA_WINDOW (user_data);
  CossaWindowPrivate *priv = window->priv;

  cossa_previewer_set_zoom_level (COSSA_PREVIEWER (priv->previewer), COSSA_ZOOM_1_1);
  update_zoom_controls (window);
}

GtkWidget *
cossa_window_new (void)
{
  return g_object_new (COSSA_TYPE_WINDOW, NULL);
}

CossaPreviewer *
cossa_window_get_previewer (CossaWindow *window)
{
  CossaWindowPrivate *priv;

  g_return_val_if_fail (COSSA_IS_WINDOW (window), NULL);

  priv = window->priv;
  return COSSA_PREVIEWER (priv->previewer);
}
