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 class PickerColumnCmp {
    constructor(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);
    }
    ngAfterViewInit() {
        let 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
        });
    }
    ngOnDestroy() {
        this.events.unlistenAll();
    }
    pointerStart(ev) {
        (void 0);
        this._haptic.gestureSelectionStart();
        ev.preventDefault();
        this.debouncer.debounce(() => {
            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());
            let options = this.col.options;
            let minY = (options.length - 1);
            let 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;
    }
    pointerMove(ev) {
        ev.preventDefault();
        ev.stopPropagation();
        this.debouncer.debounce(() => {
            if (this.startY === null) {
                return;
            }
            let currentY = pointerCoord(ev).y;
            this.pos.push(currentY, Date.now());
            let 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);
            let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0);
            if (currentIndex !== this.lastTempIndex) {
                this._haptic.gestureSelectionChanged();
                this.lastTempIndex = currentIndex;
            }
        });
    }
    pointerEnd(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;
        }
        let endY = pointerCoord(ev).y;
        this.pos.push(endY, Date.now());
        let endPos = (this.pos.length - 1);
        let startPos = endPos;
        let 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();
    }
    decelerate() {
        let 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();
        }
        let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0);
        if (currentIndex !== this.lastTempIndex) {
            this._haptic.gestureSelectionChanged();
        }
        this.lastTempIndex = currentIndex;
    }
    optClick(ev, index) {
        if (!this.velocity) {
            ev.preventDefault();
            ev.stopPropagation();
            this.setSelected(index, 150);
        }
    }
    setSelected(selectedIndex, duration) {
        let 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);
    }
    update(y, duration, saveY, emitChange) {
        y = Math.round(y);
        let i, button, opt, optOffset, visible, translateX, translateY, translateZ, rotateX, transform, selected;
        const parent = this.colEle.nativeElement;
        const children = parent.children;
        const length = children.length;
        const selectedIndex = this.col.selectedIndex = Math.min(Math.max(Math.round(-y / this.optHeight), 0), length - 1);
        const durationStr = (duration === 0) ? null : duration + 'ms';
        const 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]));
                }
            }
        }
    }
    refresh() {
        let min = this.col.options.length - 1;
        let 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 },],
};
export class PickerCmp {
    constructor(_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(cssClass => {
                renderer.setElementClass(_elementRef.nativeElement, cssClass, true);
            });
        }
        this.id = (++pickerIds);
        this.lastClick = 0;
    }
    ionViewWillLoad() {
        let data = this.d;
        data.buttons = data.buttons.map(button => {
            if (isString(button)) {
                return { text: button };
            }
            if (button.role) {
                button.cssRole = `picker-toolbar-${button.role}`;
            }
            return button;
        });
        data.columns = data.columns.map(column => {
            if (!isPresent(column.columnWidth)) {
                column.columnWidth = (100 / data.columns.length) + '%';
            }
            if (!isPresent(column.options)) {
                column.options = [];
            }
            column.options = column.options.map(inputOpt => {
                let 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;
        });
    }
    ionViewWillEnter() {
        this._gestureBlocker.block();
    }
    ionViewDidLeave() {
        this._gestureBlocker.unblock();
    }
    refresh() {
        this._cols.forEach(column => {
            column.refresh();
        });
    }
    _colChange(selectedOption) {
        var picker = this._viewCtrl;
        picker.ionChange.emit(this.getSelected());
    }
    _keyUp(ev) {
        if (this.enabled && this._viewCtrl.isLast()) {
            if (ev.keyCode === Key.ENTER) {
                if (this.lastClick + 1000 < Date.now()) {
                    (void 0);
                    let button = this.d.buttons[this.d.buttons.length - 1];
                    this.btnClick(button);
                }
            }
            else if (ev.keyCode === Key.ESCAPE) {
                (void 0);
                this.bdClick();
            }
        }
    }
    ionViewDidEnter() {
        let activeElement = document.activeElement;
        if (activeElement) {
            activeElement.blur();
        }
        let focusableEle = this._elementRef.nativeElement.querySelector('button');
        if (focusableEle) {
            focusableEle.focus();
        }
        this.enabled = true;
    }
    btnClick(button, dismissDelay) {
        if (!this.enabled) {
            return;
        }
        this.lastClick = Date.now();
        let shouldDismiss = true;
        if (button.handler) {
            if (button.handler(this.getSelected()) === false) {
                shouldDismiss = false;
            }
        }
        if (shouldDismiss) {
            setTimeout(() => {
                this.dismiss(button.role);
            }, dismissDelay || this._config.get('pageTransitionDelay'));
        }
    }
    bdClick() {
        if (this.enabled && this.d.enableBackdropDismiss) {
            this.dismiss('backdrop');
        }
    }
    dismiss(role) {
        return this._viewCtrl.dismiss(this.getSelected(), role);
    }
    getSelected() {
        let selected = {};
        this.d.columns.forEach((col, index) => {
            let selectedColumn = col.options[col.selectedIndex];
            selected[col.name] = {
                text: selectedColumn ? selectedColumn.text : null,
                value: selectedColumn ? selectedColumn.value : null,
                columnIndex: index,
            };
        });
        return selected;
    }
    ngOnDestroy() {
        (void 0);
        this._gestureBlocker.destroy();
    }
}
PickerCmp.decorators = [
    { type: Component, args: [{
                selector: 'ion-picker-cmp',
                template: `
    <ion-backdrop (click)="bdClick()"></ion-backdrop>
    <div class="picker-wrapper">
      <div class="picker-toolbar">
        <div *ngFor="let b of d.buttons" class="picker-toolbar-button" [ngClass]="b.cssRole">
          <button ion-button (click)="btnClick(b)" [ngClass]="b.cssClass" class="picker-button" clear>
            {{b.text}}
          </button>
        </div>
      </div>
      <div class="picker-columns">
        <div class="picker-above-highlight"></div>
        <div *ngFor="let c of d.columns" [col]="c" class="picker-col" (ionChange)="_colChange($event)"></div>
        <div class="picker-below-highlight"></div>
      </div>
    </div>
  `,
                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'],] },],
};
let pickerIds = -1;
const PICKER_OPT_SELECTED = 'picker-opt-selected';
const DECELERATION_FRICTION = 0.97;
const FRAME_MS = (1000 / 60);
//# sourceMappingURL=picker-component.js.map