"""
    Flowblade Movie Editor is a nonlinear video editor.
    Copyright 2012 Janne Liljeblad.

    This file is part of Flowblade Movie Editor <https://github.com/jliljebl/flowblade/>.

    Flowblade Movie Editor 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 3 of the License, or
    (at your option) any later version.

    Flowblade Movie Editor 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 Flowblade Movie Editor.  If not, see <http://www.gnu.org/licenses/>.
"""

"""
Module handles drag and drop between widgets.
"""

from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import GLib

import os

import editorstate
import callbackbridge
import gui
import utils
import respaths

# Source identifiers
SOURCE_MEDIA_FILE = "media_file"
SOURCE_MONITOR_WIDGET = "monitor"
SOURCE_EFFECTS_TREE = "effects"
SOURCE_RANGE_LOG = "range log"

# GUI consts
MEDIA_ICON_WIDTH = 20
MEDIA_ICON_HEIGHT = 15

MEDIA_FILES_DND_TARGET = Gtk.TargetEntry.new('media_file', Gtk.TargetFlags.SAME_APP, 0)
EFFECTS_DND_TARGET = Gtk.TargetEntry.new('effect', Gtk.TargetFlags.SAME_APP, 0)
CLIPS_DND_TARGET = Gtk.TargetEntry.new('clip', Gtk.TargetFlags.SAME_APP, 0)
RANGE_DND_TARGET = Gtk.TargetEntry.new('range', Gtk.TargetFlags.SAME_APP, 0)
CLIP_EFFECTS_DND_TARGET = Gtk.TargetEntry.new('clip_effect', Gtk.TargetFlags.SAME_APP, 0)
URI_DND_TARGET = Gtk.TargetEntry.new('text/uri-list', 0, 0)


# These are used to hold data needed on drag drop instead of the API provided by GtkSelectionData.
drag_data = None 
drag_source = None

# We start drag from out of timeline manually and need to finish it ourselves too.
tline_out_drag_context = None

# Drag icons
clip_icon = None
empty_icon = None


def init():
    global clip_icon, empty_icon
    clip_icon = GdkPixbuf.Pixbuf.new_from_file(respaths.IMAGE_PATH + "clip_dnd.png")
    empty_icon = GdkPixbuf.Pixbuf.new_from_file(respaths.IMAGE_PATH + "empty.png")


# ----------------------------------------------- set gui components as drag sources and destinations
def connect_media_files_object_widget(widget):
    widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                           [MEDIA_FILES_DND_TARGET], 
                           Gdk.DragAction.COPY)
    widget.connect("drag_data_get", _media_files_drag_data_get)
    widget.drag_source_set_icon_pixbuf(clip_icon)
    
    connect_media_drop_widget(widget)
    
def connect_media_files_object_cairo_widget(widget):
    widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                           [MEDIA_FILES_DND_TARGET], 
                           Gdk.DragAction.COPY)
    widget.connect("drag_data_get", _media_files_drag_data_get)
    widget.drag_source_set_icon_pixbuf(clip_icon)

    connect_media_drop_widget(widget)

def connect_media_drop_widget(widget):
    widget.drag_dest_set(Gtk.DestDefaults.ALL, [URI_DND_TARGET], Gdk.DragAction.COPY)
    widget.drag_dest_add_uri_targets()
    widget.connect("drag_data_received", _media_files_drag_received)
    
def connect_bin_tree_view(treeview, move_files_to_bin_func):
    treeview.enable_model_drag_dest([MEDIA_FILES_DND_TARGET],
                                    Gdk.DragAction.DEFAULT)
                                         
    treeview.connect("drag_data_received", _bin_drag_data_received, move_files_to_bin_func)

def connect_effects_select_tree_view(tree_view):
    tree_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK,
                                       [EFFECTS_DND_TARGET], 
                                       Gdk.DragAction.COPY)
    tree_view.connect("drag_data_get", _effects_drag_data_get)
  
def connect_video_monitor(widget):
    widget.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP,
                         [MEDIA_FILES_DND_TARGET], 
                         Gdk.DragAction.COPY)

    widget.connect("drag_drop", _on_monitor_drop)
    widget.connect("drag_data_get", _save_monitor_media)
    
    widget.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                           [MEDIA_FILES_DND_TARGET], 
                           Gdk.DragAction.COPY)
    
    widget.drag_source_set_icon_pixbuf(clip_icon)

def connect_tline(widget, do_effect_drop_func, do_media_drop_func):
    widget.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP,
                         [MEDIA_FILES_DND_TARGET, EFFECTS_DND_TARGET, CLIPS_DND_TARGET], 
                         Gdk.DragAction.COPY)
    widget.connect("drag_drop", _on_tline_drop, do_effect_drop_func, do_media_drop_func)
    
