(function ($) {
    $.extend($.fn.datagrid.defaults, {
        dropAccept: "tr.datagrid-row",
        onBeforeDrag: function (row) {},
        onStartDrag: function (row) {},
        onStopDrag: function (row) {},
        onDragEnter: function (targetRow, sourceRow) {},
        onDragOver: function (targetRow, sourceRow) {},
        onDragLeave: function (targetRow, sourceRow) {},
        onBeforeDrop: function (targetRow, sourceRow, point) {},
        onDrop: function (targetRow, sourceRow, point) {}
    });
    $.extend($.fn.datagrid.methods, {
        _getRowIndexs: function (jq, row) {
            var dg = jq;
            var rows = $.isArray(row) ? row : [row];
            var indexs = $.map(rows, function (row) { return dg.datagrid("getRowIndex", row); });
            return $.grep(indexs, function (index) { return index >= 0; });
        },
        _deleteRows: function (jq, indexs) {
            return jq.each(function () {
                // sort desc
                indexs.sort(function (x, y) {
                    return parseInt(x) > parseInt(y) ? -1 : 1;
                });
                for (var i = 0; i < indexs.length; i++) {
                    $(this).datagrid('deleteRow', indexs[i]);
                }
            });
        },
        _restoreSelections: function (jq) {
            return jq.each(function () {
                var rows = $(this).datagrid("getRows");
                for (var i = 0; i < rows.length; i++) {
                    if (rows[i]._selected) {
                        $(this).datagrid("selectRow", i);
                        rows[i]._selected = undefined;
                    }
                }
            });
        }
    });

    var disabledDroppingRows = [];

    function enableDroppable(aa) {
        $.map(aa, function (row) {
            $(row).droppable('enable');
        });
    }

    $.extend($.fn.datagrid.methods, {
        resetDroppable: function (jq) {
            return jq.each(function () {
                var c = $(this).datagrid('getPanel')[0];
                var my = [];
                var left = [];
                for (var i = 0; i < disabledDroppingRows.length; i++) {
                    var t = disabledDroppingRows[i];
                    var p = $(t).closest('div.datagrid-wrap');
                    if (p.length && p[0] == c) {
                        my.push(t);
                    } else {
                        left.push(t);
                    }
                }
                disabledDroppingRows = left;
                enableDroppable(my);
            });
        },
        enableDnd: function (jq, index) {
            return jq.each(function () {
                var target = this;
                var state = $.data(this, "datagrid");
                var opts = state.options;

                var draggableOptions = {
                    disabled: false,
                    revert: true,
                    deltaX: 0,
                    deltaY: 0,
                    delay: 100,
                    cursor: "pointer",
                    proxy: function (source) {
                        var count = _datagridOf(source).datagrid("getSelections").length;
                        var p = $('<div class="window window-shadow" style="cursor:-webkit-grabbing;padding:1em 2em;z-index:' + $.fn.window.defaults.zIndex++ + '"></div>').appendTo("body");
                        p.html('<span style="font-weight:900">{message}</span>'.supplant({
                            message: count > 1
                                    ? window.R.string.STORAGE_MESSAGE_MOVE_PLURAL.supplant({0: count})
                                    : window.R.string.STORAGE_MESSAGE_MOVE_SINGULAR
                        }));
                        return p.hide();
                    },
                    onBeforeDrag: function (e) {
                        var table = _datagridOf(this);
                        if (opts.onBeforeDrag.call(table[0], getDraggingRows(this, table)) == false) {
                            return false;
                        }
                        if ($(e.target).parent().hasClass("datagrid-cell-check")) {
                            return false;
                        }
                        return e.which == 1;
                    },
                    onStartDrag: function () {
                        $(this).draggable("proxy").css({left: -10000, top: -10000});
                        var table = _datagridOf(this);
                        var rows = getDraggingRows(this, table);
                        setValid(rows, false);
                        state.draggingRow = rows;
                        opts.onStartDrag.call(table[0], rows);
                    },
                    onDrag: function (e) {
                        var data = e.data;
                        var proxy = $(this).draggable("proxy");
                        var dist = Math.sqrt(Math.pow(e.pageX - data.startX, 2) + Math.pow(e.pageY - data.startY, 2));

                        if (proxy && dist > 4) {
                            proxy.show();

                            var table = _datagridOf(this);
                            var tr = opts.finder.getTr(table[0], _indexOf(this), "body");
                            $.extend(data, {
                                startX: tr.offset().left,
                                startY: tr.offset().top,
                                offsetWidth: 0,
                                offsetHeight: 0
                            });
                        }

                        data.left = Math.max(0, Math.min(data.left, $(document.body).width() - (proxy ? proxy.outerWidth() : 0)));
                        data.top = Math.max(0, Math.min(data.top, $(document.body).height() - (proxy ? proxy.outerHeight() : 0)));
                    },
                    onEndDrag: function (e) {
                        var dd = $(this).data("draggable").droppables
                                .filter(function () {
                                            var dropObj = $(this);
                                            if (dropObj.droppable("options").disabled
                                                || (dropObj.hasClass("datagrid-row") && !dropObj.hasClass("datagrid-row-over"))) {
                                                return false;
                                            }
                                            var offset = dropObj.offset();
                                            return e.pageX > offset.left && e.pageX < offset.left + dropObj.outerWidth()
                                                   && e.pageY > offset.top && e.pageY < offset.top + dropObj.outerHeight();
                                        });
                        var trs = dd.filter(function () { return $(this).hasClass("datagrid-row"); });
                        if (trs.length) {
                            dd = trs;
                        }
                        $(this).data("draggable").droppables = dd;
                    },
                    onStopDrag: function () {
                        enableDroppable(disabledDroppingRows);
                        disabledDroppingRows = [];
                        setValid(state.draggingRow, true);

                        var table = _datagridOf(this);
                        opts.onStopDrag.call(table[0], state.draggingRow);
                    }
                };
                var droppableOptions = {
                    accept: opts.dropAccept,
                    onDragEnter: function (e, source) {
                        if ($(this).droppable("options").disabled) {
                            return;
                        }
                        var table = _datagridOf(this);
                        var tr = table.datagrid("options").finder.getTr(table[0], null, "highlight");

                        var rows = getDraggingRows(source, table);
                        var dropRow = getRow(this);
                        if (tr.length && dropRow) {
                            if (opts.onDragEnter.call(table[0], dropRow, rows) == false) {
                                tr.droppable("disable");
                                tr.each(function () {
                                    disabledDroppingRows.push(this);
                                });
                            }
                        }
                    },
                    onDragOver: function (e, source) {
                        if ($(this).droppable("options").disabled) {
                            return;
                        }
                        if ($.inArray(this, disabledDroppingRows) >= 0) {
                            return;
                        }
                        var table = _datagridOf(this);
                        var tr = table.datagrid("options").finder.getTr(table[0], null, "highlight");

                        if (tr.length && !_dropAllowed(tr)) {
                            return;
                        }

                        var rows = getDraggingRows(source, table);
                        var dropRow = getRow(this);

                        if (tr.length && dropRow) {
                            if (opts.onDragOver.call(table[0], dropRow, rows) == false) {
                                tr.droppable("disable")
                                        .each(function () {
                                                  disabledDroppingRows.push(this);
                                              });
                            }
                        }
                    },
                    onDragLeave: function (e, source) {
                        if ($(this).droppable("options").disabled) {
                            return;
                        }

                        var table = _datagridOf(this);
                        var rows = getDraggingRows(source, table);
                        var dropRow = getRow(this);
                        if (dropRow) {
                            opts.onDragLeave.call(table[0], dropRow, rows);
                        }
                    },
                    onDrop: function (e, source) {
                        if ($(this).droppable("options").disabled) {
                            return;
                        }
                        var table = _datagridOf(this);
                        var tr = table.datagrid("options").finder.getTr(table[0], null, "highlight");

                        var rows = getDraggingRows(source, table);
                        var dRow = null;
                        if (tr.length) {
                            if (!_dropAllowed(tr)) {
                                return;
                            }
                            dRow = getRow(tr);
                        }

                        if (opts.onBeforeDrop.call(table[0], dRow, rows) == false) {
                            return;
                        }
                        update.call(this);
                        opts.onDrop.call(table[0], dRow, rows);

                        function update() {
                            var dg = _datagridOf(source);
                            var indexs = dg.datagrid("_getRowIndexs", rows);
                            dg.datagrid("_deleteRows", indexs);
                            table.datagrid("_restoreSelections");
                        }
                    }
                };

                var trs = index != undefined
                          ? opts.finder.getTr(this, index)
                          : opts.finder.getTr(this, 0, "allbody");
                trs.draggable(draggableOptions);
                trs.droppable(droppableOptions);
                $(this).data("datagrid").dc.view.droppable(droppableOptions).droppable("enable");

                function getRow(tr) {
                    if (!$(tr).hasClass("datagrid-row")) {
                        return null;
                    }
                    var table = _datagridOf(tr);
                    return table.datagrid("options").finder.getRow(table[0], $(tr));
                }

                function getDraggingRows(tr, table) {
                    if (!$(tr).hasClass("datagrid-row")) {
                        return [];
                    }

                    var rows = table.datagrid("getRows");
                    for (var i = 0; i < rows.length; i++) {
                        rows[i]._selected = undefined;
                    }
                    if ($(tr).hasClass("datagrid-row-selected")) {
                        rows = table.datagrid("getSelections");
                        $.map(rows, function (row) { row._selected = true; });
                        return rows;
                    }
                    var row = table.datagrid("options").finder.getRow(table[0], $(tr));
                    row._selected = $(tr).hasClass("datagrid-row-selected");
                    return [row];
                }

                function _datagridOf(el) {
                    return $(el).closest("div.datagrid-view").children(".datagrid-f");
                }

                function _indexOf(el) {
                    var attr = $(el).attr("datagrid-row-index");
                    return attr ? parseInt(attr) : $(el).attr("node-id");
                }

                function _dropAllowed(el) {
                    var opts = $(el).droppable("options");
                    return !(opts.disabled || opts.accept == "no-accept");
                }

                function setValid(rows, valid) {
                    var accept = valid ? opts.dropAccept : "no-accept";
                    $.map($.isArray(rows) ? rows : [rows], function (row) {
                        var index = $(target).datagrid("getRowIndex", row);
                        opts.finder.getTr(target, index).droppable({accept: accept});
                    });
                }
            });
        }

    });
})(jQuery);
