import { Injectable, NgZone } from '@angular/core';
import { Activator } from './activator';
import { App } from '../app/app';
import { Config } from '../../config/config';
import { hasPointerMoved, pointerCoord } from '../../util/dom';
import { RippleActivator } from './ripple';
export var TapClick = (function () {
    function TapClick(config, app, zone) {
        this.app = app;
        this.lastTouch = 0;
        this.disableClick = 0;
        this.lastActivated = 0;
        var self = this;
        if (config.get('activator') === 'ripple') {
            self.activator = new RippleActivator(app, config);
        }
        else if (config.get('activator') === 'highlight') {
            self.activator = new Activator(app, config);
        }
        self.usePolyfill = (config.get('tapPolyfill') === true);
        zone.runOutsideAngular(function () {
            addListener('click', self.click.bind(self), true);
            addListener('touchstart', self.touchStart.bind(self));
            addListener('touchend', self.touchEnd.bind(self));
            addListener('touchcancel', self.pointerCancel.bind(self));
            addListener('mousedown', self.mouseDown.bind(self), true);
            addListener('mouseup', self.mouseUp.bind(self), true);
        });
        self.pointerMove = function (ev) {
            if (hasPointerMoved(POINTER_MOVE_UNTIL_CANCEL, self.startCoord, pointerCoord(ev))) {
                self.pointerCancel(ev);
            }
        };
    }
    TapClick.prototype.touchStart = function (ev) {
        this.lastTouch = Date.now();
        this.pointerStart(ev);
    };
    TapClick.prototype.touchEnd = function (ev) {
        this.lastTouch = Date.now();
        if (this.usePolyfill && this.startCoord && this.app.isEnabled()) {
            var endCoord = pointerCoord(ev);
            if (!hasPointerMoved(POINTER_TOLERANCE, this.startCoord, endCoord)) {
                this.disableClick = this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT;
                if (this.app.isScrolling()) {
                    console.debug('click from touch prevented by scrolling ' + Date.now());
                }
                else {
                    console.debug('create click from touch ' + Date.now());
                    var clickEvent = document.createEvent('MouseEvents');
                    clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
                    clickEvent.isIonicTap = true;
                    ev.target.dispatchEvent(clickEvent);
                }
            }
        }
        this.pointerEnd(ev);
    };
    TapClick.prototype.mouseDown = function (ev) {
        if (this.isDisabledNativeClick()) {
            console.debug('mouseDown prevent ' + ev.target.tagName + ' ' + Date.now());
            ev.stopPropagation();
        }
        else if (this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT < Date.now()) {
            this.pointerStart(ev);
        }
    };
    TapClick.prototype.mouseUp = function (ev) {
        if (this.isDisabledNativeClick()) {
            console.debug('mouseUp prevent ' + ev.target.tagName + ' ' + Date.now());
            ev.preventDefault();
            ev.stopPropagation();
        }
        if (this.lastTouch + DISABLE_NATIVE_CLICK_AMOUNT < Date.now()) {
            this.pointerEnd(ev);
        }
    };
    TapClick.prototype.pointerStart = function (ev) {
        var activatableEle = getActivatableTarget(ev.target);
        if (activatableEle) {
            this.startCoord = pointerCoord(ev);
            var now = Date.now();
            if (this.lastActivated + 150 < now && !this.app.isScrolling()) {
                this.activator && this.activator.downAction(ev, activatableEle, this.startCoord);
                this.lastActivated = now;
            }
            this.moveListeners(true);
        }
        else {
            this.startCoord = null;
        }
    };
    TapClick.prototype.pointerEnd = function (ev) {
        if (this.startCoord && this.activator) {
            var activatableEle = getActivatableTarget(ev.target);
            if (activatableEle) {
                this.activator.upAction(ev, activatableEle, this.startCoord);
            }
        }
        this.moveListeners(false);
    };
    TapClick.prototype.pointerCancel = function (ev) {
        console.debug('pointerCancel from ' + ev.type + ' ' + Date.now());
        this.activator && this.activator.clearState();
        this.moveListeners(false);
    };
    TapClick.prototype.moveListeners = function (shouldAdd) {
        removeListener(this.usePolyfill ? 'touchmove' : 'mousemove', this.pointerMove);
        if (shouldAdd) {
            addListener(this.usePolyfill ? 'touchmove' : 'mousemove', this.pointerMove);
        }
    };
    TapClick.prototype.click = function (ev) {
        var preventReason = null;
        if (!this.app.isEnabled()) {
            preventReason = 'appDisabled';
        }
        else if (!ev.isIonicTap && this.isDisabledNativeClick()) {
            preventReason = 'nativeClick';
        }
        if (preventReason !== null) {
            console.debug('click prevent ' + preventReason + ' ' + Date.now());
            ev.preventDefault();
            ev.stopPropagation();
        }
    };
    TapClick.prototype.isDisabledNativeClick = function () {
        return this.disableClick > Date.now();
    };
    TapClick.decorators = [
        { type: Injectable },
    ];
    TapClick.ctorParameters = [
        { type: Config, },
        { type: App, },
        { type: NgZone, },
    ];
    return TapClick;
}());
function getActivatableTarget(ele) {
    var targetEle = ele;
    for (var x = 0; x < 4; x++) {
        if (!targetEle)
            break;
        if (isActivatable(targetEle))
            return targetEle;
        targetEle = targetEle.parentElement;
    }
    return null;
}
export var isActivatable = function (ele) {
    if (ACTIVATABLE_ELEMENTS.test(ele.tagName)) {
        return true;
    }
    var attributes = ele.attributes;
    for (var i = 0, l = attributes.length; i < l; i++) {
        if (ACTIVATABLE_ATTRIBUTES.test(attributes[i].name)) {
            return true;
        }
    }
    return false;
};
function addListener(type, listener, useCapture) {
    document.addEventListener(type, listener, useCapture);
}
function removeListener(type, listener) {
    document.removeEventListener(type, listener);
}
var ACTIVATABLE_ELEMENTS = /^(A|BUTTON)$/;
var ACTIVATABLE_ATTRIBUTES = /tappable|button/i;
var POINTER_TOLERANCE = 4;
var POINTER_MOVE_UNTIL_CANCEL = 10;
var DISABLE_NATIVE_CLICK_AMOUNT = 2500;
export function setupTapClick(config, app, zone) {
    return function () {
        return new TapClick(config, app, zone);
    };
}
