/*
 * Copyright (c) 2011 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Author: Cosimo Cecchi <cosimoc@redhat.com>
 *
 */

#include "gd-main-view-generic.h"

enum {
  VIEW_SELECTION_CHANGED,
  NUM_SIGNALS
};

static guint signals[NUM_SIGNALS] = { 0, };

typedef GdMainViewGenericIface GdMainViewGenericInterface;
G_DEFINE_INTERFACE (GdMainViewGeneric, gd_main_view_generic, GTK_TYPE_WIDGET)

static void
gd_main_view_generic_default_init (GdMainViewGenericInterface *iface)
{
  signals[VIEW_SELECTION_CHANGED] =
    g_signal_new ("view-selection-changed",
                  GD_TYPE_MAIN_VIEW_GENERIC,
                  G_SIGNAL_RUN_LAST,
                  0, NULL, NULL, NULL,
                  G_TYPE_NONE, 0);
}

/**
 * gd_main_view_generic_set_model:
 * @self:
 * @model: (allow-none):
 *
 */
void
gd_main_view_generic_set_model (GdMainViewGeneric *self,
                                GtkTreeModel *model)
{
  GdMainViewGenericInterface *iface;

  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);

  (* iface->set_model) (self, model);
}

GtkTreePath *
gd_main_view_generic_get_path_at_pos (GdMainViewGeneric *self,
                                      gint x,
                                      gint y)
{
  GdMainViewGenericInterface *iface;

  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);

  return (* iface->get_path_at_pos) (self, x, y);
}

void
gd_main_view_generic_set_selection_mode (GdMainViewGeneric *self,
                                         gboolean selection_mode)
{
  GdMainViewGenericInterface *iface;

  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);

  (* iface->set_selection_mode) (self, selection_mode);
}


typedef struct {
  GtkTreePath *rubberband_start;
  GtkTreePath *rubberband_end;
} RubberbandInfo;

static void
rubber_band_info_destroy (RubberbandInfo *info)
{
  g_clear_pointer (&info->rubberband_start,
		   gtk_tree_path_free);
  g_clear_pointer (&info->rubberband_end,
		   gtk_tree_path_free);
  g_slice_free (RubberbandInfo, info);
}

static RubberbandInfo*
get_rubber_band_info (GdMainViewGeneric *self)
{
  RubberbandInfo *info;

  info = g_object_get_data (G_OBJECT (self), "gd-main-view-generic-rubber-band");
  if (info == NULL)
    {
      info = g_slice_new0 (RubberbandInfo);
      g_object_set_data_full (G_OBJECT (self), "gd-main-view-generic-rubber-band",
			      info, (GDestroyNotify)rubber_band_info_destroy);
    }

  return info;
}

void
gd_main_view_generic_set_rubberband_range (GdMainViewGeneric *self,
					   GtkTreePath *start,
					   GtkTreePath *end)
{
  RubberbandInfo *info;

  info = get_rubber_band_info (self);

  if (start == NULL || end == NULL)
    {
      g_clear_pointer (&info->rubberband_start,
		       gtk_tree_path_free);
      g_clear_pointer (&info->rubberband_end,
		       gtk_tree_path_free);
    }
  else
    {
      if (gtk_tree_path_compare (start, end) < 0)
	{
	  info->rubberband_start = gtk_tree_path_copy (start);
	  info->rubberband_end = gtk_tree_path_copy (end);
	}
      else
	{
	  info->rubberband_start = gtk_tree_path_copy (end);
	  info->rubberband_end = gtk_tree_path_copy (start);
	}
    }

  gtk_widget_queue_draw (GTK_WIDGET (self));
}

void
_gd_main_view_generic_get_rubberband_range (GdMainViewGeneric *self,
					    GtkTreePath **start,
					    GtkTreePath **end)
{
  RubberbandInfo *info;

  info = get_rubber_band_info (self);

  *start = info->rubberband_start;
  *end = info->rubberband_end;
}

void
gd_main_view_generic_scroll_to_path (GdMainViewGeneric *self,
                                     GtkTreePath *path)
{
  GdMainViewGenericInterface *iface;

  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);

  (* iface->scroll_to_path) (self, path);
}

