import * as tslib_1 from "tslib";
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, forwardRef, ViewEncapsulation, ChangeDetectionStrategy } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { OnChange } from '../core/decorators';
/*
 * Provider Expression that allows component to register as a ControlValueAccessor.
 * This allows it to support [(ngModel)].
 * @docs-private
 */
var REGION_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(function () { return RegionComponent; }),
    multi: true
};
/**
 * Region Picker Component
 */
var RegionComponent = /** @class */ (function () {
    function RegionComponent(_cd) {
        var _this = this;
        this._cd = _cd;
        /** region value change event, emit an array of ids */
        this.change = new EventEmitter();
        /**
         * region datasource
         * @default []
         */
        this.datasource = [];
        /**
         * region value, an array of numbers
         * @default []
         */
        this.value = [];
        /**
         * region value change event
         * @docs-private
         */
        this.valueChange = new EventEmitter();
        /**
         * whether the region is disabled
         * @default false
         */
        this.disabled = false;
        /** timeout */
        this._timeout = 100;
        /** value map */
        this._valueMap = {};
        var self = this;
        self.valueChange.subscribe(function (newValue) {
            if (!newValue) {
                return;
            }
            // set default region value
            self.walkDatasource(self.datasource, function (item) {
                var checked = newValue.indexOf(+item.id) !== -1;
                self.updateNode(item, checked);
            });
            self.datasource = self.datasource.slice();
            _this._cd.markForCheck();
        });
    }
    RegionComponent.prototype.ngOnInit = function () {
        // compute city rows, when show city panel, we want two split cities in three columns
        this.walkDatasource(this.datasource, function (item) {
            if (item.children) {
                var rows = Math.ceil(item.children.length / 3);
                var rowArray = [];
                for (var i = 0; i < rows; i++) {
                    rowArray.push(i);
                }
                item.cityRows = rowArray;
            }
        });
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.onToggleNode = function (item, checked) {
        this.updateNode(item, checked);
        this.getSelectedValue();
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.updateNode = function (node, checked) {
        node.intermediate = false;
        node.selected = checked;
        if (node.selected) {
            this._valueMap[node.id] = true;
        }
        else {
            delete this._valueMap[node.id];
        }
        this.setChildren(node.children || [], checked);
        var parentNode = this.findParentNode(node.id);
        if (parentNode) {
            if (node.children) {
                parentNode.intermediate = node.children.some(function (v) { return !!v.selected; });
            }
            this.updateParent(parentNode, checked);
        }
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.setChildren = function (children, checked) {
        var self = this;
        if (children && children.length) {
            children.forEach(function (v) {
                v.intermediate = false;
                v.selected = checked;
                if (v.selected) {
                    self._valueMap[v.id] = true;
                }
                else {
                    delete self._valueMap[v.id];
                }
                self.setChildren(v.children || [], checked);
            });
        }
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.updateParent = function (currentNode, checked) {
        var self = this;
        if (currentNode.children) {
            var allChecked = currentNode.children.every(function (v) { return !!v.selected; });
            var someChecked = currentNode.children.some(function (v) { return !!v.selected || !!v.intermediate; });
            if (allChecked) {
                currentNode.selected = allChecked;
                currentNode.intermediate = false;
            }
            else if (someChecked) {
                currentNode.intermediate = true;
                currentNode.selected = false;
            }
            else {
                currentNode.selected = false;
                currentNode.intermediate = false;
            }
            if (currentNode.selected) {
                this._valueMap[currentNode.id] = true;
            }
            else {
                delete this._valueMap[currentNode.id];
            }
            var parentNode = self.findParentNode(currentNode.id);
            if (parentNode) {
                if (currentNode.intermediate) {
                    parentNode.intermediate = true;
                }
                this.updateParent(parentNode, checked);
            }
        }
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.getSelectedValue = function () {
        this.value = Object.keys(this._valueMap).map(function (v) { return +v; });
        this._markForCheck();
        this.change.emit(this.value);
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.walkDatasource = function (datasource, fn) {
        var self = this;
        datasource.forEach(function (item) {
            fn(item);
            if (item.children) {
                self.walkDatasource(item.children, fn);
            }
        });
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.findParentNode = function (id) {
        var parentNode;
        this.walkDatasource(this.datasource, function (item) {
            if (item.children) {
                item.children.forEach(function (child) {
                    if (child.id === id) {
                        parentNode = item;
                        return;
                    }
                });
            }
        });
        return parentNode;
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.onShowCity = function (overlay, event) {
        clearTimeout(this._timer);
        if (this._previousProvince) {
            this._previousProvince.hide();
        }
        overlay.show();
        this._previousProvince = overlay;
        event.stopImmediatePropagation();
        event.preventDefault();
    };
    RegionComponent.prototype.onClearTimeout = function () {
        clearTimeout(this._timer);
    };
    RegionComponent.prototype.onHideOverlay = function (overlay) {
        this._timer = setTimeout(function () {
            if (overlay) {
                overlay.hide();
            }
        }, this._timeout);
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.onHideCity = function (overlay) {
        this._timer = setTimeout(function () {
            if (overlay) {
                overlay.hide();
            }
        }, this._timeout);
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.onOverPanel = function (province) {
        clearTimeout(this._timer);
        province.showCity = true;
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.onLeavePanel = function (province) {
        var _this = this;
        this._timer = setTimeout(function () {
            province.showCity = false;
            _this._cd.markForCheck();
        }, this._timeout);
    };
    /**
     * @docs-private
     */
    RegionComponent.prototype.getSelectedCount = function (cities) {
        return cities.filter(function (v) { return !!v.selected; }).length;
    };
    RegionComponent.prototype.ngOnDestroy = function () {
        clearTimeout(this._timer);
    };
    /**
     * Registers a callback that will be triggered when the value has changed.
     * Implemented as part of ControlValueAccessor.
     * @param fn On change callback function.
     */
    RegionComponent.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.
     */
    RegionComponent.prototype.registerOnTouched = function (fn) {
        this._onTouch = fn;
    };
    /**
     * Sets the model value. Implemented as part of ControlValueAccessor.
     * @param value Value to be set to the model.
     */
    RegionComponent.prototype.writeValue = function (value) {
        if (value != null) {
            this.value = value;
        }
    };
    /**
     * Toggles the disabled state of the component. Implemented as part of ControlValueAccessor.
     * @param isDisabled Whether the component should be disabled.
     */
    RegionComponent.prototype.setDisabledState = function (isDisabled) {
        this.disabled = isDisabled;
        this._cd.markForCheck();
    };
    /**
     * update form model value and mark for check
     */
    RegionComponent.prototype._markForCheck = function () {
        if (this._onModelChange) {
            this._onModelChange(this.value);
        }
        if (this._onTouch) {
            this._onTouch(this.value);
        }
        this._cd.markForCheck();
    };
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], RegionComponent.prototype, "change", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array)
    ], RegionComponent.prototype, "datasource", void 0);
    tslib_1.__decorate([
        OnChange(),
        Input(),
        tslib_1.__metadata("design:type", Array)
    ], RegionComponent.prototype, "value", void 0);
    tslib_1.__decorate([
        OnChange(true),
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], RegionComponent.prototype, "disabled", void 0);
    RegionComponent = tslib_1.__decorate([
        Component({
            selector: 'nb-region',
            template: "<div class=\"nb-region-section\" *ngFor=\"let rootItem of datasource\"><div class=\"nb-region-section-title\"><nb-checkbox [value]=\"rootItem.id\" [disabled]=\"disabled || rootItem.disabled\" [checked]=\"rootItem.selected\" [intermediate]=\"rootItem.intermediate\" (change)=\"onToggleNode(rootItem, $event)\">{{ rootItem.label }}</nb-checkbox></div><div *ngIf=\"rootItem.children\" class=\"nb-region-section-content\"><div *ngFor=\"let district of rootItem.children\" class=\"nb-region-district\"><div class=\"nb-region-district-title\"><nb-checkbox [value]=\"district.id\" [disabled]=\"disabled || district.disabled\" [checked]=\"district.selected\" [intermediate]=\"district.intermediate\" (change)=\"onToggleNode(district, $event)\">{{ district.label }}</nb-checkbox></div><div *ngIf=\"district.children\" class=\"nb-region-district-content\"><div *ngFor=\"let province of district.children\" class=\"nb-region-province\" nbOverlayOrigin #origin=\"nbOverlayOrigin\"><div class=\"nb-region-province-title\" (mouseover)=\"onShowCity(overlay, $event)\" (mouseout)=\"onHideCity(overlay)\"><nb-checkbox [value]=\"province.id\" [disabled]=\"disabled || province.disabled\" [checked]=\"province.selected\" [intermediate]=\"province.intermediate\" (change)=\"onToggleNode(province, $event)\">{{ province.label }}<ng-container *ngIf=\"province.children\"><span class=\"province-count\">({{ getSelectedCount(province.children) }}/{{ province.children.length }})</span></ng-container></nb-checkbox></div><nb-overlay #overlay [origin]=\"origin\" placement=\"bottom-left\"><div class=\"nb-region-city-panel\" (mouseover)=\"onClearTimeout()\" (mouseout)=\"onHideOverlay(overlay)\"><div class=\"nb-region-city-table\"><ng-container *ngFor=\"let rowIndex of province.cityRows\"><div class=\"nb-region-city-row\"><div *ngFor=\"let city of province.children.slice(rowIndex * 3, rowIndex * 3 + 3)\" class=\"nb-region-city-cell\"><nb-checkbox [value]=\"city.id\" [disabled]=\"disabled || city.disabled\" [checked]=\"city.selected\" [intermediate]=\"city.intermediate\" (change)=\"onToggleNode(city, $event)\">{{ city.label }}</nb-checkbox></div></div></ng-container></div></div></nb-overlay></div></div></div></div></div>",
            encapsulation: ViewEncapsulation.None,
            changeDetection: ChangeDetectionStrategy.OnPush,
            preserveWhitespaces: false,
            providers: [REGION_VALUE_ACCESSOR],
            host: {
                'class': 'nb-widget nb-region'
            },
            exportAs: 'nbRegion'
        }),
        tslib_1.__metadata("design:paramtypes", [ChangeDetectorRef])
    ], RegionComponent);
    return RegionComponent;
}());
export { RegionComponent };
//# sourceMappingURL=region.js.map