def connect_range_log(treeview, range_log_drop_on_monitor_callback):
    global range_log_drop_on_monitor
    range_log_drop_on_monitor = range_log_drop_on_monitor_callback
    
    treeview.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                           [CLIPS_DND_TARGET, MEDIA_FILES_DND_TARGET], 
                           Gdk.DragAction.COPY)
    treeview.connect("drag_data_get", _range_log_drag_data_get)
    treeview.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.DROP,
                             [RANGE_DND_TARGET], 
                             Gdk.DragAction.COPY)
    treeview.connect("drag_drop", _on_range_drop)
    treeview.drag_source_set_icon_pixbuf(clip_icon)
    
def start_tline_clips_out_drag(event, clips, widget):

    global tline_out_drag_context
    if tline_out_drag_context != None:
        return
    
    global drag_data
    drag_data = clips
    target_list = Gtk.TargetList.new([RANGE_DND_TARGET])
    tline_out_drag_context = widget.drag_begin_with_coordinates(target_list, Gdk.DragAction.COPY, 1, event, -1, -1)

def clear_tline_out_drag_context():
    global tline_out_drag_context
    tline_out_drag_context = None

# ------------------------------------------------- handlers for drag events
def _media_files_drag_data_get(widget, context, selection, target_id, timestamp):
    _save_media_panel_selection()

def _media_files_drag_received(widget, context, x, y, data, info, timestamp):
    uris = data.get_uris()
    files = []
    for uri in uris:
        try:
            uri_tuple = GLib.filename_from_uri(uri)
        except:
            continue
        uri, unused = uri_tuple
        if os.path.exists(uri) == True:
            if utils.is_media_file(uri) == True:
                files.append(uri)

    if len(files) == 0:
        return

    callbackbridge.projectaction_open_file_names(files)
    
def _range_log_drag_data_get(treeview, context, selection, target_id, timestamp):
    _save_treeview_selection(treeview)
    global drag_source
    drag_source = SOURCE_RANGE_LOG

def _effects_drag_data_get(treeview, context, selection, target_id, timestamp):
    _save_treeview_selection(treeview)
    global drag_source
    drag_source = SOURCE_EFFECTS_TREE
    context.finish(True, False, timestamp)
    
def _on_monitor_drop(widget, context, x, y, timestamp):
    context.finish(True, False, timestamp)
    if drag_data == None: # A user error drag from monitor to monitor
        return

    try:
        # Drag from media panel
        media_file = drag_data[0].media_file
        callbackbridge.updater_set_and_display_monitor_media_file(media_file)
        gui.pos_bar.widget.grab_focus()
    except:
        range_log_drop_on_monitor(max(drag_data[0].get_indices())) # drag_data is Gtk.TreePath

def _on_effect_stack_drop(widget, context, x, y, timestamp):
    context.finish(True, False, timestamp)
    
def _bin_drag_data_received(treeview, context, x, y, selection, info, etime, move_files_to_bin_func):
    try:
        bin_path, drop_pos = treeview.get_dest_row_at_pos(x, y)
    except:
        return # drop on empty

    media_files = []

    selected_media_objects = gui.media_list_view.selected_objects
    for media_object in selected_media_objects:
        media_files.append(media_object.media_file)
    move_files_to_bin_func(max(bin_path), media_files)

def _save_treeview_selection(treeview):
    treeselection = treeview.get_selection()
    (model, rows) = treeselection.get_selected_rows()
    global drag_data
    drag_data = rows

def _save_media_panel_selection():
    global drag_data, drag_source
    drag_data = gui.media_list_view.get_selected_media_objects_for_drag()
    drag_source = SOURCE_MEDIA_FILE

def _save_monitor_media(widget, context, selection, target_id, timestamp):
    media_file = editorstate.MONITOR_MEDIA_FILE()
    global drag_data, drag_source
    drag_data = media_file
    drag_source = SOURCE_MONITOR_WIDGET
    if media_file == None:
        return False

    return True

def _on_tline_drop(widget, context, x, y, timestamp, do_effect_drop_func, do_media_drop_func):
    if drag_data == None:
        context.finish(True, False, timestamp)
        return
    
    if drag_source == SOURCE_EFFECTS_TREE:
        do_effect_drop_func(x, y)
        gui.tline_canvas.widget.grab_focus()
    elif drag_source == SOURCE_MEDIA_FILE:
        do_media_drop_func(drag_data, x, y, True)
        gui.tline_canvas.widget.grab_focus()
    elif drag_source == SOURCE_MONITOR_WIDGET:
        if drag_data != None:
            do_media_drop_func(drag_data, x, y, True)
            gui.tline_canvas.widget.grab_focus()
        else:
            print("monitor_drop fail")
    elif drag_source == SOURCE_RANGE_LOG:
        callbackbridge.editevent_tline_range_item_drop(drag_data, x, y)
    else:
        print("_on_tline_drop failed to do anything")
    
    context.finish(True, False, timestamp)

def _on_range_drop(widget, context, x, y, timestamp):
    callbackbridge.medialog_clips_drop(drag_data)

    context.finish(True, False, timestamp)

    
