/*
 * nautilus-window-slot-dnd.c - Handle DnD for widgets acting as
 * NautilusWindowSlot proxies
 *
 * Copyright (C) 2000, 2001 Eazel, Inc.
 * Copyright (C) 2010, Red Hat, Inc.
 *
 * The Gnome Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 * The Gnome Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Authors: Pavel Cisler <pavel@eazel.com>,
 *      Ettore Perazzoli <ettore@gnu.org>
 */

#include <config.h>

#include "nautilus-application.h"
#include "nautilus-files-view-dnd.h"
#include "nautilus-window-slot-dnd.h"

#ifdef GDK_WINDOWING_X11
#include <gdk/x11/gdkx.h>
#endif

typedef struct
{
    NautilusFile *target_file;
    NautilusWindowSlot *target_slot;
    GtkWidget *widget;

    graphene_point_t hover_start_point;
    guint switch_location_timer;
} NautilusDragSlotProxyInfo;

static void
switch_location (NautilusDragSlotProxyInfo *drag_info)
{
    GFile *location;
    GtkRoot *window;

    if (drag_info->target_file == NULL)
    {
        return;
    }

    window = gtk_widget_get_root (drag_info->widget);
    g_assert (NAUTILUS_IS_WINDOW (window));

    location = nautilus_file_get_location (drag_info->target_file);
    nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()),
                                             location, NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE,
                                             NULL, NAUTILUS_WINDOW (window), NULL);
    g_object_unref (location);
}

static gboolean
slot_proxy_switch_location_timer (gpointer user_data)
{
    NautilusDragSlotProxyInfo *drag_info = user_data;

    drag_info->switch_location_timer = 0;

    switch_location (drag_info);

    return FALSE;
}

static void
slot_proxy_check_switch_location_timer (NautilusDragSlotProxyInfo *drag_info)
{
    if (drag_info->switch_location_timer)
    {
        return;
    }

    drag_info->switch_location_timer = g_timeout_add (HOVER_TIMEOUT,
                                                      slot_proxy_switch_location_timer,
                                                      drag_info);
}

static void
slot_proxy_remove_switch_location_timer (NautilusDragSlotProxyInfo *drag_info)
{
    if (drag_info->switch_location_timer != 0)
    {
        g_source_remove (drag_info->switch_location_timer);
        drag_info->switch_location_timer = 0;
    }
}

static GdkDragAction
slot_proxy_drag_motion (GtkDropTarget *target,
                        gdouble        x,
                        gdouble        y,
                        gpointer       user_data)
{
    NautilusDragSlotProxyInfo *drag_info;
    NautilusWindowSlot *target_slot;
    GtkRoot *window;
    GdkDragAction action;
    char *target_uri;
    GFile *location;
    const GValue *value;
    graphene_point_t start;

    drag_info = user_data;

    action = 0;

    value = gtk_drop_target_get_value (target);
    if (value == NULL)
    {
        return 0;
    }

    window = gtk_widget_get_root (drag_info->widget);
    g_assert (NAUTILUS_IS_WINDOW (window));

    target_uri = NULL;
    if (drag_info->target_file != NULL)
    {
        target_uri = nautilus_file_get_uri (drag_info->target_file);
    }
    else
    {
        if (drag_info->target_slot != NULL)
        {
            target_slot = drag_info->target_slot;
        }
        else
        {
            target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window));
        }

        if (target_slot != NULL)
        {
            location = nautilus_window_slot_get_location (target_slot);
            target_uri = g_file_get_uri (location);
        }
    }

    if (target_uri != NULL)
    {
        NautilusFile *file;
        NautilusDirectory *directory;
        gboolean can;
        file = nautilus_file_get_existing_by_uri (target_uri);
        directory = nautilus_directory_get_for_file (file);
        can = nautilus_file_can_write (file) && nautilus_directory_is_editable (directory);
        nautilus_directory_unref (directory);
        g_object_unref (file);
        if (!can)
        {
            action = 0;
            goto out;
        }

        if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
        {
            GSList *items = g_value_get_boxed (value);
            action = nautilus_dnd_get_preferred_action (file, items->data);
        }
    }

    g_free (target_uri);

out:
    start = drag_info->hover_start_point;
    if (gtk_drag_check_threshold (drag_info->widget, start.x, start.y, x, y))
    {
        slot_proxy_remove_switch_location_timer (drag_info);
        slot_proxy_check_switch_location_timer (drag_info);
        drag_info->hover_start_point.x = x;
        drag_info->hover_start_point.y = y;
    }

    return action;
}

