import { Component, ElementRef, EventEmitter, Input, HostListener, NgZone, Output, Renderer, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { CSS, cancelRaf, pointerCoord, nativeRaf } from '../../util/dom';
import { clamp, isNumber, isPresent, isString } from '../../util/util';
import { Config } from '../../config/config';
import { Key } from '../../util/key';
import { NavParams } from '../../navigation/nav-params';
import { Haptic } from '../../util/haptic';
import { UIEventManager } from '../../util/ui-event-manager';
import { ViewController } from '../../navigation/view-controller';
import { NativeRafDebouncer } from '../../util/debouncer';
import { GestureController, BLOCK_ALL } from '../../gestures/gesture-controller';
export var PickerColumnCmp = (function () {
    function PickerColumnCmp(config, elementRef, _sanitizer, _zone, _haptic) {
        this.elementRef = elementRef;
        this._sanitizer = _sanitizer;
        this._zone = _zone;
        this._haptic = _haptic;
        this.y = 0;
        this.pos = [];
        this.startY = null;
        this.debouncer = new NativeRafDebouncer();
        this.events = new UIEventManager(false);
        this.ionChange = new EventEmitter();
        this.rotateFactor = config.getNumber('pickerRotateFactor', 0);
        this.scaleFactor = config.getNumber('pickerScaleFactor', 1);
        this.decelerateFunc = this.decelerate.bind(this);
    }
    PickerColumnCmp.prototype.ngAfterViewInit = function () {
        var colEle = this.colEle.nativeElement;
        this.colHeight = colEle.clientHeight;
        this.optHeight = (colEle.firstElementChild ? colEle.firstElementChild.clientHeight : 0);
        this.setSelected(this.col.selectedIndex, 0);
        this.events.pointerEvents({
            elementRef: this.elementRef,
            pointerDown: this.pointerStart.bind(this),
            pointerMove: this.pointerMove.bind(this),
            pointerUp: this.pointerEnd.bind(this),
            capture: true
        });
    };
    PickerColumnCmp.prototype.ngOnDestroy = function () {
        this.events.unlistenAll();
    };
    PickerColumnCmp.prototype.pointerStart = function (ev) {
        var _this = this;
        (void 0);
        this._haptic.gestureSelectionStart();
        ev.preventDefault();
        this.debouncer.debounce(function () {
            if (_this.rafId) {
                cancelRaf(_this.rafId);
                _this.rafId = null;
            }
            _this.startY = pointerCoord(ev).y;
            _this.velocity = 0;
            _this.pos.length = 0;
            _this.pos.push(_this.startY, Date.now());
            var options = _this.col.options;
            var minY = (options.length - 1);
            var maxY = 0;
            for (var i = 0; i < options.length; i++) {
                if (!options[i].disabled) {
                    minY = Math.min(minY, i);
                    maxY = Math.max(maxY, i);
                }
            }
            _this.minY = (minY * _this.optHeight * -1);
            _this.maxY = (maxY * _this.optHeight * -1);
        });
        return true;
    };
    PickerColumnCmp.prototype.pointerMove = function (ev) {
        var _this = this;
        ev.preventDefault();
        ev.stopPropagation();
        this.debouncer.debounce(function () {
            if (_this.startY === null) {
                return;
            }
            var currentY = pointerCoord(ev).y;
            _this.pos.push(currentY, Date.now());
            var y = _this.y + (currentY - _this.startY);
            if (y > _this.minY) {
                y = Math.pow(y, 0.8);
                _this.bounceFrom = y;
            }
            else if (y < _this.maxY) {
                y += Math.pow(_this.maxY - y, 0.9);
                _this.bounceFrom = y;
            }
            else {
                _this.bounceFrom = 0;
            }
            _this.update(y, 0, false, false);
            var currentIndex = Math.max(Math.abs(Math.round(y / _this.optHeight)), 0);
            if (currentIndex !== _this.lastTempIndex) {
                _this._haptic.gestureSelectionChanged();
                _this.lastTempIndex = currentIndex;
            }
        });
    };
    PickerColumnCmp.prototype.pointerEnd = function (ev) {
        ev.preventDefault();
        this.debouncer.cancel();
        if (this.startY === null) {
            return;
        }
        (void 0);
        this.velocity = 0;
        if (this.bounceFrom > 0) {
            this.update(this.minY, 100, true, true);
            return;
        }
        else if (this.bounceFrom < 0) {
            this.update(this.maxY, 100, true, true);
            return;
        }
        var endY = pointerCoord(ev).y;
        this.pos.push(endY, Date.now());
        var endPos = (this.pos.length - 1);
        var startPos = endPos;
        var timeRange = (Date.now() - 100);
        for (var i = endPos; i > 0 && this.pos[i] > timeRange; i -= 2) {
            startPos = i;
        }
        if (startPos !== endPos) {
            var timeOffset = (this.pos[endPos] - this.pos[startPos]);
            var movedTop = (this.pos[startPos - 1] - this.pos[endPos - 1]);
            this.velocity = ((movedTop / timeOffset) * FRAME_MS);
        }
        if (Math.abs(endY - this.startY) > 3) {
            var y = this.y + (endY - this.startY);
            this.update(y, 0, true, true);
        }
        this.startY = null;
        this.decelerate();
    };
    PickerColumnCmp.prototype.decelerate = function () {
        var y = 0;
        if (isNaN(this.y) || !this.optHeight) {
            this.update(y, 0, true, true);
            this._haptic.gestureSelectionEnd();
        }
        else if (Math.abs(this.velocity) > 0) {
            this.velocity *= DECELERATION_FRICTION;
            this.velocity = (this.velocity > 0)
                ? Math.max(this.velocity, 1)
                : Math.min(this.velocity, -1);
            y = Math.round(this.y - this.velocity);
            if (y > this.minY) {
                y = this.minY;
                this.velocity = 0;
            }
            else if (y < this.maxY) {
                y = this.maxY;
                this.velocity = 0;
            }
            var notLockedIn = (y % this.optHeight !== 0 || Math.abs(this.velocity) > 1);
            this.update(y, 0, true, !notLockedIn);
            if (notLockedIn) {
                this.rafId = nativeRaf(this.decelerateFunc);
            }
        }
        else if (this.y % this.optHeight !== 0) {
            var currentPos = Math.abs(this.y % this.optHeight);
            this.velocity = (currentPos > (this.optHeight / 2) ? 1 : -1);
            this._haptic.gestureSelectionEnd();
            this.decelerate();
        }
        var currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0);
        if (currentIndex !== this.lastTempIndex) {
            this._haptic.gestureSelectionChanged();
        }
        this.lastTempIndex = currentIndex;
    };
    PickerColumnCmp.prototype.optClick = function (ev, index) {
        if (!this.velocity) {
            ev.preventDefault();
            ev.stopPropagation();
            this.setSelected(index, 150);
        }
    };
    PickerColumnCmp.prototype.setSelected = function (selectedIndex, duration) {
        var y = (selectedIndex > -1) ? ((selectedIndex * this.optHeight) * -1) : 0;
        if (this.rafId) {
            cancelRaf(this.rafId);
            this.rafId = null;
        }
        this.velocity = 0;
        this.update(y, duration, true, true);
    };
    PickerColumnCmp.prototype.update = function (y, duration, saveY, emitChange) {
        y = Math.round(y);
        var i, button, opt, optOffset, visible, translateX, translateY, translateZ, rotateX, transform, selected;
        var parent = this.colEle.nativeElement;
        var children = parent.children;
        var length = children.length;
        var selectedIndex = this.col.selectedIndex = Math.min(Math.max(Math.round(-y / this.optHeight), 0), length - 1);
        var durationStr = (duration === 0) ? null : duration + 'ms';
        var scaleStr = "scale(" + this.scaleFactor + ")";
        for (i = 0; i < length; i++) {
            button = children[i];
            opt = this.col.options[i];
            optOffset = (i * this.optHeight) + y;
            visible = true;
            transform = '';
            if (this.rotateFactor !== 0) {
                rotateX = optOffset * this.rotateFactor;
                if (Math.abs(rotateX) > 90) {
                    visible = false;
                }
                else {
                    translateX = 0;
                    translateY = 0;
                    translateZ = 90;
                    transform = "rotateX(" + rotateX + "deg) ";
                }
            }
            else {
                translateX = 0;
                translateZ = 0;
                translateY = optOffset;
                if (Math.abs(translateY) > 170) {
                    visible = false;
                }
            }
            selected = selectedIndex === i;
            if (visible) {
                transform += "translate3d(0px," + translateY + "px," + translateZ + "px) ";
                if (this.scaleFactor !== 1 && !selected) {
                    transform += scaleStr;
                }
            }
            else {
                transform = 'translate3d(-9999px,0px,0px)';
            }
            if (duration !== opt._dur) {
                opt._dur = duration;
                button.style[CSS.transitionDuration] = durationStr;
            }
            if (transform !== opt._trans) {
                opt._trans = transform;
                button.style[CSS.transform] = transform;
            }
            if (selected !== opt._selected) {
                opt._selected = selected;
                if (selected) {
                    button.classList.add(PICKER_OPT_SELECTED);
                }
                else {
                    button.classList.remove(PICKER_OPT_SELECTED);
                }
            }
        }
        if (saveY) {
            this.y = y;
        }
        if (emitChange) {
            if (this.lastIndex === undefined) {
                this.lastIndex = this.col.selectedIndex;
            }
            else if (this.lastIndex !== this.col.selectedIndex) {
                this.lastIndex = this.col.selectedIndex;
                var ionChange = this.ionChange;
                if (ionChange.observers.length > 0) {
                    this._zone.run(ionChange.emit.bind(ionChange, this.col.options[this.col.selectedIndex]));
                }
            }
        }
    };
    PickerColumnCmp.prototype.refresh = function () {
        var min = this.col.options.length - 1;
        var max = 0;
        for (var i = 0; i < this.col.options.length; i++) {
            if (!this.col.options[i].disabled) {
                min = Math.min(min, i);
                max = Math.max(max, i);
            }
        }
        var selectedIndex = clamp(min, this.col.selectedIndex, max);
        if (selectedIndex !== this.col.selectedIndex) {
            var y = (selectedIndex * this.optHeight) * -1;
            this.update(y, 150, true, true);
        }
    };
    PickerColumnCmp.decorators = [
        { type: Component, args: [{
                    selector: '.picker-col',
                    template: '<div *ngIf="col.prefix" class="picker-prefix" [style.width]="col.prefixWidth">{{col.prefix}}</div>' +
                        '<div class="picker-opts" #colEle [style.width]="col.optionsWidth">' +
                        '<button *ngFor="let o of col.options; let i=index"' +
                        '[class.picker-opt-disabled]="o.disabled" ' +
                        'class="picker-opt" disable-activated (click)="optClick($event, i)">' +
                        '{{o.text}}' +
                        '</button>' +
                        '</div>' +
                        '<div *ngIf="col.suffix" class="picker-suffix" [style.width]="col.suffixWidth">{{col.suffix}}</div>',
                    host: {
                        '[style.min-width]': 'col.columnWidth',
                        '[class.picker-opts-left]': 'col.align=="left"',
                        '[class.picker-opts-right]': 'col.align=="right"',
                    }
                },] },
    ];
    PickerColumnCmp.ctorParameters = [
        { type: Config, },
        { type: ElementRef, },
        { type: DomSanitizer, },
        { type: NgZone, },
        { type: Haptic, },
    ];
    PickerColumnCmp.propDecorators = {
        'colEle': [{ type: ViewChild, args: ['colEle',] },],
        'col': [{ type: Input },],
        'ionChange': [{ type: Output },],
    };
    return PickerColumnCmp;
}());
export var PickerCmp = (function () {
    function PickerCmp(_viewCtrl, _elementRef, _config, gestureCtrl, params, renderer) {
        this._viewCtrl = _viewCtrl;
        this._elementRef = _elementRef;
        this._config = _config;
        this._gestureBlocker = gestureCtrl.createBlocker(BLOCK_ALL);
        this.d = params.data;
        this.mode = _config.get('mode');
        renderer.setElementClass(_elementRef.nativeElement, "picker-" + this.mode, true);
        if (this.d.cssClass) {
            this.d.cssClass.split(' ').forEach(function (cssClass) {
                renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
            });
        }
        this.id = (++pickerIds);
        this.lastClick = 0;
    }
    PickerCmp.prototype.ionViewWillLoad = function () {
        var data = this.d;
        data.buttons = data.buttons.map(function (button) {
            if (isString(button)) {
                return { text: button };
            }
            if (button.role) {
                button.cssRole = "picker-toolbar-" + button.role;
            }
            return button;
        });
        data.columns = data.columns.map(function (column) {
            if (!isPresent(column.columnWidth)) {
                column.columnWidth = (100 / data.columns.length) + '%';
            }
            if (!isPresent(column.options)) {
                column.options = [];
            }
            column.options = column.options.map(function (inputOpt) {
                var opt = {
                    text: '',
                    value: '',
                    disabled: inputOpt.disabled,
                };
                if (isPresent(inputOpt)) {
                    if (isString(inputOpt) || isNumber(inputOpt)) {
                        opt.text = inputOpt.toString();
                        opt.value = inputOpt;
                    }
                    else {
                        opt.text = isPresent(inputOpt.text) ? inputOpt.text : inputOpt.value;
                        opt.value = isPresent(inputOpt.value) ? inputOpt.value : inputOpt.text;
                    }
                }
                return opt;
            });
            return column;
        });
    };
    PickerCmp.prototype.ionViewWillEnter = function () {
        this._gestureBlocker.block();
    };
    PickerCmp.prototype.ionViewDidLeave = function () {
        this._gestureBlocker.unblock();
    };
    PickerCmp.prototype.refresh = function () {
        this._cols.forEach(function (column) {
            column.refresh();
        });
    };
    PickerCmp.prototype._colChange = function (selectedOption) {
        var picker = this._viewCtrl;
        picker.ionChange.emit(this.getSelected());
    };
    PickerCmp.prototype._keyUp = function (ev) {
        if (this.enabled && this._viewCtrl.isLast()) {
            if (ev.keyCode === Key.ENTER) {
                if (this.lastClick + 1000 < Date.now()) {
                    (void 0);
                    var button = this.d.buttons[this.d.buttons.length - 1];
                    this.btnClick(button);
                }
            }
            else if (ev.keyCode === Key.ESCAPE) {
                (void 0);
                this.bdClick();
            }
        }
    };
    PickerCmp.prototype.ionViewDidEnter = function () {
        var activeElement = document.activeElement;
        if (activeElement) {
            activeElement.blur();
        }
        var focusableEle = this._elementRef.nativeElement.querySelector('button');
        if (focusableEle) {
            focusableEle.focus();
        }
        this.enabled = true;
    };
    PickerCmp.prototype.btnClick = function (button, dismissDelay) {
        var _this = this;
        if (!this.enabled) {
            return;
        }
        this.lastClick = Date.now();
        var shouldDismiss = true;
        if (button.handler) {
            if (button.handler(this.getSelected()) === false) {
                shouldDismiss = false;
            }
        }
        if (shouldDismiss) {
            setTimeout(function () {
                _this.dismiss(button.role);
            }, dismissDelay || this._config.get('pageTransitionDelay'));
        }
    };
    PickerCmp.prototype.bdClick = function () {
        if (this.enabled && this.d.enableBackdropDismiss) {
            this.dismiss('backdrop');
        }
    };
    PickerCmp.prototype.dismiss = function (role) {
        return this._viewCtrl.dismiss(this.getSelected(), role);
    };
    PickerCmp.prototype.getSelected = function () {
        var selected = {};
        this.d.columns.forEach(function (col, index) {
            var selectedColumn = col.options[col.selectedIndex];
            selected[col.name] = {
                text: selectedColumn ? selectedColumn.text : null,
                value: selectedColumn ? selectedColumn.value : null,
                columnIndex: index,
            };
        });
        return selected;
    };
    PickerCmp.prototype.ngOnDestroy = function () {
        (void 0);
        this._gestureBlocker.destroy();
    };
    PickerCmp.decorators = [
        { type: Component, args: [{
                    selector: 'ion-picker-cmp',
                    template: "\n    <ion-backdrop (click)=\"bdClick()\"></ion-backdrop>\n    <div class=\"picker-wrapper\">\n      <div class=\"picker-toolbar\">\n        <div *ngFor=\"let b of d.buttons\" class=\"picker-toolbar-button\" [ngClass]=\"b.cssRole\">\n          <button ion-button (click)=\"btnClick(b)\" [ngClass]=\"b.cssClass\" class=\"picker-button\" clear>\n            {{b.text}}\n          </button>\n        </div>\n      </div>\n      <div class=\"picker-columns\">\n        <div class=\"picker-above-highlight\"></div>\n        <div *ngFor=\"let c of d.columns\" [col]=\"c\" class=\"picker-col\" (ionChange)=\"_colChange($event)\"></div>\n        <div class=\"picker-below-highlight\"></div>\n      </div>\n    </div>\n  ",
                    host: {
                        'role': 'dialog'
                    },
                    encapsulation: ViewEncapsulation.None,
                },] },
    ];
    PickerCmp.ctorParameters = [
        { type: ViewController, },
        { type: ElementRef, },
        { type: Config, },
        { type: GestureController, },
        { type: NavParams, },
        { type: Renderer, },
    ];
    PickerCmp.propDecorators = {
        '_cols': [{ type: ViewChildren, args: [PickerColumnCmp,] },],
        '_keyUp': [{ type: HostListener, args: ['body:keyup', ['$event'],] },],
    };
    return PickerCmp;
}());
var pickerIds = -1;
var PICKER_OPT_SELECTED = 'picker-opt-selected';
var DECELERATION_FRICTION = 0.97;
var FRAME_MS = (1000 / 60);
//# sourceMappingURL=picker-component.js.map