import * as tslib_1 from "tslib";
import { Component, Input, Output, EventEmitter, forwardRef, ViewEncapsulation, ChangeDetectionStrategy, ViewChild, Renderer2, ElementRef, ChangeDetectorRef, QueryList, ViewChildren } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { SliderHandComponent } from './slider-hand';
import { SliderTrackerComponent } from './slider-tracker';
import { SliderService } from './slider.service';
import { OnChange } from '../core/decorators';
/*
 * Provider Expression that allows component to register as a ControlValueAccessor.
 * This allows it to support [(ngModel)].
 * @docs-private
 */
var SLIDER_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(function () { return SliderComponent; }),
    multi: true
};
var SliderComponent = /** @class */ (function () {
    function SliderComponent(render, _cd, el, service) {
        this.render = render;
        this._cd = _cd;
        this.el = el;
        this.service = service;
        /**
         * If we can use input box to change the slider value, input value should be larger than
         * min, less than max and multiple of the step.
         * Only can use when range is false
         * @default 0
         */
        this.input = false;
        this.spinner = false;
        /**
         * Min value of slider
         * @default 0
         */
        this.min = 0;
        /**
         * Max value of slider
         * @default 100
         */
        this.max = 100;
        /**
         * If the slider has double hands
         * @default false
         */
        this.range = false;
        /**
         * Whether the slider is disabled or not
         * @default false
         */
        this.disabled = false;
        /**
         * The step of dragging
         * @default 1
         */
        this.step = 1;
        this._orientation = 'horizontal';
        /**
         * The event emitted when slider value changes, emit the value of slider
         */
        this.change = new EventEmitter();
        /**
         * The hands init position
         * @docs-private
         */
        this.hands = [];
        /**
         * Mark the current hand index
         * @docs-private
         */
        this._currentHand = 1;
        /**
         * If move one hand over the other
         * @docs-private
         */
        this._handRange = false;
        this.render = render;
        this.service = service;
    }
    Object.defineProperty(SliderComponent.prototype, "orientation", {
        /**
         * Orientation of the slider
         * @default horizontal
         */
        get: function () {
            return this._orientation;
        },
        set: function (orientation) {
            if (orientation === 'horizontal') {
                this._orientation = true;
            }
            else {
                this._orientation = false;
            }
        },
        enumerable: true,
        configurable: true
    });
    SliderComponent.prototype.ngOnInit = function () {
        this.initSlider();
    };
    SliderComponent.prototype.ngAfterViewInit = function () {
        var _this = this;
        this.afterView();
        setTimeout(function () {
            // 设置hand能移动的范围值
            var slider = _this._slider.nativeElement;
            _this.limitMove = _this.orientation ? slider.clientWidth : slider.clientHeight;
        }, 500);
    };
    /**
     * Dragging hands copy, because the hands'order change when dragging cross,
     * so keep a init order here
     * @docs-private
     */
    SliderComponent.prototype.afterView = function () {
        this.sliderHands = Object.assign({}, {
            first: this._hands.first,
            last: this._hands.last
        });
    };
    /**
     * Set default value
     * @docs-private
     */
    SliderComponent.prototype.setDefaultValue = function () {
        var _this = this;
        if (this.range) {
            if (!this.value) {
                this.value = [];
                this.value[0] = this.value[1] = this.min;
            }
            else {
                this.value.map(function (val, i, arr) { return arr[i]
                    = _this.valueCheck(val, _this.min); });
            }
        }
        else {
            this.value = this.valueCheck(this.value, this.min);
        }
    };
    /**
     * value check
     * @docs-private
     */
    SliderComponent.prototype.valueCheck = function (v, option) {
        return !isNaN(v) ? v : option;
    };
    /**
     * 输入值校验
     * @docs-private
     */
    SliderComponent.prototype.inputValidate = function () {
        if (this.range) {
            if (this.value[0] < this.min || this.value[0] > this.max) {
                throw new Error('Input value should larger than min and less than max');
            }
        }
        else {
            if (this.value > this.max || this.value < this.min) {
                throw new Error('Input value should larger than min and less than max');
            }
        }
    };
    /**
     * Initialize the slider and the sub components
     * @docs-private
     */
    SliderComponent.prototype.initSlider = function () {
        this.setDefaultValue();
        this.inputValidate();
        // 根据输入值设置hand, tracker初始值
        if (this.range) {
            this.trackerSelected = this.getWidthFromValue(this.value);
            this.trackerPos = this.getWidthFromValue(this.value[0]);
            this.hands.push({ initPos: this.trackerPos }, { initPos: this.getWidthFromValue(this.value[1]) });
        }
        else {
            this.trackerSelected = this.getWidthFromValue(this.value);
            this.hands.push({ initPos: this.trackerSelected });
        }
        this.emitChange();
    };
    /**
     * 根据输入值计算tracker的width
     * @param value input value
     * @docs-private
     */
    SliderComponent.prototype.getWidthFromValue = function (value) {
        var trackerMin = this.valueCheck(value[0], this.min);
        var trackerMax = this.valueCheck(value[1], value);
        return (trackerMax - trackerMin) / (this.max - this.min) * 100;
    };
    /**
     * 输入框输入slider value，限于range为false
     * @docs-private
     */
    SliderComponent.prototype.inputValue = function (v) {
        var calculated = +v;
        if (calculated > this.max
            || calculated < this.min
            || (calculated % this.step !== 0)) {
            return;
        }
        this.value = calculated;
        var value = this.trackerSelected = this.getWidthFromValue(calculated);
        this.sliderHands.first.updateStyle(value);
        this.emitChange();
    };
    /**
     * 更新slider
     * @param info Info
     * @docs-private
     */
    SliderComponent.prototype.updateSlider = function (info) {
        var hands = this.hands;
        if (this.range) {
            this.getNearest(info.initPos, info.endPos, this.hands);
            var move = hands[1].initPos - hands[0].initPos;
            this.value[0] = this.service.getValue(hands[0].initPos, this.step, this.min, this.max);
            this.value[1] = this.service.getValue(hands[1].initPos, this.step, this.min, this.max);
            this.trackerSelected = move;
            this.trackerPos = hands[0].initPos;
            this.emitChange();
            return;
        }
        this.value = this.service.getValue(info.endPos, this.step, this.min, this.max);
        this.trackerSelected = hands[0].initPos = info.endPos;
        this.emitChange();
    };
    /**
     * emit change
     * @docs-private
     */
    SliderComponent.prototype.emitChange = function () {
        this.change.emit(this.value);
        // 更新form model
        this._markForCheck();
    };
    /**
     * rail click handler
     * @docs-private
     */
    SliderComponent.prototype.clickHandler = function (e) {
        if (this.disabled) {
            return;
        }
        var endOffset = this.orientation ? e.offsetX : (this.limitMove - e.offsetY);
        var endPos = endOffset / this.limitMove * 100;
        var step = this.step / (this.max - this.min) * 100;
        endPos = Math.round(endPos / step) * step;
        this.updateSlider({
            initPos: endPos,
            endPos: endPos
        });
        var hand = this._hand || this.sliderHands.last;
        hand.updateStyle(endPos);
    };
    /**
     * 算法适用于range为true时计算hand的位置
     * @param initX 当前移动的hand
     * @param endX 当前hand移动到的新位置
     * @param scope Scope 两个hand的原始位置
     * @docs-private
     */
    SliderComponent.prototype.getNearest = function (initX, endX, scope) {
        var x0 = scope[0].initPos;
        var x1 = scope[1].initPos;
        var hands = this.sliderHands;
        if (x0 === x1) {
            if (endX > x0) {
                scope[0].initPos = x0;
                scope[1].initPos = endX;
            }
            else {
                scope[0].initPos = endX;
                scope[1].initPos = x0;
            }
            if (!this._currentHand) {
                if (endX > x0) {
                    this._handRange = !this._handRange;
                }
            }
            else {
                if (endX < x0) {
                    this._handRange = !this._handRange;
                }
            }
            return;
        }
        if (Math.abs((initX - x0)) < Math.abs((initX - x1))) {
            // move x0
            if (endX < x1) {
                scope[0].initPos = (endX);
                this._handRange
                    ? (this._hand = hands.last)
                    : (this._hand = hands.first);
                this._currentHand = 0;
            }
            else {
                scope[0].initPos = (x1);
                scope[1].initPos = (endX);
                this._handRange = !this._handRange;
                this._currentHand = 1;
            }
        }
        else {
            // move x1
            if (endX > x0) {
                scope[1].initPos = (endX);
                this._handRange
                    ? (this._hand = hands.first)
                    : (this._hand = hands.last);
                this._currentHand = 1;
            }
            else {
                scope[0].initPos = (endX);
                scope[1].initPos = (x0);
                this._handRange = !this._handRange;
                this._currentHand = 0;
            }
        }
    };
    /**
     * Registers a callback that will be triggered when the value has changed.
     * Implemented as part of ControlValueAccessor.
     * @param fn On change callback function.
     */
    SliderComponent.prototype.registerOnChange = function (fn) {
        this._onModelChange = fn;
    };
    /**
     * Registers a callback that will be triggered when the control has been touched.
     * Implemented as part of ControlValueAccessor.
     * @param fn On touch callback function.
     */
    SliderComponent.prototype.registerOnTouched = function (fn) {
        this._onTouch = fn;
    };
    /**
     * Sets the model value. Implemented as part of ControlValueAccessor.
     * @param {any} value - value to be set to the model.
     */
    SliderComponent.prototype.writeValue = function (value) {
        if (value) {
            this.value = value;
            this._cd.markForCheck();
        }
    };
    /**
     * Toggles the disabled state of the component. Implemented as part of ControlValueAccessor.
     * @param {boolean} isDisabled - Whether the component should be disabled.
     */
    SliderComponent.prototype.setDisabledState = function (isDisabled) {
        this.disabled = isDisabled;
    };
    /**
     * update form model value and mark for check
     * @docs-private
     */
    SliderComponent.prototype._markForCheck = function () {
        if (this._onModelChange) {
            this._onModelChange(this.value);
        }
        if (this._onTouch) {
            this._onTouch(this.value);
        }
        this._cd.markForCheck();
    };
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], SliderComponent.prototype, "input", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], SliderComponent.prototype, "spinner", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], SliderComponent.prototype, "min", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], SliderComponent.prototype, "max", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], SliderComponent.prototype, "range", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], SliderComponent.prototype, "disabled", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Number)
    ], SliderComponent.prototype, "step", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object)
    ], SliderComponent.prototype, "value", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String),
        tslib_1.__metadata("design:paramtypes", [String])
    ], SliderComponent.prototype, "orientation", null);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], SliderComponent.prototype, "change", void 0);
    tslib_1.__decorate([
        OnChange(true),
        tslib_1.__metadata("design:type", Boolean)
    ], SliderComponent.prototype, "_handRange", void 0);
    tslib_1.__decorate([
        ViewChild('slider', { static: false }),
        tslib_1.__metadata("design:type", ElementRef)
    ], SliderComponent.prototype, "_slider", void 0);
    tslib_1.__decorate([
        ViewChild(SliderTrackerComponent, { static: false }),
        tslib_1.__metadata("design:type", SliderTrackerComponent)
    ], SliderComponent.prototype, "_tracker", void 0);
    tslib_1.__decorate([
        ViewChildren(SliderHandComponent),
        tslib_1.__metadata("design:type", QueryList)
    ], SliderComponent.prototype, "_hands", void 0);
    SliderComponent = tslib_1.__decorate([
        Component({
            selector: 'nb-slider',
            template: "<div class=\"nb-slider-inner\"><input *ngIf=\"!range && input\" [ngModel]=\"value\" [disabled]=\"disabled\" (ngModelChange)=\"inputValue($event)\"><div #slider class=\"nb-slider\" [class.nb-slider-vertical]=\"!orientation\" [class.nb-slider-disabled]=\"disabled\"><div class=\"nb-slider-rail\" (click)=\"clickHandler($event)\"></div><nb-slider-tracker [selected]=\"trackerSelected\" [position]=\"trackerPos\" [orientation]=\"orientation\"></nb-slider-tracker><nb-slider-hand *ngFor=\"let hand of hands;\" [max]=\"max\" [min]=\"min\" [step]=\"step\" [orientation]=\"orientation\" [initPos]=\"hand.initPos\" [limitMove]=\"limitMove\" [disabled]=\"disabled\" (change)=\"updateSlider($event)\"></nb-slider-hand></div><nb-spinner [maxValue]=\"max\" [(ngModel)]=\"value\" [minValue]=\"min\" *ngIf=\"!range && spinner\" (change)=\"inputValue($event)\"></nb-spinner></div>",
            encapsulation: ViewEncapsulation.None,
            changeDetection: ChangeDetectionStrategy.OnPush,
            preserveWhitespaces: false,
            providers: [SLIDER_VALUE_ACCESSOR],
            host: {
                'class': 'nb-widget'
            },
            exportAs: 'nbSlider'
        }),
        tslib_1.__metadata("design:paramtypes", [Renderer2,
            ChangeDetectorRef,
            ElementRef,
            SliderService])
    ], SliderComponent);
    return SliderComponent;
}());
export { SliderComponent };
//# sourceMappingURL=slider.js.map