/**
 * A gesture recognizer for swipe events
 */
Ext.define('Ext.event.gesture.Swipe', {
    extend: 'Ext.event.gesture.SingleTouch',
 
    priority: 600,
 
    handledEvents: ['swipestart', 'swipe', 'swipecancel'],
 
    /**
     * @member Ext.dom.Element
     * @event swipe
     * Fires when there is a swipe
     * When listening to this, ensure you know about the {@link Ext.event.Event#direction} property
     * in the `event` object.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
     */
 
    /**
     * @property {Number} direction
     * The direction of the swipe. Available options are:
     *
     * - up
     * - down
     * - left
     * - right
     *
     * **This is only available when the event type is `swipe`**
     * @member Ext.event.Event
     */
 
    /**
     * @property {Number} duration
     * The duration of the swipe.
     *
     * **This is only available when the event type is `swipe`**
     * @member Ext.event.Event
     */
 
    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },
 
    onTouchStart: function(e) {
        var me = this,
            ret = me.callParent([e]),
            touch;
 
        if (ret !== false) {
            touch = e.changedTouches[0];
 
            me.startTime = e.time;
 
            me.isHorizontal = true;
            me.isVertical = true;
 
            me.startX = touch.pageX;
            me.startY = touch.pageY;
        }
 
        return ret;
    },
 
    onTouchMove: function(e) {
        var me = this,
            touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - me.startX,
            deltaY = y - me.startY,
            absDeltaX = Math.abs(- me.startX),
            absDeltaY = Math.abs(- me.startY),
            duration = e.time - me.startTime,
            minDistance, direction, distance;
 
        // If delta is 0 on both axes that's not swipe
        if ((absDeltaX === 0 && absDeltaY === 0) || (duration > me.getMaxDuration())) {
            return me.cancel(e);
        }
 
        if (me.isHorizontal && absDeltaY > me.getMaxOffset()) {
            me.isHorizontal = false;
        }
 
        if (me.isVertical && absDeltaX > me.getMaxOffset()) {
            me.isVertical = false;
        }
 
        if (!me.isVertical || !me.isHorizontal) {
            minDistance = me.getMinDistance();
 
            if (me.isHorizontal && absDeltaX < minDistance) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            }
            else if (me.isVertical && absDeltaY < minDistance) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
        }
 
        if (!me.isHorizontal && !me.isVertical) {
            return me.cancel(e);
        }
 
        if (direction && !me.isStarted) {
            me.isStarted = true;
 
            me.fire('swipestart', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
    },
 
    onTouchEnd: function(e) {
        var me = this,
            touch, x, y, deltaX, deltaY, absDeltaX, absDeltaY, minDistance, duration,
            direction, distance;
 
        if (me.onTouchMove(e) !== false) {
            touch = e.changedTouches[0];
            x = touch.pageX;
            y = touch.pageY;
            deltaX = x - me.startX;
            deltaY = y - me.startY;
            absDeltaX = Math.abs(deltaX);
            absDeltaY = Math.abs(deltaY);
            minDistance = me.getMinDistance();
            duration = e.time - me.startTime;
 
            if (me.isVertical && absDeltaY < minDistance) {
                me.isVertical = false;
            }
 
            if (me.isHorizontal && absDeltaX < minDistance) {
                me.isHorizontal = false;
            }
 
            if (me.isHorizontal) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            }
            else if (me.isVertical) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
 
            me.fire('swipe', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
 
        return this.callParent([e]);
    },
 
    onCancel: function(e) {
        this.fire('swipecancel', e, null, true);
    },
 
    reset: function() {
        var me = this;
 
        me.startTime = me.isHorizontal = me.isVertical = me.startX = me.startY = null;
 
        return me.callParent();
    }
}, function(Swipe) {
    var gestures = Ext.manifest.gestures;
 
    Swipe.instance = new Swipe(gestures && gestures.swipe);
});