import * as tslib_1 from "tslib";
import { Component, Input, Output, EventEmitter, ViewEncapsulation, ChangeDetectionStrategy, forwardRef, ChangeDetectorRef, TemplateRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { TransferService } from '../transfer/transfer.service';
/*
 * Provider Expression that allows component to register as a ControlValueAccessor.
 * This allows it to support [(ngModel)].
 * @docs-private
 */
var TREE_TRANSFER_VALUE_ACCESSOR = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(function () { return TreeTransferComponent; }),
    multi: true
};
var TreeTransferComponent = /** @class */ (function () {
    function TreeTransferComponent(_cd, service) {
        this._cd = _cd;
        this.service = service;
        /** get selected value event */
        this.getValue = new EventEmitter();
        /** search event */
        this.searchValue = new EventEmitter();
        /** tree node expand event */
        this.onExpandNode = new EventEmitter();
        /** tree node expand event */
        this.onExtendData = new EventEmitter();
        /**
         * candidate list data
         * @default []
         */
        this.candidateData = [];
        /**
         * selected list data
         * @default []
         */
        this.selectedData = [];
        /**
         * Whether the transfer is disabled
         * @default false
         */
        this.disabled = false;
        /**
         * Whether the transfer is can all-trans
         * @default false
         */
        this.allSelectLink = true;
        /**
         * Whether the transfer is can all-delete
         * @default false
         */
        this.allDeleteLink = true;
        /**
         * Whether the transfer is can all-trans
         * @default false
         */
        this.candidateSearch = true;
        /**
         * Whether the transfer is can all-delete
         * @default false
         */
        this.selectedSearch = true;
        /**
         * Whether the transfer is can all-trans
         */
        this.candidateTitle = '备选列表';
        /**
         * Whether the transfer is can all-delete
         */
        this.selectedTitle = '已选列表';
        /**
         * 右侧提示文案
         */
        this.rightNotice = '请从左侧选择内容';
        /**
         * all options count
         * @docs-private
         */
        this.candidateCount = 0;
        /**
         * all selected options count
         * @docs-private
         */
        this.selectedCount = 0;
        /**
         * candidate trans to list data
         * @docs-private
         */
        this._candidateNodeList = {};
        /**
         * selected trans to list data
         * @docs-private
         */
        this._selectedNodeList = {};
        /**
         * selected options's id as list
         * @docs-private
         */
        this.value = [];
        /**
         * the search keyword
         * @docs-private
         */
        this.keyword = '';
    }
    TreeTransferComponent.prototype.ngOnChanges = function (changes) {
        if (changes['candidateData'] || changes['selectedData']) {
            this.initTransfer();
        }
    };
    /**
     * init transfer
     * @docs-private
     */
    TreeTransferComponent.prototype.initTransfer = function () {
        this.initTree(this.candidateData, 'candidate');
        this.initTree(this.selectedData, 'selected');
        this.initCount();
        this.clearDataListSelected();
        this.countSelectedNodes(this.candidateData);
        this.service.sendMsg({ candidateCount: this.candidateCount, selectedCount: this.selectedCount });
        this._candidateNodeList = {};
        this.transferTreeToList(this.candidateData, 'candidate');
        this._selectedNodeList = {};
        this.transferTreeToList(this.selectedData, 'selected');
    };
    /**
     * set the show attribute to true
     * @docs-private
     */
    TreeTransferComponent.prototype.initTree = function (treeData, mode) {
        var _this = this;
        if (treeData && treeData.length) {
            treeData.forEach(function (node) {
                node.show = mode === 'candidate' ? true : node.isSelected;
                node.isExpanded = mode === 'candidate' ? node.isExpanded : node.isSelected;
                if (node.children && node.children.length) {
                    _this.initTree(node.children, mode);
                }
            });
        }
    };
    /**
     * set the show attribute to false, for fitler
     * @docs-private
     */
    TreeTransferComponent.prototype.transferTreeToList = function (treeData, mode, isForSearch) {
        var _this = this;
        if (treeData && treeData.length) {
            treeData.forEach(function (node) {
                node.show = !isForSearch ? node.show : false;
                if (mode === 'candidate') {
                    _this._candidateNodeList[node.id] = node;
                }
                if (mode === 'selected') {
                    _this._selectedNodeList[node.id] = node;
                }
                if (node.children && node.children.length) {
                    _this.transferTreeToList(node.children, mode, isForSearch);
                }
            });
        }
    };
    /**
     * init count
     * @docs-private
     */
    TreeTransferComponent.prototype.initCount = function () {
        this.candidateCount = 0;
        this.selectedCount = 0;
    };
    /**
     * clear selected options's id as list
     * @docs-private
     */
    TreeTransferComponent.prototype.clearDataListSelected = function () {
        this.value = [];
    };
    /**
     * calculate selected options count and push in value
     * @docs-private
     */
    TreeTransferComponent.prototype.countSelectedNodes = function (tree) {
        var _this = this;
        if (tree && tree.length) {
            tree.forEach(function (node) {
                if (node.isSelected && node.selectable && !_this.hasChildren(node)) {
                    _this.selectedCount++;
                    _this.value.push(node);
                }
                if (node.selectable && !_this.hasChildren(node)) {
                    _this.candidateCount++;
                }
                if (node.children && node.children.length) {
                    _this.countSelectedNodes(node.children);
                }
            });
        }
    };
    /**
     * judge node whether have child
     * @docs-private
     */
    TreeTransferComponent.prototype.hasChildren = function (node) {
        if (node.children && node.children.length) {
            return true;
        }
        return false;
    };
    /**
     * @param   {string} part     搜索的字符串片段
     * @param   {string} complete 完整的字符串
     * @returns boolean 字符串片段在字符串中的位置
     */
    TreeTransferComponent.prototype.search = function (keyWord, fullWord) {
        var key = new RegExp(keyWord.trim(), 'i');
        return fullWord ? fullWord.match(key) : false;
    };
    /**
     * fitler candidate or selected list by key word
     * @docs-private
     */
    TreeTransferComponent.prototype.searchByKeyWord = function (e) {
        var event = this.keyword = e.event;
        var mode = e.mode;
        // 向组件外部暴露搜索事件
        this.searchValue.emit(event);
        /**
         * when the keyword is null, show the all list data
         * @docs-private
         */
        if (!event) {
            if (mode === 'candidate') {
                this.candidateData = this.getRootNodes(this._candidateNodeList);
                this.initTree(this.candidateData, 'candidate');
                return;
            }
            if (mode === 'selected') {
                this.selectedData = this.getRootNodes(this._selectedNodeList);
                this.initTree(this.selectedData, 'selected');
                return;
            }
        }
        this.resetNodeList(mode);
        if (mode === 'candidate') {
            for (var key in this._candidateNodeList) {
                if (this._candidateNodeList.hasOwnProperty(key)) {
                    var node = this._candidateNodeList[key];
                    if (node.name && this.search(event, node.name)) {
                        this.searchNodes(node, mode, event);
                    }
                }
            }
            this.candidateData = [].concat(this.candidateData);
        }
        if (mode === 'selected') {
            for (var key in this._selectedNodeList) {
                if (this._selectedNodeList.hasOwnProperty(key)) {
                    var node = this._selectedNodeList[key];
                    if (node.name && this.search(event, node.name) && node.isSelected) {
                        this.searchNodes(node, mode, event);
                    }
                }
            }
            this.selectedData = [].concat(this.selectedData);
        }
    };
    /**
     * reset candidate or selected node list
     * @docs-private
     */
    TreeTransferComponent.prototype.resetNodeList = function (mode) {
        if (mode === 'candidate') {
            this._candidateNodeList = {};
        }
        if (mode === 'selected') {
            this._selectedNodeList = {};
        }
        var treeData = mode === 'candidate' ? this.candidateData : this.selectedData;
        this.transferTreeToList(treeData, mode, true);
    };
    /**
     * get node the searched by key word
     * @docs-private
     */
    TreeTransferComponent.prototype.searchNodes = function (node, mode, keyword, type) {
        this.setSearchNodes(node);
        if (node.children && node.name && this.search(keyword, node.name)) {
            this.setSearchNodesChildren(node.children, mode, type);
        }
        if (node.parent) {
            var nodeParent = void 0;
            nodeParent = this.getTargetNode(node.parent, mode);
            if (nodeParent) {
                if (nodeParent.parent) {
                    this.searchNodes(nodeParent, mode, keyword, type);
                }
                else {
                    this.setSearchNodes(nodeParent);
                }
            }
        }
    };
    /**
     * get target node in xxNodeList
     * @docs-private
     */
    TreeTransferComponent.prototype.getTargetNode = function (treeNode, mode) {
        var targetNode;
        var nodeList = mode === 'candidate' ? this._candidateNodeList : this._selectedNodeList;
        targetNode = nodeList[treeNode.id];
        return targetNode;
    };
    /**
     * set searchnode is show or not
     * @docs-private
     */
    TreeTransferComponent.prototype.setSearchNodes = function (node) {
        node.show = true;
        node.isExpanded = true;
    };
    /**
     * set searchnode's children node is show or not
     * @docs-private
     */
    TreeTransferComponent.prototype.setSearchNodesChildren = function (nodeChildren, mode, type) {
        var _this = this;
        nodeChildren.forEach(function (child) {
            if (child.children) {
                _this.setSearchNodesChildren(child.children, mode, type);
            }
            if (mode === 'selected') {
                if (type === 'transAll') {
                    child.show = true;
                }
                if (child.isSelected) {
                    child.show = true;
                }
            }
            else if (mode === 'candidate') {
                child.show = true;
            }
        });
    };
    /**
     * get target node root node
     * @docs-private
     */
    TreeTransferComponent.prototype.getRootNodes = function (nodes) {
        return Object.values(nodes).filter(function (node) {
            return !node.parent;
        });
    };
    /**
     * tree node expand event
     * @docs-private
     */
    TreeTransferComponent.prototype.expandNode = function (event) {
        this.onExpandNode.emit(event);
    };
    /**
     * throw the node click event out
     * @docs-private
     */
    TreeTransferComponent.prototype.transNode = function (event, mode) {
        var chkVal = mode === 'selected' ? true : false;
        var targetNode = this.getTargetNode(event, mode);
        if (targetNode) {
            this.propagateDown(targetNode, chkVal, mode, 'selectedTree', 'single');
            this.propagateUp(targetNode, chkVal, mode);
        }
        // 备选树
        if (this.keyword) {
            this.resetNodeList('candidate');
            for (var key in this._candidateNodeList) {
                if (this._candidateNodeList.hasOwnProperty(key)) {
                    var node = this._candidateNodeList[key];
                    if (node.name && this.search(this.keyword, node.name)) {
                        this.searchNodes(node, 'candidate', this.keyword);
                    }
                }
            }
            this.candidateData = [].concat(this.candidateData);
        }
        var rootNodes = this.renderTargetNode(mode, mode === 'selected' ? 'selectedTree' : 'candidateTree');
        if (mode === 'candidate') {
            this.candidateData = rootNodes;
        }
        else {
            this.selectedData = rootNodes;
        }
        this.initCount();
        this.clearDataListSelected();
        this.countSelectedNodes(this.candidateData);
        this.service.sendMsg({ candidateCount: this.candidateCount, selectedCount: this.selectedCount });
        this.getValue.emit(this.value);
        this._markForCheck();
    };
    /**
     * propagate down node 'isSelected' value
     * @docs-private
     */
    TreeTransferComponent.prototype.propagateDown = function (node, chkVal, mode, currentTree, transType) {
        if (mode === 'selected' && !this.keyword && (node.selectable || this.hasChildren(node))) {
            node.isSelected = chkVal;
        }
        // 已选树，单点穿梭
        if (mode === 'selected' && currentTree === 'selectedTree'
            && (node.selectable || this.hasChildren(node)) && transType === 'single') {
            node.isSelected = chkVal;
        }
        // 已选树，全穿梭
        if (currentTree === 'selectedTree' && (node.selectable || this.hasChildren(node)) && node.show) {
            node.isSelected = chkVal;
        }
        // 备选树
        if (currentTree === 'candidateTree' && !chkVal) {
            node.isSelected = chkVal;
        }
        // 备选树，带搜索内容
        if (currentTree === 'candidateTree' && node.selectable && node.show
            && (this.search(this.keyword, node.name) || node.parent && this.getTargetNode(node.parent, 'candidate').isSelected)) {
            node.isSelected = chkVal;
        }
        if (node.children && node.children.length) {
            for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
                var child = _a[_i];
                this.propagateDown(child, chkVal, mode, currentTree, transType);
            }
        }
    };
    /**
     * propagate up node 'isSelected' value
     * @docs-private
     */
    TreeTransferComponent.prototype.propagateUp = function (node, chkVal, mode) {
        node.isSelected = chkVal;
        var nodeParent;
        if (node.parent) {
            nodeParent = this.getTargetNode(node.parent, mode);
            if (nodeParent) {
                if (nodeParent.parent) {
                    this.propagateUp(nodeParent, chkVal, mode);
                }
                else {
                    nodeParent.isSelected = chkVal;
                }
            }
        }
    };
    /**
     * render target node 'isExpanded' and 'isSelected' attribute
     * @docs-private
     */
    TreeTransferComponent.prototype.renderTargetNode = function (mode, currentTree) {
        var nodeList = mode === 'candidate' ? this._candidateNodeList : this._selectedNodeList;
        var rootNodes = [];
        rootNodes = this.getRootNodes(nodeList);
        for (var _i = 0, rootNodes_1 = rootNodes; _i < rootNodes_1.length; _i++) {
            var root = rootNodes_1[_i];
            this.renderTransTree(root, mode, currentTree);
        }
        return rootNodes;
    };
    /**
     * render target's root node and root's children the 'isExpanded' and 'isSelected' attribute
     * @docs-private
     */
    TreeTransferComponent.prototype.renderTransTree = function (root, mode, currentTree) {
        if (currentTree === 'selectedTree') {
            root.show = root.isSelected;
        }
        root.isExpanded = root.show;
        if (root.children && root.children.length) {
            for (var _i = 0, _a = root.children; _i < _a.length; _i++) {
                var child = _a[_i];
                this.renderTransTree(child, mode, currentTree);
            }
        }
    };
    /**
     * trans all options to Selected or not
     * @docs-private
     */
    TreeTransferComponent.prototype.transAll = function (e) {
        if (this.disabled) {
            return;
        }
        var mode = e.mode;
        var chkVal = e.chkVal;
        // 当是‘candidate’mode，并且没有任何选中，禁止再次执行取消全部选中的操作过程
        if (mode === 'candidate' && this.selectedCount === 0) {
            return;
        }
        // 当是‘selected’mode，并且已经全部选中，禁止再次执行全部选中过程
        if (mode === 'selected' && this.selectedCount === this.candidateCount) {
            return;
        }
        // 备选树
        var rootCandidateNodes = [];
        rootCandidateNodes = this.renderRootNodes(this.candidateData, mode, chkVal, 'candidateTree');
        this.candidateData = rootCandidateNodes;
        // 已选树
        var rootSelectedNodes = [];
        if (this.keyword && chkVal) {
            this.resetNodeList('selected');
            for (var key in this._selectedNodeList) {
                if (this._selectedNodeList.hasOwnProperty(key)) {
                    var node = this._selectedNodeList[key];
                    if (node.name && this.search(this.keyword, node.name)) {
                        this.searchNodes(node, 'selected', this.keyword, 'transAll');
                    }
                }
            }
            this.selectedData = [].concat(this.selectedData);
        }
        rootSelectedNodes = this.renderRootNodes(this.selectedData, mode, chkVal, 'selectedTree');
        this.selectedData = rootSelectedNodes;
        this.initCount();
        this.clearDataListSelected();
        this.countSelectedNodes(this.selectedData);
        this.service.sendMsg({ candidateCount: this.candidateCount, selectedCount: this.selectedCount });
        this.getValue.emit(this.value);
        this._markForCheck();
    };
    /**
     * render root nodes and root's children the 'isExpanded' and 'isSelected' attribute
     * @docs-private
     */
    TreeTransferComponent.prototype.renderRootNodes = function (treeData, mode, chkVal, currentTree) {
        var rootNodes = this.getRootNodes(treeData);
        for (var _i = 0, rootNodes_2 = rootNodes; _i < rootNodes_2.length; _i++) {
            var root = rootNodes_2[_i];
            this.propagateDown(root, chkVal, mode, currentTree);
            this.renderTransTree(root, mode, currentTree);
        }
        return rootNodes;
    };
    /**
     * throw out extend data event
     */
    TreeTransferComponent.prototype.extendData = function () {
        if (this.disabled) {
            return;
        }
        this.onExtendData.emit();
    };
    /**
     * Registers a callback that will be triggered when the value has changed.
     * Implemented as part of ControlValueAccessor.
     * @param fn On change callback function.
     */
    TreeTransferComponent.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.
     */
    TreeTransferComponent.prototype.registerOnTouched = function (fn) {
        this._onTouch = fn;
    };
    /**
     * set tree-transfer model value. Implemented as part of ControlValueAccessor.
     * @docs-private
     */
    TreeTransferComponent.prototype.writeValue = function (value) {
        if (value != null) {
            this.value = value;
            this._cd.markForCheck();
        }
    };
    /**
     * Toggles the disabled state of the component. Implemented as part of ControlValueAccessor.
     * @param isDisabled Whether the component should be disabled.
     */
    TreeTransferComponent.prototype.setDisabledState = function (isDisabled) {
        this.disabled = isDisabled;
    };
    /**
     * update form model value and mark for check
     * @docs-private
     */
    TreeTransferComponent.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)
    ], TreeTransferComponent.prototype, "getValue", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], TreeTransferComponent.prototype, "searchValue", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], TreeTransferComponent.prototype, "onExpandNode", void 0);
    tslib_1.__decorate([
        Output(),
        tslib_1.__metadata("design:type", EventEmitter)
    ], TreeTransferComponent.prototype, "onExtendData", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array)
    ], TreeTransferComponent.prototype, "candidateData", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Array)
    ], TreeTransferComponent.prototype, "selectedData", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TreeTransferComponent.prototype, "disabled", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TreeTransferComponent.prototype, "allSelectLink", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TreeTransferComponent.prototype, "allDeleteLink", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TreeTransferComponent.prototype, "candidateSearch", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Boolean)
    ], TreeTransferComponent.prototype, "selectedSearch", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TreeTransferComponent.prototype, "candidateTitle", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TreeTransferComponent.prototype, "selectedTitle", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", String)
    ], TreeTransferComponent.prototype, "rightNotice", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", Object)
    ], TreeTransferComponent.prototype, "addLink", void 0);
    tslib_1.__decorate([
        Input(),
        tslib_1.__metadata("design:type", TemplateRef)
    ], TreeTransferComponent.prototype, "optionTpl", void 0);
    TreeTransferComponent = tslib_1.__decorate([
        Component({
            selector: 'nb-tree-transfer',
            template: "<nb-transfer (searchValue)=\"searchByKeyWord($event)\" (onTransAll)=\"transAll($event)\" (onExtendData)=\"extendData()\" [allSelectLink]=\"allSelectLink\" [allDeleteLink]=\"allDeleteLink\" [candidateSearch]=\"candidateSearch\" [selectedSearch]=\"selectedSearch\" [candidateTitle]=\"candidateTitle\" [selectedTitle]=\"selectedTitle\" [addLink]=\"addLink\" [disabled]=\"disabled\"><nb-transfer-candidate><nb-tree [treeData]=\"candidateData\" selectionMode=\"candidate\" [disabled]=\"disabled\" (onExpandNode)=\"expandNode($event)\" (onNodeSelect)=\"transNode($event, 'selected')\" [optionTpl]=\"optionTpl\"></nb-tree></nb-transfer-candidate><nb-transfer-selected><nb-tree *ngIf=\"selectedCount\" [treeData]=\"selectedData\" selectionMode=\"selected\" [disabled]=\"disabled\" (onNodeSelect)=\"transNode($event, 'candidate')\" [optionTpl]=\"optionTpl\"></nb-tree><div *ngIf=\"!selectedCount\" class=\"nb-transfer-no-data\">{{ rightNotice }}</div></nb-transfer-selected></nb-transfer>",
            encapsulation: ViewEncapsulation.None,
            changeDetection: ChangeDetectionStrategy.OnPush,
            preserveWhitespaces: false,
            providers: [TREE_TRANSFER_VALUE_ACCESSOR, TransferService],
            host: {
                'class': 'nb-widget nb-tree-transfer'
            },
            exportAs: 'nbTreeTransfer'
        }),
        tslib_1.__metadata("design:paramtypes", [ChangeDetectorRef,
            TransferService])
    ], TreeTransferComponent);
    return TreeTransferComponent;
}());
export { TreeTransferComponent };
//# sourceMappingURL=tree-transfer.js.map