define(function(require, exports, module) {
    var kity = require('../core/kity');
    var utils = require('../core/utils');
    var MinderNode = require('../core/node');
    var Command = require('../core/command');
    var Module = require('../core/module');

    // 矩形的变形动画定义
    var MoveToParentCommand = kity.createClass('MoveToParentCommand', {
        base: Command,
        execute: function(minder, nodes, parent) {
            var node;
            for (var i = 0; i < nodes.length; i++) {
                node = nodes[i];
                if (node.parent) {
                    node.parent.removeChild(node);
                    parent.appendChild(node);
                    node.render();
                }
            }
            parent.expand();
            minder.select(nodes, true);
        }
    });

    var DropHinter = kity.createClass('DropHinter', {
        base: kity.Group,

        constructor: function() {
            this.callBase();
            this.rect = new kity.Rect();
            this.addShape(this.rect);
        },

        render: function(target) {
            this.setVisible(!!target);
            if (target) {
                this.rect
                    .setBox(target.getLayoutBox())
                    .setRadius(target.getStyle('radius') || 0)
                    .stroke(
                        target.getStyle('drop-hint-color') || 'yellow',
                        target.getStyle('drop-hint-width') || 2
                );
                this.bringTop();
            }
        }
    });

    var OrderHinter = kity.createClass('OrderHinter', {
        base: kity.Group,

        constructor: function() {
            this.callBase();
            this.area = new kity.Rect();
            this.path = new kity.Path();
            this.addShapes([this.area, this.path]);
        },

        render: function(hint) {
            this.setVisible(!!hint);
            if (hint) {
                this.area.setBox(hint.area);
                this.area.fill(hint.node.getStyle('order-hint-area-color') || 'rgba(0, 255, 0, .5)');
                this.path.setPathData(hint.path);
                this.path.stroke(
                    hint.node.getStyle('order-hint-path-color') || '#0f0',
                    hint.node.getStyle('order-hint-path-width') || 1);
            }
        }
    });

    // 对拖动对象的一个替代盒子，控制整个拖放的逻辑，包括：
    //    1. 从节点列表计算出拖动部分
    //    2. 计算可以 drop 的节点，产生 drop 交互提示
    var TreeDragger = kity.createClass('TreeDragger', {

        constructor: function(minder) {
            this._minder = minder;
            this._dropHinter = new DropHinter();
            this._orderHinter = new OrderHinter();
            minder.getRenderContainer().addShapes([this._dropHinter, this._orderHinter]);
        },

        dragStart: function(position) {
            // 只记录开始位置，不马上开启拖放模式
            // 这个位置同时是拖放范围收缩时的焦点位置（中心）
            this._startPosition = position;
        },

        dragMove: function(position) {
            // 启动拖放模式需要最小的移动距离
            var DRAG_MOVE_THRESHOLD = 10;

            if (!this._startPosition) return;

            var movement = kity.Vector.fromPoints(this._dragPosition || this._startPosition, position);
            var minder = this._minder;

            this._dragPosition = position;

            if (!this._dragMode) {
                // 判断拖放模式是否该启动
                if (kity.Vector.fromPoints(this._dragPosition, this._startPosition).length() < DRAG_MOVE_THRESHOLD) {
                    return;
                }
                if (!this._enterDragMode()) {
                    return;
                }
            }

            for (var i = 0; i < this._dragSources.length; i++) {
                this._dragSources[i].setLayoutOffset(this._dragSources[i].getLayoutOffset().offset(movement));
                minder.applyLayoutResult(this._dragSources[i]);
            }

            if (!this._dropTest()) {
                this._orderTest();
            } else {
                this._renderOrderHint(this._orderSucceedHint = null);
            }
        },

        dragEnd: function() {
            this._startPosition = null;
            this._dragPosition = null;

            if (!this._dragMode) {
                return;
            }

            this._fadeDragSources(1);

            if (this._dropSucceedTarget) {

                this._dragSources.forEach(function(source) {
                    source.setLayoutOffset(null);
                });

                this._minder.layout(-1);

                this._minder.execCommand('movetoparent', this._dragSources, this._dropSucceedTarget);

            } else if (this._orderSucceedHint) {

                var hint = this._orderSucceedHint;
                var index = hint.node.getIndex();

                var sourceIndexes = this._dragSources.map(function(source) {
                    // 顺便干掉布局偏移
                    source.setLayoutOffset(null);
                    return source.getIndex();
                });

                var maxIndex = Math.max.apply(Math, sourceIndexes);
                var minIndex = Math.min.apply(Math, sourceIndexes);

                if (index < minIndex && hint.type == 'down') index++;
                if (index > maxIndex && hint.type == 'up') index--;

                hint.node.setLayoutOffset(null);

                this._minder.execCommand('arrange', index);
                this._renderOrderHint(null);
            } else {
                this._minder.fire('savescene');
            }
            this._minder.layout(300);
            this._leaveDragMode();
            this._minder.fire('contentchange');
        },

        // 进入拖放模式：
        //    1. 计算拖放源和允许的拖放目标
        //    2. 标记已启动
        _enterDragMode: function() {
            this._calcDragSources();
            if (!this._dragSources.length) {
                this._startPosition = null;
                return false;
            }
            this._fadeDragSources(0.5);
            this._calcDropTargets();
            this._calcOrderHints();
            this._dragMode = true;
            this._minder.setStatus('dragtree');
            return true;
        },

        // 从选中的节点计算拖放源
        //    并不是所有选中的节点都作为拖放源，如果选中节点中存在 A 和 B，
        //    并且 A 是 B 的祖先，则 B 不作为拖放源
        //
        //    计算过程：
        //       1. 将节点按照树高排序，排序后只可能是前面节点是后面节点的祖先
        //       2. 从后往前枚举排序的结果，如果发现枚举目标之前存在其祖先，
        //          则排除枚举目标作为拖放源，否则加入拖放源
        _calcDragSources: function() {
            this._dragSources = this._minder.getSelectedAncestors();
        },

        _fadeDragSources: function(opacity) {
            var minder = this._minder;
            this._dragSources.forEach(function(source) {
                source.getRenderContainer().setOpacity(opacity, 200);
                source.traverse(function(node) {
                    if (opacity < 1) {
                        minder.detachNode(node);
                    } else {
                        minder.attachNode(node);
                    }
                }, true);
            });
        },


        // 计算拖放目标可以释放的节点列表（释放意味着成为其子树），存在这条限制规则：
        //    - 不能拖放到拖放目标的子树上（允许拖放到自身，因为多选的情况下可以把其它节点加入）
        //
        //    1. 加入当前节点（初始为根节点）到允许列表
        //    2. 对于当前节点的每一个子节点：
        //       (1) 如果是拖放目标的其中一个节点，忽略（整棵子树被剪枝）
        //       (2) 如果不是拖放目标之一，以当前子节点为当前节点，回到 1 计算
        //    3. 返回允许列表
        //
        _calcDropTargets: function() {

            function findAvailableParents(nodes, root) {
                var availables = [],
                    i;
                availables.push(root);
                root.getChildren().forEach(function(test) {
                    for (i = 0; i < nodes.length; i++) {
                        if (nodes[i] == test) return;
                    }
                    availables = availables.concat(findAvailableParents(nodes, test));
                });
                return availables;
            }

            this._dropTargets = findAvailableParents(this._dragSources, this._minder.getRoot());
            this._dropTargetBoxes = this._dropTargets.map(function(source) {
                return source.getLayoutBox();
            });
        },

        _calcOrderHints: function() {
            var sources = this._dragSources;
            var ancestor = MinderNode.getCommonAncestor(sources);

            // 只有一个元素选中，公共祖先是其父
            if (ancestor == sources[0]) ancestor = sources[0].parent;

            if (sources.length === 0 || ancestor != sources[0].parent) {
                this._orderHints = [];
                return;
            }

            var siblings = ancestor.children;

            this._orderHints = siblings.reduce(function(hint, sibling) {
                if (sources.indexOf(sibling) == -1) {
                    hint = hint.concat(sibling.getOrderHint());
                }
                return hint;
            }, []);
        },

        _leaveDragMode: function() {
            this._dragMode = false;
            this._dropSucceedTarget = null;
            this._orderSucceedHint = null;
            this._renderDropHint(null);
            this._renderOrderHint(null);
            this._minder.rollbackStatus();
        },

        _drawForDragMode: function() {
            this._text.setContent(this._dragSources.length + ' items');
            this._text.setPosition(this._startPosition.x, this._startPosition.y + 5);
            this._minder.getRenderContainer().addShape(this);
        },

        /**
         * 通过 judge 函数判断 targetBox 和 sourceBox 的位置交叉关系
         * @param targets -- 目标节点
         * @param targetBoxMapper -- 目标节点与对应 Box 的映射关系
         * @param judge -- 判断函数
         * @returns {*}
         * @private
         */
        _boxTest: function(targets, targetBoxMapper, judge) {
            var sourceBoxes = this._dragSources.map(function(source) {
                return source.getLayoutBox();
            });

            var i, j, target, sourceBox, targetBox;

            judge = judge || function(intersectBox, sourceBox, targetBox) {
                return intersectBox && !intersectBox.isEmpty();
            };

            for (i = 0; i < targets.length; i++) {

                target = targets[i];
                targetBox = targetBoxMapper.call(this, target, i);

                for (j = 0; j < sourceBoxes.length; j++) {
                    sourceBox = sourceBoxes[j];

                    var intersectBox = sourceBox.intersect(targetBox);
                    if (judge(intersectBox, sourceBox, targetBox)) {
                        return target;
                    }
                }
            }

            return null;
        },

        _dropTest: function() {
            this._dropSucceedTarget = this._boxTest(this._dropTargets, function(target, i) {
                return this._dropTargetBoxes[i];
            }, function(intersectBox, sourceBox, targetBox) {
                function area(box) {
                    return box.width * box.height;
                }
                if (!intersectBox) return false;
                /*
                * Added by zhangbobell, 2015.9.8
                *
                * 增加了下面一行判断，修复了循环比较中 targetBox 为折叠节点时，intersetBox 面积为 0，
                * 而 targetBox 的 width 和 height 均为 0
                * 此时造成了满足以下的第二个条件而返回 true
                * */
                if (!area(intersectBox)) return false;
                // 面积判断，交叉面积大于其中的一半
                if (area(intersectBox) > 0.5 * Math.min(area(sourceBox), area(targetBox))) return true;
                // 有一个边完全重合的情况，也认为两个是交叉的
                if (intersectBox.width + 1 >= Math.min(sourceBox.width, targetBox.width)) return true;
                if (intersectBox.height + 1 >= Math.min(sourceBox.height, targetBox.height)) return true;
                return false;
            });
            this._renderDropHint(this._dropSucceedTarget);
            return !!this._dropSucceedTarget;
        },

        _orderTest: function() {
            this._orderSucceedHint = this._boxTest(this._orderHints, function(hint) {
                return hint.area;
            });
            this._renderOrderHint(this._orderSucceedHint);
            return !!this._orderSucceedHint;
        },

        _renderDropHint: function(target) {
            this._dropHinter.render(target);
        },

        _renderOrderHint: function(hint) {
            this._orderHinter.render(hint);
        },
        preventDragMove: function() {
            this._startPosition = null;
        }
    });

    Module.register('DragTree', function() {
        var dragger;

        return {
            init: function() {
                dragger = new TreeDragger(this);
                window.addEventListener('mouseup', function() {
                    dragger.dragEnd();
                });
            },
            events: {
                'normal.mousedown inputready.mousedown': function(e) {
                    // 单选中根节点也不触发拖拽
                    if (e.originEvent.button) return;
                    if (e.getTargetNode() && e.getTargetNode() != this.getRoot()) {
                        dragger.dragStart(e.getPosition());
                    }
                },
                'normal.mousemove dragtree.mousemove': function(e) {
                    dragger.dragMove(e.getPosition());
                },
                'normal.mouseup dragtree.beforemouseup': function(e) {
                    dragger.dragEnd();
                    //e.stopPropagation();
                    e.preventDefault();
                },
                'statuschange': function(e) {
                    if (e.lastStatus == 'textedit' && e.currentStatus == 'normal') {
                        dragger.preventDragMove();
                    }
                }
            },
            commands: {
                'movetoparent': MoveToParentCommand
            }
        };
    });
});