/**
 * A gesture recognizer for swipe events
 */
Ext.define('Ext.event.gesture.Swipe', {
    extend: 'Ext.event.gesture.SingleTouch',
 
    priority: 500,
 
    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
     */
 
    inheritableStatics: {
        /**
         * @private
         * @static
         * @inheritable
         */
        MAX_OFFSET_EXCEEDED: 'Max Offset Exceeded',
        /**
         * @private
         * @static
         * @inheritable
         */
        MAX_DURATION_EXCEEDED: 'Max Duration Exceeded',
        /**
         * @private
         * @static
         * @inheritable
         */
        DISTANCE_NOT_ENOUGH: 'Distance Not Enough'
    },
 
    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },
 
    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }
 
        var touch = e.changedTouches[0];
 
        this.startTime = e.time;
 
        this.isHorizontal = true;
        this.isVertical = true;
 
        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },
 
    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(- this.startX),
            absDeltaY = Math.abs(- this.startY),
            duration = e.time - this.startTime,
            minDistance = this.getMinDistance(),
            time = e.time,
            direction, distance;
 
        if (time - this.startTime > this.getMaxDuration()) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }
 
        if (this.isHorizontal && absDeltaY > this.getMaxOffset()) {
            this.isHorizontal = false;
        }
 
        if (this.isVertical && absDeltaX > this.getMaxOffset()) {
            this.isVertical = false;
        }
 
        if (!this.isVertical || !this.isHorizontal) {
            if (this.isHorizontal && absDeltaX < minDistance) {
                direction = (deltaX < 0) ? 'left' : 'right';
                distance = absDeltaX;
            }
            else if (this.isVertical && absDeltaY < minDistance) {
                direction = (deltaY < 0) ? 'up' : 'down';
                distance = absDeltaY;
            }
        }
 
        if (direction && !this.started) {
            this.started = true;
 
            this.fire('swipestart', e, {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
 
        if (!this.isHorizontal && !this.isVertical) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },
 
    onTouchEnd: function(e) {
        if (this.onTouchMove(e) === false) {
            return false;
        }
 
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(deltaX),
            absDeltaY = Math.abs(deltaY),
            minDistance = this.getMinDistance(),
            duration = e.time - this.startTime,
            direction, distance;
 
        if (this.isVertical && absDeltaY < minDistance) {
            this.isVertical = false;
        }
 
        if (this.isHorizontal && absDeltaX < minDistance) {
            this.isHorizontal = false;
        }
 
        if (this.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = absDeltaX;
        }
        else if (this.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = absDeltaY;
        }
        else {
            return this.fail(this.self.DISTANCE_NOT_ENOUGH);
        }
 
        this.started = false;
 
        this.fire('swipe', e, {
            touch: touch,
            direction: direction,
            distance: distance,
            duration: duration
        });
    },
 
    onTouchCancel: function(e) {
        this.fire('swipecancel', e);
        return false;
    },
 
    reset: function() {
        var me = this;
 
        me.startTime = me.isHorizontal = me.isVertical = me.startX = me.startY = null;
    }
}, function(Swipe) {
    var gestures = Ext.manifest.gestures;
    Swipe.instance = new Swipe(gestures && gestures.swipe);
});