static void
drag_info_free (gpointer user_data)
{
    NautilusDragSlotProxyInfo *drag_info = user_data;

    g_clear_object (&drag_info->target_file);
    g_clear_object (&drag_info->target_slot);

    g_slice_free (NautilusDragSlotProxyInfo, drag_info);
}

static void
drag_info_clear (NautilusDragSlotProxyInfo *drag_info)
{
    slot_proxy_remove_switch_location_timer (drag_info);
}

static void
slot_proxy_drag_leave (GtkDropTarget *target,
                       gpointer       user_data)
{
    NautilusDragSlotProxyInfo *drag_info;

    drag_info = user_data;

    drag_info_clear (drag_info);
}

static void
slot_proxy_handle_drop (GtkDropTarget *target,
                        const GValue  *value,
                        gdouble        x,
                        gdouble        y,
                        gpointer       user_data)
{
    GtkRoot *window;
    NautilusWindowSlot *target_slot;
    NautilusFilesView *target_view;
    char *target_uri;
    GList *uri_list = NULL;
    GFile *location;
    NautilusDragSlotProxyInfo *drag_info;

    drag_info = user_data;

    window = gtk_widget_get_root (drag_info->widget);
    g_assert (NAUTILUS_IS_WINDOW (window));

    if (drag_info->target_slot != NULL)
    {
        target_slot = drag_info->target_slot;
    }
    else
    {
        target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window));
    }

    target_uri = NULL;
    if (drag_info->target_file != NULL)
    {
        target_uri = nautilus_file_get_uri (drag_info->target_file);
    }
    else if (target_slot != NULL)
    {
        location = nautilus_window_slot_get_location (target_slot);
        target_uri = g_file_get_uri (location);
    }

    target_view = NULL;
    if (target_slot != NULL)
    {
        NautilusView *view;

        view = nautilus_window_slot_get_current_view (target_slot);

        if (view && NAUTILUS_IS_FILES_VIEW (view))
        {
            target_view = NAUTILUS_FILES_VIEW (view);
        }
    }

    if (target_slot != NULL && target_view != NULL)
    {
        if (G_VALUE_HOLDS (value, GDK_TYPE_FILE_LIST))
        {
            GSList *items = g_value_get_boxed (value);
            GdkDragAction actions;

            for (GSList *l = items; l != NULL; l = l->next)
            {
                uri_list = g_list_prepend (uri_list, g_file_get_uri (l->data));
            }

            actions = gdk_drop_get_actions (gtk_drop_target_get_current_drop (target));

            #ifdef GDK_WINDOWING_X11
            if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
            {
                /* Temporary workaround until the below GTK MR (or equivalend fix)
                 * is merged.  Without this fix, the preferred action isn't set correctly.
                 * https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4982 */
                GdkDrag *drag = gdk_drop_get_drag (gtk_drop_target_get_current_drop (target));
                actions = gdk_drag_get_selected_action (drag);
            }
            #endif

            nautilus_files_view_drop_proxy_received_uris (target_view,
                                                          uri_list,
                                                          target_uri,
                                                          actions);
            g_list_free_full (uri_list, g_free);
        }
    }
    g_free (target_uri);

    drag_info_clear (drag_info);
}

void
nautilus_drag_slot_proxy_init (GtkWidget          *widget,
                               NautilusFile       *target_file,
                               NautilusWindowSlot *target_slot)
{
    NautilusDragSlotProxyInfo *drag_info;
    GtkDropTarget *target;

    g_assert (GTK_IS_WIDGET (widget));

    drag_info = g_slice_new0 (NautilusDragSlotProxyInfo);

    g_object_set_data_full (G_OBJECT (widget), "drag-slot-proxy-data", drag_info,
                            drag_info_free);

    if (target_file != NULL)
    {
        drag_info->target_file = nautilus_file_ref (target_file);
    }

    if (target_slot != NULL)
    {
        drag_info->target_slot = g_object_ref (target_slot);
    }

    drag_info->widget = widget;
    /* TODO: Implement GDK_ACTION_ASK */
    target = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_ALL);

    gtk_drop_target_set_preload (target, TRUE);
    /* TODO: Implement GDK_TYPE_STRING */
    gtk_drop_target_set_gtypes (target, (GType[1]) { GDK_TYPE_FILE_LIST }, 1);
    g_signal_connect (target, "enter", G_CALLBACK (slot_proxy_drag_motion), drag_info);
    g_signal_connect (target, "motion", G_CALLBACK (slot_proxy_drag_motion), drag_info);
    g_signal_connect (target, "drop", G_CALLBACK (slot_proxy_handle_drop), drag_info);
    g_signal_connect (target, "leave", G_CALLBACK (slot_proxy_drag_leave), drag_info);
    gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (target));
}
