/*
 * Copyright (C) 2003-2006 the xine project
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * drag and drop support for the main window
 */

#include "globals.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <gtk/gtk.h>

#include "drag_drop.h"
#include "playlist.h"

/*
#define LOG
*/

static const GtkTargetEntry target_table[] = {
  { "text/uri-list", 0, 0 },
  { "x-gxine/internal", GTK_TARGET_SAME_APP, 1, },
};

int dnd_add_mrls (GtkSelectionData *gsdata, int *current, int ins_pos)
{
  gint i = -1, len = -1, first = -1;
  gchar str[MAX_MRL_LEN];
  guint length = gtk_selection_data_get_length(gsdata);
  const guchar *data = gtk_selection_data_get_data(gsdata);

  while (++i < length)
  {
    char c = data[i];
    if (len < MAX_MRL_LEN - 2 && c != '\r' && c != '\n')
      str[++len] = c;
    else if ((c == '\r' || c == '\n') && len > 0)
    {
      str[len + 1] = 0;

      /* special-case file:// MRLs for DVD processing */
      if (!strncasecmp (str, "file:///", 8) ||
	  !strncasecmp (str, "file://localhost/", 17))
      {
	unsigned int i = (str[7] == '/' ? 6 : 15), ext = 0;
	while (str[++i])
	{
	  if (str[i] == '%')
	  {
	    char c[3] = { str[i+1], str[i+2], 0 };
	    i += 2;
	    str[ext] = (char) strtol (c, NULL, 16);
	  }
	  else
	    str[ext] = str[i];
	  ++ext;
	}
	str[ext] = 0;
      }

      if (first < 0)
	first = playlist_add_mrl (str, ins_pos);
      else
	playlist_add_mrl (str, ins_pos);
      if (ins_pos >= 0 && ins_pos++ <= (current ? *current : -1))
	++(*current);
      len = -1;
    }
  }

  return first;
}


/* drag&drop onto TreeView objects */

drag_mrl_t drag_mrl = { NULL, NULL, -1 };

static void drag_start_cb (GtkTreeView *widget, GdkDragContext *context,
			   gpointer data)
{
  GtkTreeSelection *sel = gtk_tree_view_get_selection (widget);
  GtkTreeIter iter;

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
  {
    drag_mrl.model = gtk_tree_view_get_model (widget);
    drag_mrl.path = gtk_tree_model_get_path (drag_mrl.model, &iter);
    drag_mrl.index = gtk_tree_path_get_indices (drag_mrl.path)[0];
  }
}

static void drag_end_cb (GtkTreeView *widget, GdkDragContext *context,
			 gpointer data)
{
  if (drag_mrl.model)
  {
    drag_mrl.model = NULL;
    drag_mrl.index = -1;
    gtk_tree_path_free (drag_mrl.path);
  }
}

static void drag_data_get_cb (GtkTreeView *widget, GdkDragContext *context,
			      GtkSelectionData *selection, guint info,
			      guint time, gpointer data)
{
  if (drag_mrl.model)
    gtk_tree_set_row_drag_data (selection, drag_mrl.model, drag_mrl.path);
}

void treeview_drag_drop_setup (GtkTreeView *widget,
			       void (*drop_cb) (GtkTreeView *, GdkDragContext *,
						gint, gint, GtkSelectionData *,
						guint, guint))
{
  g_object_connect (G_OBJECT (widget),
	"signal::drag-begin", G_CALLBACK (drag_start_cb), NULL,
	"signal::drag-data-get", G_CALLBACK (drag_data_get_cb), NULL,
	"signal::drag-data-received", G_CALLBACK (drop_cb), NULL,
	"signal::drag-end", G_CALLBACK (drag_end_cb), NULL,
	NULL);
  gtk_tree_view_enable_model_drag_source (widget, GDK_BUTTON1_MASK,
					  target_table + 1, 1,
					  GDK_ACTION_COPY | GDK_ACTION_MOVE);
  gtk_tree_view_enable_model_drag_dest (widget, target_table, 2,
					GDK_ACTION_COPY | GDK_ACTION_MOVE);
}

int treeview_get_drop_index (GtkTreeView *widget, GdkDragContext *context,
			     gint x, gint y)
{
  GtkTreePath *path;
  gint cx, cy, ins_pos = -1;

#if !GTK_CHECK_VERSION(3, 0, 0)
  gdk_window_get_geometry (gtk_tree_view_get_bin_window (widget),
                           &cx, &cy, NULL, NULL, NULL);
#else
  gdk_window_get_geometry (gtk_tree_view_get_bin_window (widget),
                           &cx, &cy, NULL, NULL);
#endif

  if (gtk_tree_view_get_path_at_pos (widget, x -= cx, y -= cy,
				     &path, NULL, &cx, &cy))
  {
    GdkRectangle rect;
    /* in lower 1/3 of row? use next row as target */
    gtk_tree_view_get_background_area (widget, path,
				       gtk_tree_view_get_column (widget, 0),
				       &rect);
    if (cy >= rect.height * 2 / 3.0)
    {
      gtk_tree_path_free (path);
      if (gtk_tree_view_get_path_at_pos (widget, x, y + rect.height,
					 &path, NULL, &cx, &cy))
	ins_pos = gtk_tree_path_get_indices (path)[0];
    }
    else
      ins_pos = gtk_tree_path_get_indices (path)[0];
    gtk_tree_path_free (path);
  }
  return ins_pos;
}


/* drag&drop onto non-TreeView objects */

static void drag_data_received_cb (GtkWidget *widget, GdkDragContext *context,
				   gint x, gint y, GtkSelectionData *data,
				   guint info, guint time, gpointer autoplay)
{
  logprintf ("drag_drop: drag callback, length=%d, format=%d\n",
	     data->length, data->format);

  gint format = gtk_selection_data_get_format(data);
  if (format == 8)
  {
    int first = dnd_add_mrls (data, NULL, -1);
    if (first >= 0 && autoplay)
      playlist_play (first);
    gtk_drag_finish (context, TRUE, FALSE, time);
  }
  else
    gtk_drag_finish (context, FALSE, FALSE, time);
}

void drag_drop_setup (GtkWidget *widget, gboolean play)
{
  gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_ALL,
		     target_table, 1, GDK_ACTION_COPY);
  g_signal_connect (G_OBJECT (widget), "drag-data-received",
		    G_CALLBACK (drag_data_received_cb), play ? "" : NULL);
}
