/**
 * @ignore
 *  auto scroll for drag object's container
 * @author yiminghe@gmail.com
 */
KISSY.add('dd/plugin/scroll', function (S, DD, Base, Node, DOM) {

    var DDM = DD.DDM,
        win = S.Env.host,
        SCROLL_EVENT = '.-ks-dd-scroll' + S.now(),
        RATE = [10, 10],
        ADJUST_DELAY = 100,
        DIFF = [20, 20];

    /**
     * @class KISSY.DD.Plugin.Scroll
     * Scroll plugin to make parent node scroll while dragging.
     */
    function Scroll() {
        Scroll.superclass.constructor.apply(this, arguments);
    }

    Scroll.ATTRS = {
        /**
         * node to be scrolled while dragging
         * @cfg {window|String|HTMLElement} node
         */
        /**
         * @ignore
         */
        node: {
            // value:window:不行,默认值一定是简单对象
            valueFn: function () {
                return Node.one(win);
            },
            setter: function (v) {
                return Node.one(v);
            }
        },
        /**
         * adjust velocity, larger faster
         * default [10,10]
         * @cfg {Number[]} rate
         */
        /**
         * @ignore
         */
        rate: {
            value: RATE
        },
        /**
         * the margin to make node scroll, easier to scroll for node if larger.
         * default  [20,20]
         * @cfg {number[]} diff
         */
        /**
         * @ignore
         */
        diff: {
            value: DIFF
        }
    };


    var isWin = S.isWindow;

    S.extend(Scroll, Base, {

        pluginId: 'dd/plugin/scroll',

        /**
         * Get container node region.
         * @private
         */
        getRegion: function (node) {
            if (isWin(node[0])) {
                return {
                    width: DOM.viewportWidth(),
                    height: DOM.viewportHeight()
                };
            } else {
                return {
                    width: node.outerWidth(),
                    height: node.outerHeight()
                };
            }
        },

        /**
         * Get container node offset.
         * @private
         */
        getOffset: function (node) {
            if (isWin(node[0])) {
                return {
                    left: DOM.scrollLeft(),
                    top: DOM.scrollTop()
                };
            } else {
                return node.offset();
            }
        },

        /**
         * Get container node scroll.
         * @private
         */
        getScroll: function (node) {
            return {
                left: node.scrollLeft(),
                top: node.scrollTop()
            };
        },

        /**
         * scroll container node.
         * @private
         */
        setScroll: function (node, r) {
            node.scrollLeft(r.left);
            node.scrollTop(r.top);
        },

        /**
         * make node not to scroll while this drag object is dragging
         * @param {KISSY.DD.Draggable} drag
         * @private
         */
        pluginDestructor: function (drag) {
            drag['detach'](SCROLL_EVENT);
        },

        /**
         * make node to scroll while this drag object is dragging
         * @param {KISSY.DD.Draggable} drag
         * @private
         */
        pluginInitializer: function (drag) {
            var self = this,
                node = self.get('node');

            var rate = self.get('rate'),
                diff = self.get('diff'),
                event,
            // 目前相对 container 的偏移,container 为 window 时,相对于 viewport
                dxy,
                timer = null;

            // fix https://github.com/kissyteam/kissy/issues/115
            // dragDelegate 时 可能一个 dragDelegate对应多个 scroll
            // check container
            function checkContainer() {
                if (isWin(node[0])) {
                    return 0;
                }
                // 判断 proxyNode,不对 dragNode 做大的改变
                var mousePos = drag.mousePos,
                    r = DDM.region(node);

                if (!DDM.inRegion(r, mousePos)) {
                    clearTimeout(timer);
                    timer = 0;
                    return 1;
                }
                return 0;
            }

            function dragging(ev) {
                // 给调用者的事件,框架不需要处理
                // fake 也表示该事件不是因为 mouseover 产生的
                if (ev.fake) {
                    return;
                }

                if (checkContainer()) {
                    return;
                }

                // 更新当前鼠标相对于拖节点的相对位置
                event = ev;
                dxy = S.clone(drag.mousePos);
                var offset = self.getOffset(node);
                dxy.left -= offset.left;
                dxy.top -= offset.top;
                if (!timer) {
                    checkAndScroll();
                }
            }

            function dragEnd() {
                clearTimeout(timer);
                timer = null;
            }

            drag.on('drag' + SCROLL_EVENT, dragging);

            drag.on('dragstart' + SCROLL_EVENT, function () {
                DDM.cacheWH(node);
            });

            drag.on('dragend' + SCROLL_EVENT, dragEnd);

            function checkAndScroll() {
                if (checkContainer()) {
                    return;
                }

                var r = self.getRegion(node),
                    nw = r.width,
                    nh = r.height,
                    scroll = self.getScroll(node),
                    origin = S.clone(scroll),
                    diffY = dxy.top - nh,
                    adjust = false;

                if (diffY >= -diff[1]) {
                    scroll.top += rate[1];
                    adjust = true;
                }

                var diffY2 = dxy.top;

                if (diffY2 <= diff[1]) {
                    scroll.top -= rate[1];
                    adjust = true;
                }

                var diffX = dxy.left - nw;

                if (diffX >= -diff[0]) {
                    scroll.left += rate[0];
                    adjust = true;
                }

                var diffX2 = dxy.left;

                if (diffX2 <= diff[0]) {
                    scroll.left -= rate[0];
                    adjust = true;
                }

                if (adjust) {
                    self.setScroll(node, scroll);
                    timer = setTimeout(checkAndScroll, ADJUST_DELAY);
                    // 不希望更新相对值,特别对于相对 window 时,相对值如果不真正拖放触发的 drag,是不变的,
                    // 不会因为程序 scroll 而改变相对值

                    // 调整事件,不需要 scroll 监控,达到预期结果:元素随容器的持续不断滚动而自动调整位置.
                    event.fake = true;
                    if (isWin(node[0])) {
                        // 当使 window 自动滚动时,也要使得拖放物体相对文档位置随 scroll 改变
                        // 而相对 node 容器时,只需 node 容器滚动,拖动物体相对文档位置不需要改变
                        scroll = self.getScroll(node);
                        event.left += scroll.left - origin.left;
                        event.top += scroll.top - origin.top;
                    }
                    // 容器滚动了,元素也要重新设置 left,top
                    if (drag.get('move')) {
                        drag.get('node').offset(event);
                    }
                    drag.fire('drag', event);
                } else {
                    timer = null;
                }
            }
        }
    });

    return Scroll;
}, {
    requires: ['dd/base', 'base', 'node', 'dom']
});