/**
 * gd_main_view_generic_get_model:
 *
 * Returns: (transfer none): The associated model
 */
GtkTreeModel *
gd_main_view_generic_get_model (GdMainViewGeneric *self)
{
  GdMainViewGenericInterface *iface;

  iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self);

  return (* iface->get_model) (self);
}

static gboolean
build_selection_uris_foreach (GtkTreeModel *model,
                              GtkTreePath *path,
                              GtkTreeIter *iter,
                              gpointer user_data)
{
  GPtrArray *ptr_array = user_data;
  gchar *uri;
  gboolean is_selected;

  gtk_tree_model_get (model, iter,
                      GD_MAIN_COLUMN_URI, &uri,
                      GD_MAIN_COLUMN_SELECTED, &is_selected,
                      -1);

  if (is_selected)
    g_ptr_array_add (ptr_array, uri);
  else
    g_free (uri);

  return FALSE;
}

static gchar **
model_get_selection_uris (GtkTreeModel *model)
{
  GPtrArray *ptr_array = g_ptr_array_new ();

  gtk_tree_model_foreach (model,
                          build_selection_uris_foreach,
                          ptr_array);
  
  g_ptr_array_add (ptr_array, NULL);
  return (gchar **) g_ptr_array_free (ptr_array, FALSE);
}

static gboolean
set_selection_foreach (GtkTreeModel *model,
                       GtkTreePath *path,
                       GtkTreeIter *iter,
                       gpointer user_data)
{
  gboolean selection = GPOINTER_TO_INT (user_data);
  GtkTreeModel *actual_model;
  GtkTreeIter real_iter;

  if (GTK_IS_TREE_MODEL_FILTER (model))
    {
      actual_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
      gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
                                                        &real_iter, iter);
    }
  else if (GTK_IS_TREE_MODEL_SORT (model))
    {
      actual_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
      gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model),
                                                      &real_iter, iter);
    }
  else
    {
      actual_model = model;
      real_iter = *iter;
    }

  if (GTK_IS_LIST_STORE (actual_model))
    {
      gtk_list_store_set (GTK_LIST_STORE (actual_model), &real_iter,
                          GD_MAIN_COLUMN_SELECTED, selection,
                          -1);
    }
  else
    {
      gtk_tree_store_set (GTK_TREE_STORE (actual_model), &real_iter,
                          GD_MAIN_COLUMN_SELECTED, selection,
                          -1);
    }

  return FALSE;
}

static void
set_all_selection (GdMainViewGeneric *self,
                   GtkTreeModel *model,
                   gboolean selection)
{
  gtk_tree_model_foreach (model,
                          set_selection_foreach,
                          GINT_TO_POINTER (selection));
  g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0);
}

void
gd_main_view_generic_select_all (GdMainViewGeneric *self)
{
  GtkTreeModel *model = gd_main_view_generic_get_model (self);

  set_all_selection (self, model, TRUE);
}

void
gd_main_view_generic_unselect_all (GdMainViewGeneric *self)
{
  GtkTreeModel *model = gd_main_view_generic_get_model (self);

  set_all_selection (self, model, FALSE);
}

void
_gd_main_view_generic_dnd_common (GtkTreeModel *model,
                                  gboolean selection_mode,
                                  GtkTreePath *path,
                                  GtkSelectionData *data)
{
  gchar **uris;

  if (selection_mode)
    {
      uris = model_get_selection_uris (model);
    }
  else
    {
      GtkTreeIter iter;
      gboolean res;
      gchar *uri = NULL;

      if (path != NULL)
        {
          res = gtk_tree_model_get_iter (model, &iter, path);
          if (res)
            gtk_tree_model_get (model, &iter,
                                GD_MAIN_COLUMN_URI, &uri,
                                -1);
        }

      uris = g_new0 (gchar *, 2);
      uris[0] = uri;
      uris[1] = NULL;
    }

  gtk_selection_data_set_uris (data, uris);
  g_strfreev (uris);
}
