import { FocusMonitor, A11yModule } from '@angular/cdk/a11y';
import { DOCUMENT, CommonModule } from '@angular/common';
import { EventEmitter, ChangeDetectorRef, ElementRef, Inject, Attribute, Self, Optional, NgZone, Input, Output, ViewChild, Component, ViewEncapsulation, ChangeDetectionStrategy, Directive, NgModule } from '@angular/core';
import { TOP_CENTER, BOTTOM_CENTER, LEFT_CENTER, RIGHT_CENTER, NtOverlayComponent, NtOverlayModule } from '@ng-tangram/components/overlay';
import { coerceNumberProperty, coerceBooleanProperty } from '@angular/cdk/coercion';
import { hasModifierKey, DOWN_ARROW, RIGHT_ARROW, UP_ARROW, LEFT_ARROW, HOME, END, PAGE_DOWN, PAGE_UP } from '@angular/cdk/keycodes';
import { CdkOverlayOrigin } from '@angular/cdk/overlay';
import { NgControl } from '@angular/forms';
import { NtFormFieldControl } from '@ng-tangram/components/forms';

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var NtSliderComponent_1;
const activeEventOptions = { passive: false };
class NtSliderChange {
}
let NtSliderComponent = NtSliderComponent_1 = class NtSliderComponent extends NtFormFieldControl {
    constructor(_changeDetectorRef, _elementRef, _focusMonitor, document, tabIndex, ngControl, _ngZone) {
        super();
        this._changeDetectorRef = _changeDetectorRef;
        this._elementRef = _elementRef;
        this._focusMonitor = _focusMonitor;
        this.ngControl = ngControl;
        this._ngZone = _ngZone;
        this._min = 0;
        this._max = 100;
        this._step = 1;
        this._value = null;
        this._percent = 0;
        this._disabled = false;
        this._invert = false;
        this._stepmark = false;
        this._vertical = false;
        this.change = new EventEmitter();
        this.input = new EventEmitter();
        this.valueChange = new EventEmitter();
        this._onChange = () => { };
        this._onTouched = () => { };
        this._sliderDimensions = null;
        this._isSliding = false;
        this._isActive = false;
        this._stepmarkValues = [];
        this._pointerDown = (event) => {
            if (this.disabled || this._isSliding || (!isTouchEvent(event) && event.button !== 0)) {
                return;
            }
            this._ngZone.run(() => {
                const oldValue = this.value;
                const pointerPosition = getPointerPositionOnPage(event);
                setTimeout(() => this._isSliding = true);
                this._lastPointerEvent = event;
                event.preventDefault();
                this._focusHostElement();
                this._onMouseenter();
                this._bindGlobalEvents(event);
                this._focusHostElement();
                this._updateValueFromPosition(pointerPosition);
                this._valueOnSlideStart = this.value;
                this._pointerPositionOnStart = pointerPosition;
                if (oldValue != this.value) {
                    this._emitInputEvent();
                    this._emitChangeEvent();
                }
            });
        };
        this._pointerMove = (event) => {
            if (this._isSliding) {
                event.preventDefault();
                const oldValue = this.value;
                this._lastPointerEvent = event;
                this._updateValueFromPosition(getPointerPositionOnPage(event));
                if (oldValue != this.value) {
                    this._emitInputEvent();
                }
                this._overlay.markOpen();
                this._overlay.forceUpdatePosition();
            }
        };
        this._pointerUp = (event) => {
            if (this._isSliding) {
                const pointerPositionOnStart = this._pointerPositionOnStart;
                const currentPointerPosition = getPointerPositionOnPage(event);
                event.preventDefault();
                this._removeGlobalEvents();
                this._valueOnSlideStart = this._pointerPositionOnStart = this._lastPointerEvent = null;
                this._isSliding = false;
                if (this._valueOnSlideStart !== this.value &&
                    !this.disabled && ((pointerPositionOnStart === null || pointerPositionOnStart === void 0 ? void 0 : pointerPositionOnStart.x) !== currentPointerPosition.x ||
                    (pointerPositionOnStart === null || pointerPositionOnStart === void 0 ? void 0 : pointerPositionOnStart.y) !== currentPointerPosition.y)) {
                    this._emitChangeEvent();
                }
            }
            this._overlay.markClose();
        };
        this._windowBlur = () => {
            if (this._lastPointerEvent) {
                this._pointerUp(this._lastPointerEvent);
            }
        };
        this._document = document;
        this.tabIndex = parseInt(tabIndex) || 0;
        if (this.ngControl) {
            this.ngControl.valueAccessor = this;
        }
        this._ngZone.runOutsideAngular(() => {
            const element = _elementRef.nativeElement;
            element.addEventListener('mousedown', this._pointerDown, activeEventOptions);
            element.addEventListener('touchstart', this._pointerDown, activeEventOptions);
        });
    }
    get min() { return this._min; }
    set min(value) {
        this._min = coerceNumberProperty(value, this._max);
        if (this._value === null) {
            this.value = this._min;
        }
        this._percent = this._calculatePercentage(this._value);
        this._changeDetectorRef.markForCheck();
    }
    get max() { return this._max; }
    set max(value) {
        this._max = coerceNumberProperty(value, this._min);
        this._percent = this._calculatePercentage(this._value);
        this._changeDetectorRef.markForCheck();
    }
    get step() { return this._step; }
    set step(value) {
        this._step = coerceNumberProperty(value, this._step);
        if (this._step % 1 !== 0) {
            this._roundToDecimal = this._step.toString().split('.').pop().length;
        }
        this._changeDetectorRef.markForCheck();
    }
    get value() {
        if (this._value === null) {
            this.value = this._min;
        }
        return this._value;
    }
    set value(value) {
        if (value !== this._value) {
            let _value = coerceNumberProperty(value);
            if (this._roundToDecimal) {
                _value = parseFloat(_value.toFixed(this._roundToDecimal));
            }
            this._value = this._clamp(_value, this.min, this.max);
            this._percent = this._calculatePercentage(this._value);
            this._changeDetectorRef.markForCheck();
        }
    }
    get percent() { return this._clamp(this._percent); }
    get displayValue() {
        if (this._roundToDecimal && this.value && this.value % 1 !== 0) {
            return this.value.toFixed(this._roundToDecimal);
        }
        return this.value || 0;
    }
    get disabled() { return this._disabled; }
    set disabled(value) {
        this._disabled = coerceBooleanProperty(value);
    }
    get invert() { return this._invert; }
    set invert(value) {
        this._invert = coerceBooleanProperty(value);
    }
    get stepmark() { return this._stepmark; }
    set stepmark(value) {
        this._stepmark = coerceBooleanProperty(value);
        if (this._stepmark) {
            this._stepmarkValues = this._calculateStepmarks();
        }
        else {
            this._stepmarkValues = [];
        }
    }
    get vertical() { return this._vertical; }
    set vertical(value) {
        this._vertical = coerceBooleanProperty(value);
    }
    get _trackFillStyles() {
        let sizeValue = this.percent * 100;
        return !this.vertical
            ? { width: `${sizeValue}%` }
            : { height: `${sizeValue}%` };
    }
    get _trackHandleStyles() {
        const offsetValue = this.percent * 100;
        if (!this.vertical) {
            return !this.invert
                ? { left: `${offsetValue}%` }
                : { right: `${offsetValue}%` };
        }
        else {
            return { top: `${offsetValue}%` };
        }
    }
    get _trackTootipPosition() {
        return !this.vertical
            ? [TOP_CENTER, BOTTOM_CENTER]
            : [LEFT_CENTER, RIGHT_CENTER];
    }
    ngOnInit() {
        this._origin = new CdkOverlayOrigin(this._sliderHandle);
        this._focusMonitor
            .monitor(this._elementRef, true)
            .subscribe((origin) => {
            this._isActive = !!origin && origin !== 'keyboard';
            this._changeDetectorRef.detectChanges();
        });
    }
    ngOnDestroy() {
        const element = this._elementRef.nativeElement;
        element.removeEventListener('mousedown', this._pointerDown, activeEventOptions);
        element.removeEventListener('touchstart', this._pointerDown, activeEventOptions);
    }
    writeValue(obj) {
        if (this.disabled) {
            return;
        }
        this.value = obj;
    }
    registerOnChange(fn) {
        this._onChange = fn;
    }
    registerOnTouched(fn) {
        this._onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
    }
    focus(options) {
        this._focusHostElement(options);
    }
    blur() {
        this._blurHostElement();
    }
    _onMouseenter() {
        if (this.disabled) {
            return;
        }
        this._sliderDimensions = this._getSliderDimensions();
    }
    _onFocus() {
        this._sliderDimensions = this._getSliderDimensions();
    }
    _onBlur() {
        this._onTouched();
    }
    _onKeydown(event) {
        if (this.disabled || hasModifierKey(event)) {
            return;
        }
        const oldValue = this.value;
        switch (event.keyCode) {
            case PAGE_UP:
                this._increment(10);
                break;
            case PAGE_DOWN:
                this._increment(-10);
                break;
            case END:
                this.value = this.max;
                break;
            case HOME:
                this.value = this.min;
                break;
            case LEFT_ARROW:
                this._increment(-1);
                break;
            case UP_ARROW:
                this._increment(1);
                break;
            case RIGHT_ARROW:
                this._increment(1);
                break;
            case DOWN_ARROW:
                this._increment(-1);
                break;
            default:
                return;
        }
        if (oldValue != this.value) {
            this._emitInputEvent();
            this._emitChangeEvent();
        }
        this._isSliding = true;
        event.preventDefault();
    }
    _onKeyup() {
        this._isSliding = false;
    }
    _shouldInvertMouseCoords() {
        return !this.vertical ? this.invert : !this.invert;
    }
    _trackStepmarkStyles(stepmark) {
        if (!this.vertical) {
            return !this.invert
                ? { left: `${stepmark.percent}%` }
                : { right: `${stepmark.percent}%` };
        }
        else {
            return { top: `${stepmark.percent}%` };
        }
    }
    _increment(numSteps) {
        this.value = this._clamp((this.value || 0) + this.step * numSteps, this.min, this.max);
    }
    _getSliderDimensions() {
        return this._elementRef.nativeElement.getBoundingClientRect();
    }
    _updateValueFromPosition(pos) {
        if (!this._sliderDimensions) {
            return;
        }
        let offset = this.vertical ? this._sliderDimensions.top : this._sliderDimensions.left;
        let size = this.vertical ? this._sliderDimensions.height : this._sliderDimensions.width;
        let posComponent = this.vertical ? pos.y : pos.x;
        let percent = this._clamp((posComponent - offset) / size);
        if (this._shouldInvertMouseCoords()) {
            percent = 1 - percent;
        }
        if (percent === 0) {
            this.value = this.min;
        }
        else if (percent === 1) {
            this.value = this.max;
        }
        else {
            const exactValue = this._calculateValue(percent);
            const closestValue = Math.round((exactValue - this.min) / this.step) * this.step + this.min;
            this.value = this._clamp(closestValue, this.min, this.max);
        }
    }
    _calculatePercentage(value) {
        return ((value || 0) - this.min) / (this.max - this.min);
    }
    _calculateValue(percentage) {
        return this.min + percentage * (this.max - this.min);
    }
    _calculateStepmarks() {
        const values = [{ value: this.min, percent: 0 }];
        const range = this.max - this.min;
        let start = this.min;
        while ((start += this.step) < this.max) {
            values.push({
                value: start,
                percent: (start - this.min) / range * 100
            });
        }
        if (!values.find(item => item.value === this.max)) {
            values.push({ value: this.max, percent: 100 });
        }
        return values;
    }
    _clamp(value, min = 0, max = 1) {
        return Math.max(min, Math.min(value, max));
    }
    _bindGlobalEvents(triggerEvent) {
        if (typeof this._document !== 'undefined' && this._document) {
            const body = this._document.body;
            const isTouch = isTouchEvent(triggerEvent);
            const moveEventName = isTouch ? 'touchmove' : 'mousemove';
            const endEventName = isTouch ? 'touchend' : 'mouseup';
            body.addEventListener(moveEventName, this._pointerMove, activeEventOptions);
            body.addEventListener(endEventName, this._pointerUp, activeEventOptions);
            if (isTouch) {
                body.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
            }
        }
        if (typeof window !== 'undefined' && window) {
            window.addEventListener('blur', this._windowBlur);
        }
    }
    _removeGlobalEvents() {
        if (typeof this._document !== 'undefined' && this._document) {
            const body = this._document.body;
            body.removeEventListener('mousemove', this._pointerMove, activeEventOptions);
            body.removeEventListener('mouseup', this._pointerUp, activeEventOptions);
            body.removeEventListener('touchmove', this._pointerMove, activeEventOptions);
            body.removeEventListener('touchend', this._pointerUp, activeEventOptions);
            body.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
        }
        if (typeof window !== 'undefined' && window) {
            window.removeEventListener('blur', this._windowBlur);
        }
    }
    _focusHostElement(options) {
        this._elementRef.nativeElement.focus(options);
    }
    _blurHostElement() {
        this._elementRef.nativeElement.blur();
    }
    _emitChangeEvent() {
        this._onChange(this.value);
        this.valueChange.emit(this.value);
        this.change.emit(this._createSliderChange());
    }
    _emitInputEvent() {
        this.input.emit(this._createSliderChange());
        this._changeDetectorRef.markForCheck();
    }
    _createSliderChange() {
        const change = new NtSliderChange();
        change.source = this;
        change.value = this.value || this.min;
        return change;
    }
};
NtSliderComponent.ctorParameters = () => [
    { type: ChangeDetectorRef },
    { type: ElementRef },
    { type: FocusMonitor },
    { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
    { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
    { type: NgControl, decorators: [{ type: Self }, { type: Optional }] },
    { type: NgZone }
];
__decorate([
    Input()
], NtSliderComponent.prototype, "min", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "max", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "step", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "value", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "disabled", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "invert", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "stepmark", null);
__decorate([
    Input()
], NtSliderComponent.prototype, "vertical", null);
__decorate([
    Output()
], NtSliderComponent.prototype, "change", void 0);
__decorate([
    Output()
], NtSliderComponent.prototype, "input", void 0);
__decorate([
    Output()
], NtSliderComponent.prototype, "valueChange", void 0);
__decorate([
    ViewChild('sliderHandle', { static: true, read: ElementRef })
], NtSliderComponent.prototype, "_sliderHandle", void 0);
__decorate([
    ViewChild(NtOverlayComponent, { static: true })
], NtSliderComponent.prototype, "_overlay", void 0);
NtSliderComponent = NtSliderComponent_1 = __decorate([
    Component({
        selector: 'nt-slider',
        exportAs: 'ntSlider',
        template: "<span #sliderHandle\n  class=\"nt-slider-handle\"\n  [ngStyle]=\"_trackHandleStyles\"\n  (mouseenter)=\"_overlay.markOpen()\"\n  (mouseleave)=\"_overlay.markClose()\">\n</span>\n<span class=\"nt-slider-fill\" [ngStyle]=\"_trackFillStyles\"></span>\n<div class=\"nt-slider-stepmark\" *ngIf=\"stepmark\">\n  <span *ngFor=\"let mark of _stepmarkValues\"\n    [class.is-passed]=\"value >= mark.value\"\n    [ngStyle]=\"_trackStepmarkStyles(mark)\">\n  </span>\n</div>\n<nt-overlay\n  overlayClass=\"nt-slider-tooltip-overlay\"\n  arrow\n  [origin]=\"_origin\"\n  [positionPairs]=\"_trackTootipPosition\"\n  (overlayEnter)=\"_overlay.markOpen()\"\n  (overlayLeave)=\"_overlay.markClose()\">\n  <div class=\"nt-slider-tooltip\">{{ displayValue }}</div>\n</nt-overlay>\n",
        encapsulation: ViewEncapsulation.None,
        changeDetection: ChangeDetectionStrategy.OnPush,
        host: {
            '(focus)': '_onFocus()',
            '(blur)': '_onBlur()',
            '(keydown)': '_onKeydown($event)',
            '(keyup)': '_onKeyup()',
            '(mouseenter)': '_onMouseenter()',
            '(selectstart)': '$event.preventDefault()',
            'class': 'nt-slider',
            'role': 'slider',
            '[tabIndex]': 'tabIndex',
            '[class.is-sliding]': '_isSliding',
            '[class.vertical]': 'vertical',
            '[class.invert]': 'invert'
        },
        providers: [
            { provide: NtFormFieldControl, useExisting: NtSliderComponent_1, multi: true }
        ]
    }),
    __param(3, Inject(DOCUMENT)),
    __param(4, Attribute('tabindex')),
    __param(5, Self()), __param(5, Optional())
], NtSliderComponent);
function isTouchEvent(event) {
    return event.type[0] === 't';
}
function getPointerPositionOnPage(event) {
    const point = isTouchEvent(event) ? (event.touches[0] || event.changedTouches[0]) : event;
    return { x: point.clientX, y: point.clientY };
}

var __decorate$1 = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
let NtSliderInputDirective = class NtSliderInputDirective {
    constructor(element) {
        this._input = element.nativeElement;
    }
    get slider() { return this._slider; }
    set slider(value) {
        this._slider = value;
        if (this._slider) {
            this._connect();
        }
        else {
            this._disconnect();
        }
    }
    _connect() {
        this._connection = this.slider.input.subscribe(() => this._updateInputValue());
        this._coverOriginalAttributes();
        this._updateInputValue();
        this._input.addEventListener('change', this._onInputValueChange.bind(this));
    }
    _disconnect() {
        var _a;
        (_a = this._connection) === null || _a === void 0 ? void 0 : _a.unsubscribe();
        this._input.removeEventListener('change', this._onInputValueChange);
        this._restoreOriginalAttributes();
    }
    _updateInputValue() {
        this._input.value = `${this.slider.displayValue}`;
    }
    _onInputValueChange(_) {
        const newValue = Number.parseFloat(this._input.value);
        this.slider.writeValue(newValue);
        this._updateInputValue();
        this._emitSliderChangeEvent();
    }
    _emitSliderChangeEvent() {
        this.slider.valueChange.emit(this.slider.value);
        this.slider.change.emit(this._createSliderChange());
    }
    _createSliderChange() {
        const change = new NtSliderChange();
        change.source = this.slider;
        change.value = this.slider.value || 0;
        return change;
    }
    _coverOriginalAttributes() {
        this._originalStep = this._input.step;
        this._originalMin = this._input.min;
        this._originalMax = this._input.max;
        this._input.step = `${this.slider.step}`;
        this._input.min = `${this.slider.min}`;
        this._input.max = `${this.slider.max}`;
    }
    _restoreOriginalAttributes() {
        this._input.step = this._originalStep;
        this._input.min = this._originalMin;
        this._input.max = this._originalMax;
    }
};
NtSliderInputDirective.ctorParameters = () => [
    { type: ElementRef }
];
__decorate$1([
    Input('ntSliderInput')
], NtSliderInputDirective.prototype, "slider", null);
NtSliderInputDirective = __decorate$1([
    Directive({
        selector: 'input[type="number"][ntSliderInput]'
    })
], NtSliderInputDirective);

var __decorate$2 = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
let NtSliderRangeComponent = class NtSliderRangeComponent {
    constructor() { }
};
NtSliderRangeComponent = __decorate$2([
    Component({
        selector: 'nt-slider-range',
        template: "",
        encapsulation: ViewEncapsulation.None
    })
], NtSliderRangeComponent);

var __decorate$3 = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
let NtSliderModule = class NtSliderModule {
};
NtSliderModule = __decorate$3([
    NgModule({
        imports: [
            CommonModule,
            A11yModule,
            NtOverlayModule,
        ],
        exports: [
            NtSliderComponent,
            NtSliderRangeComponent,
            NtSliderInputDirective
        ],
        declarations: [
            NtSliderComponent,
            NtSliderRangeComponent,
            NtSliderInputDirective
        ]
    })
], NtSliderModule);

export { NtSliderChange, NtSliderComponent, NtSliderInputDirective, NtSliderModule, NtSliderRangeComponent };
//# sourceMappingURL=slider.js.map
