import { __assign, __extends, __rest } from "tslib";
import { difference, each, isNil, keys, mix, pick } from '@antv/util';
import { propagationDelegate } from '../util/event';
import { applyMatrix2BBox, getMatrixByTranslate } from '../util/matrix';
import { getBBoxWithClip, updateClip } from '../util/util';
import Component from './component';
var STATUS_UPDATE = 'update_status';
var COPY_PROPERTIES = ['visible', 'tip', 'delegateObject']; // 更新对象时需要复制的属性
var COPY_PROPERTIES_EXCLUDES = ['container', 'group', 'shapesMap', 'isRegister', 'isUpdating', 'destroyed']; // 更新子组件时排除的属性
var GroupComponent = /** @class */ (function (_super) {
    __extends(GroupComponent, _super);
    function GroupComponent() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    GroupComponent.prototype.getDefaultCfg = function () {
        var cfg = _super.prototype.getDefaultCfg.call(this);
        return __assign(__assign({}, cfg), { container: null, 
            /**
             * @private
             * 缓存图形的 Map
             */
            shapesMap: {}, group: null, capture: true, 
            /**
             * @private 组件或者图形是否允许注册
             * @type {false}
             */
            isRegister: false, 
            /**
             * @private 是否正在更新
             * @type {false}
             */
            isUpdating: false, 
            /**
             * @private
             * 是否初始状态，一旦 render，update 后，这个状态就变成 false, clear 后恢复
             */
            isInit: true });
    };
    GroupComponent.prototype.remove = function () {
        this.clear();
        var group = this.get('group');
        group.remove();
    };
    GroupComponent.prototype.clear = function () {
        var group = this.get('group');
        group.clear();
        this.set('shapesMap', {});
        this.clearOffScreenCache();
        this.set('isInit', true);
    };
    GroupComponent.prototype.getChildComponentById = function (id) {
        var group = this.getElementById(id);
        var inst = group && group.get('component');
        return inst;
    };
    GroupComponent.prototype.getElementById = function (id) {
        return this.get('shapesMap')[id];
    };
    GroupComponent.prototype.getElementByLocalId = function (localId) {
        var id = this.getElementId(localId);
        return this.getElementById(id);
    };
    GroupComponent.prototype.getElementsByName = function (name) {
        var rst = [];
        each(this.get('shapesMap'), function (elem) {
            if (elem.get('name') === name) {
                rst.push(elem);
            }
        });
        return rst;
    };
    GroupComponent.prototype.getContainer = function () {
        return this.get('container');
    };
    GroupComponent.prototype.updateInner = function (cfg) {
        // this.updateInner();
        // this.set('isUpdating', false);
        this.offScreenRender();
        if (this.get('updateAutoRender')) {
            this.render();
        }
    };
    GroupComponent.prototype.render = function () {
        var offScreenGroup = this.get('offScreenGroup');
        if (!offScreenGroup) {
            offScreenGroup = this.offScreenRender();
        }
        var group = this.get('group');
        this.updateElements(offScreenGroup, group);
        this.deleteElements();
        this.applyOffset();
        if (!this.get('eventInitted')) {
            this.initEvent();
            this.set('eventInitted', true);
        }
        this.set('isInit', false);
    };
    GroupComponent.prototype.show = function () {
        var group = this.get('group');
        group.show();
        this.set('visible', true);
    };
    GroupComponent.prototype.hide = function () {
        var group = this.get('group');
        group.hide();
        this.set('visible', false);
    };
    GroupComponent.prototype.setCapture = function (capture) {
        var group = this.get('group');
        group.set('capture', capture);
        this.set('capture', capture);
    };
    GroupComponent.prototype.destroy = function () {
        this.removeEvent();
        this.remove();
        _super.prototype.destroy.call(this);
    };
    GroupComponent.prototype.getBBox = function () {
        return this.get('group').getCanvasBBox();
    };
    GroupComponent.prototype.getLayoutBBox = function () {
        var group = this.get('group');
        // 防止被 clear 了，offScreenBBox 不存在
        var bbox = this.getInnerLayoutBBox();
        var matrix = group.getTotalMatrix();
        if (matrix) {
            bbox = applyMatrix2BBox(matrix, bbox);
        }
        return bbox; // 默认返回 getBBox，不同的组件内部单独实现
    };
    // 复写 on, off, emit 透传到 group
    GroupComponent.prototype.on = function (evt, callback, once) {
        var group = this.get('group');
        group.on(evt, callback, once);
        return this;
    };
    GroupComponent.prototype.off = function (evt, callback) {
        var group = this.get('group');
        group && group.off(evt, callback);
        return this;
    };
    GroupComponent.prototype.emit = function (eventName, eventObject) {
        var group = this.get('group');
        group.emit(eventName, eventObject);
    };
    GroupComponent.prototype.init = function () {
        _super.prototype.init.call(this);
        if (!this.get('group')) {
            this.initGroup();
        }
        this.offScreenRender(); // 绘制离屏 group
    };
    // 获取组件内部布局占的包围盒
    GroupComponent.prototype.getInnerLayoutBBox = function () {
        return this.get('offScreenBBox') || this.get('group').getBBox();
    };
    // 抛出委托对象
    GroupComponent.prototype.delegateEmit = function (eventName, eventObject) {
        var group = this.get('group');
        eventObject.target = group;
        group.emit(eventName, eventObject);
        propagationDelegate(group, eventName, eventObject);
    };
    // 创建离屏的 group ,不添加在 canvas 中
    GroupComponent.prototype.createOffScreenGroup = function () {
        var group = this.get('group');
        var GroupClass = group.getGroupBase(); // 获取分组的构造函数
        var newGroup = new GroupClass({
            delegateObject: this.getDelegateObject(),
        });
        return newGroup;
    };
    // 应用 offset
    GroupComponent.prototype.applyOffset = function () {
        var offsetX = this.get('offsetX');
        var offsetY = this.get('offsetY');
        this.moveElementTo(this.get('group'), {
            x: offsetX,
            y: offsetY,
        });
    };
    GroupComponent.prototype.initGroup = function () {
        var container = this.get('container');
        this.set('group', container.addGroup({
            id: this.get('id'),
            name: this.get('name'),
            capture: this.get('capture'),
            visible: this.get('visible'),
            isComponent: true,
            component: this,
            delegateObject: this.getDelegateObject(),
        }));
    };
    // 离屏渲染
    GroupComponent.prototype.offScreenRender = function () {
        this.clearOffScreenCache();
        var offScreenGroup = this.createOffScreenGroup();
        this.renderInner(offScreenGroup);
        this.set('offScreenGroup', offScreenGroup);
        // 包含包围盒的 bbox
        this.set('offScreenBBox', getBBoxWithClip(offScreenGroup));
        return offScreenGroup;
    };
    /**
     * @protected
     * 在组件上添加分组，主要解决 isReigeter 的问题
     * @param {IGroup} parent 父元素
     * @param {object} cfg    分组的配置项
     */
    GroupComponent.prototype.addGroup = function (parent, cfg) {
        this.appendDelegateObject(parent, cfg);
        var group = parent.addGroup(cfg);
        if (this.get('isRegister')) {
            this.registerElement(group);
        }
        return group;
    };
    /**
     * @protected
     * 在组件上添加图形，主要解决 isReigeter 的问题
     * @param {IGroup} parent 父元素
     * @param {object} cfg    分组的配置项
     */
    GroupComponent.prototype.addShape = function (parent, cfg) {
        this.appendDelegateObject(parent, cfg);
        var shape = parent.addShape(cfg);
        if (this.get('isRegister')) {
            this.registerElement(shape);
        }
        return shape;
    };
    /**
     * 在组件上添加子组件
     *
     * @param parent 父元素
     * @param cfg 子组件配置项
     */
    GroupComponent.prototype.addComponent = function (parent, cfg) {
        var id = cfg.id, Ctor = cfg.component, restCfg = __rest(cfg, ["id", "component"]);
        // @ts-ignore
        var inst = new Ctor(__assign(__assign({}, restCfg), { id: id, container: parent, updateAutoRender: this.get('updateAutoRender') }));
        inst.init();
        inst.render();
        if (this.get('isRegister')) {
            this.registerElement(inst.get('group'));
        }
        return inst;
    };
    GroupComponent.prototype.initEvent = function () { };
    GroupComponent.prototype.removeEvent = function () {
        var group = this.get('group');
        group.off();
    };
    GroupComponent.prototype.getElementId = function (localId) {
        var id = this.get('id'); // 组件的 Id
        var name = this.get('name'); // 组件的名称
        return id + "-" + name + "-" + localId;
    };
    GroupComponent.prototype.registerElement = function (element) {
        var id = element.get('id');
        this.get('shapesMap')[id] = element;
    };
    GroupComponent.prototype.unregisterElement = function (element) {
        var id = element.get('id');
        delete this.get('shapesMap')[id];
    };
    // 移动元素
    GroupComponent.prototype.moveElementTo = function (element, point) {
        var matrix = getMatrixByTranslate(point);
        element.attr('matrix', matrix);
    };
    /**
     * 图形元素新出现时的动画，默认图形从透明度 0 到当前透明度
     * @protected
     * @param {string} elmentName 图形元素名称
     * @param {IElement} newElement  新的图形元素
     * @param {object} animateCfg 动画的配置项
     */
    GroupComponent.prototype.addAnimation = function (elmentName, newElement, animateCfg) {
        // 缓存透明度
        var originOpacity = newElement.attr('opacity');
        if (isNil(originOpacity)) {
            originOpacity = 1;
        }
        newElement.attr('opacity', 0);
        newElement.animate({ opacity: originOpacity }, animateCfg);
    };
    /**
     * 图形元素新出现时的动画，默认图形从透明度 0 到当前透明度
     * @protected
     * @param {string} elmentName 图形元素名称
     * @param {IElement} originElement 要删除的图形元素
     * @param {object} animateCfg 动画的配置项
     */
    GroupComponent.prototype.removeAnimation = function (elementName, originElement, animateCfg) {
        originElement.animate({ opacity: 0 }, animateCfg);
    };
    /**
     * 图形元素的更新动画
     * @param {string} elmentName 图形元素名称
     * @param {IElement} originElement 现有的图形元素
     * @param {object} newAttrs  新的图形元素
     * @param {object} animateCfg 动画的配置项
     */
    GroupComponent.prototype.updateAnimation = function (elementName, originElement, newAttrs, animateCfg) {
        originElement.animate(newAttrs, animateCfg);
    };
    // 更新组件的图形
    GroupComponent.prototype.updateElements = function (newGroup, originGroup) {
        var _this = this;
        var animate = this.get('animate');
        var animateOption = this.get('animateOption');
        var children = newGroup.getChildren().slice(0); // 创建一个新数组，防止添加到 originGroup 时， children 变动
        var preElement; // 前面已经匹配到的图形元素，用于
        each(children, function (element) {
            var elementId = element.get('id');
            var originElement = _this.getElementById(elementId);
            var elementName = element.get('name');
            if (originElement) {
                if (element.get('isComponent')) {
                    // 嵌套子组件更新
                    var childComponent = element.get('component');
                    var origChildComponent = originElement.get('component');
                    var newCfg = pick(childComponent.cfg, difference(keys(childComponent.cfg), COPY_PROPERTIES_EXCLUDES));
                    origChildComponent.update(newCfg);
                    originElement.set(STATUS_UPDATE, 'update');
                }
                else {
                    var replaceAttrs = _this.getReplaceAttrs(originElement, element);
                    // 更新
                    if (animate && animateOption.update) {
                        // 没有动画
                        _this.updateAnimation(elementName, originElement, replaceAttrs, animateOption.update);
                    }
                    else {
                        // originElement.attrs = replaceAttrs; // 直接替换
                        originElement.attr(replaceAttrs);
                    }
                    // 如果是分组，则继续执行
                    if (element.isGroup()) {
                        _this.updateElements(element, originElement);
                    }
                    // 复制属性
                    each(COPY_PROPERTIES, function (name) {
                        originElement.set(name, element.get(name));
                    });
                    updateClip(originElement, element);
                    preElement = originElement;
                    // 执行完更新后设置状态位为更新
                    originElement.set(STATUS_UPDATE, 'update');
                }
            }
            else {
                // 没有对应的图形，则插入当前图形
                originGroup.add(element); // 应该在 group 加个 insertAt 的方法
                var siblings = originGroup.getChildren(); // 兄弟节点
                siblings.splice(siblings.length - 1, 1); // 先从数组中移除，然后放到合适的位置
                if (preElement) {
                    // 前面已经有更新的图形或者插入的图形，则在这个图形后面插入
                    var index = siblings.indexOf(preElement);
                    siblings.splice(index + 1, 0, element); // 在已经更新的图形元素后面插入
                }
                else {
                    siblings.unshift(element);
                }
                _this.registerElement(element); // 注册节点
                element.set(STATUS_UPDATE, 'add'); // 执行完更新后设置状态位为添加
                if (element.get('isComponent')) {
                    // 直接新增子组件container属性，实例不变
                    var childComponent = element.get('component');
                    childComponent.set('container', originGroup);
                }
                else if (element.isGroup()) {
                    // 如果元素是新增加的元素，则遍历注册所有的子节点
                    _this.registerNewGroup(element);
                }
                preElement = element;
                if (animate) {
                    var animateCfg = _this.get('isInit') ? animateOption.appear : animateOption.enter;
                    if (animateCfg) {
                        _this.addAnimation(elementName, element, animateCfg);
                    }
                }
            }
        });
    };
    GroupComponent.prototype.clearUpdateStatus = function (group) {
        var children = group.getChildren();
        each(children, function (el) {
            el.set(STATUS_UPDATE, null); // 清理掉更新状态
        });
    };
    // 清理离屏缓存
    GroupComponent.prototype.clearOffScreenCache = function () {
        var offScreenGroup = this.get('offScreenGroup');
        if (offScreenGroup) {
            // 销毁原先的离线 Group
            offScreenGroup.destroy();
        }
        this.set('offScreenGroup', null);
        this.set('offScreenBBox', null);
    };
    // private updateInner() {
    //   const group = this.get('group');
    //   const newGroup = this.createOffScreenGroup();
    //   this.renderInner(newGroup);
    //   this.applyOffset();
    //   this.updateElements(newGroup, group);
    //   this.deleteElements();
    //   newGroup.destroy(); // 销毁虚拟分组
    // }
    // 获取发生委托时的对象，在事件中抛出
    GroupComponent.prototype.getDelegateObject = function () {
        var _a;
        var name = this.get('name');
        var delegateObject = (_a = {},
            _a[name] = this,
            _a.component = this,
            _a);
        return delegateObject;
    };
    // 附加委托信息，用于事件
    GroupComponent.prototype.appendDelegateObject = function (parent, cfg) {
        var parentObject = parent.get('delegateObject');
        if (!cfg.delegateObject) {
            cfg.delegateObject = {};
        }
        mix(cfg.delegateObject, parentObject); // 将父元素上的委托信息复制到自身
    };
    // 获取需要替换的属性，如果原先图形元素存在，而新图形不存在，则设置 undefined
    GroupComponent.prototype.getReplaceAttrs = function (originElement, newElement) {
        var originAttrs = originElement.attr();
        var newAttrs = newElement.attr();
        each(originAttrs, function (v, k) {
            if (newAttrs[k] === undefined) {
                newAttrs[k] = undefined;
            }
        });
        return newAttrs;
    };
    GroupComponent.prototype.registerNewGroup = function (group) {
        var _this = this;
        var children = group.getChildren();
        each(children, function (element) {
            _this.registerElement(element); // 注册节点
            element.set(STATUS_UPDATE, 'add'); // 执行完更新后设置状态位为添加
            if (element.isGroup()) {
                _this.registerNewGroup(element);
            }
        });
    };
    // 移除多余的元素
    GroupComponent.prototype.deleteElements = function () {
        var _this = this;
        var shapesMap = this.get('shapesMap');
        var deleteArray = [];
        // 遍历获取需要删除的图形元素
        each(shapesMap, function (element, id) {
            if (!element.get(STATUS_UPDATE) || element.destroyed) {
                deleteArray.push([id, element]);
            }
            else {
                element.set(STATUS_UPDATE, null); // 清理掉更新状态
            }
        });
        var animate = this.get('animate');
        var animateOption = this.get('animateOption');
        // 删除图形元素
        each(deleteArray, function (item) {
            var id = item[0], element = item[1];
            if (!element.destroyed) {
                var elementName = element.get('name');
                if (animate && animateOption.leave) {
                    // 需要动画结束时移除图形
                    var callbackAnimCfg = mix({
                        callback: function () {
                            _this.removeElement(element);
                        },
                    }, animateOption.leave);
                    _this.removeAnimation(elementName, element, callbackAnimCfg);
                }
                else {
                    _this.removeElement(element);
                }
            }
            delete shapesMap[id]; // 从缓存中移除
        });
    };
    GroupComponent.prototype.removeElement = function (element) {
        if (element.get('isGroup')) {
            var component = element.get('component');
            if (component) {
                component.destroy();
            }
        }
        element.remove();
    };
    return GroupComponent;
}(Component));
export default GroupComponent;
//# sourceMappingURL=group-component.js.map