import { __assign, __awaiter, __generator, __rest, __values } from "tslib";
import { deepMix, each, get, isArray, isNull } from '@antv/util';
import { doAnimate } from '../animate';
import { getGeometryLabelLayout } from '../geometry/label';
import { getLabelBackgroundInfo } from '../geometry/label/util';
import { polarToCartesian } from '../util/graphics';
import { rotate, translate } from '../util/transform';
import { FIELD_ORIGIN } from '../constant';
import { updateLabel } from './update-label';
/**
 * Geometry labels 渲染组件
 */
var Labels = /** @class */ (function () {
    function Labels(cfg) {
        /** 存储当前 shape 的映射表，键值为 shape id */
        this.shapesMap = {};
        var layout = cfg.layout, container = cfg.container;
        this.layout = layout;
        this.container = container;
    }
    /**
     * 渲染文本
     */
    Labels.prototype.render = function (items, shapes, isUpdate) {
        if (isUpdate === void 0) { isUpdate = false; }
        return __awaiter(this, void 0, void 0, function () {
            var shapesMap, offscreenGroup, items_1, items_1_1, item, lastShapesMap;
            var e_1, _a;
            var _this = this;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        shapesMap = {};
                        offscreenGroup = this.createOffscreenGroup();
                        if (!items.length) return [3 /*break*/, 2];
                        try {
                            // 如果 items 空的话就不进行绘制调整操作
                            // step 1: 在虚拟 group 中创建 shapes
                            for (items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
                                item = items_1_1.value;
                                if (item) {
                                    shapesMap[item.id] = this.renderLabel(item, offscreenGroup);
                                }
                            }
                        }
                        catch (e_1_1) { e_1 = { error: e_1_1 }; }
                        finally {
                            try {
                                if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
                            }
                            finally { if (e_1) throw e_1.error; }
                        }
                        // [todo] Move layout into Worker.
                        // step 2: 根据布局，调整 labels
                        return [4 /*yield*/, this.doLayout(items, shapes, shapesMap)];
                    case 1:
                        // [todo] Move layout into Worker.
                        // step 2: 根据布局，调整 labels
                        _b.sent();
                        // step 3.1: 绘制 labelLine
                        this.renderLabelLine(items, shapesMap);
                        // step 3.2: 绘制 labelBackground
                        this.renderLabelBackground(items, shapesMap);
                        // step 4: 根据用户设置的偏移量调整 label
                        this.adjustLabel(items, shapesMap);
                        _b.label = 2;
                    case 2:
                        lastShapesMap = this.shapesMap;
                        each(shapesMap, function (shape, id) {
                            if (shape.destroyed) {
                                // label 在布局调整环节被删除了（doLayout）
                                delete shapesMap[id];
                            }
                            else {
                                if (lastShapesMap[id]) {
                                    // 图形发生更新
                                    var data = shape.get('data');
                                    var origin_1 = shape.get('origin');
                                    var coordinate = shape.get('coordinate');
                                    var currentAnimateCfg = shape.get('animateCfg');
                                    var currentShape = lastShapesMap[id]; // 已经在渲染树上的 shape
                                    updateLabel(currentShape, shapesMap[id], {
                                        data: data,
                                        origin: origin_1,
                                        animateCfg: currentAnimateCfg,
                                        coordinate: coordinate,
                                    });
                                    shapesMap[id] = currentShape; // 保存引用
                                }
                                else {
                                    // 新生成的 shape
                                    // If container has been destroyed, no need to render labels.
                                    if (_this.container.destroyed)
                                        return;
                                    _this.container.add(shape);
                                    var animateCfg = get(shape.get('animateCfg'), isUpdate ? 'enter' : 'appear');
                                    if (animateCfg) {
                                        doAnimate(shape, animateCfg, {
                                            toAttrs: __assign({}, shape.attr()),
                                            coordinate: shape.get('coordinate'),
                                        });
                                    }
                                }
                                delete lastShapesMap[id];
                            }
                        });
                        // 移除
                        each(lastShapesMap, function (deleteShape) {
                            var animateCfg = get(deleteShape.get('animateCfg'), 'leave');
                            if (animateCfg) {
                                doAnimate(deleteShape, animateCfg, {
                                    toAttrs: null,
                                    coordinate: deleteShape.get('coordinate'),
                                });
                            }
                            else {
                                deleteShape.remove(true); // 移除
                            }
                        });
                        this.shapesMap = shapesMap;
                        offscreenGroup.destroy();
                        return [2 /*return*/];
                }
            });
        });
    };
    /** 清除当前 labels */
    Labels.prototype.clear = function () {
        this.container.clear();
        this.shapesMap = {};
    };
    /** 销毁 */
    Labels.prototype.destroy = function () {
        this.container.destroy();
        this.shapesMap = null;
    };
    Labels.prototype.renderLabel = function (cfg, container) {
        var id = cfg.id, elementId = cfg.elementId, data = cfg.data, mappingData = cfg.mappingData, coordinate = cfg.coordinate, animate = cfg.animate, content = cfg.content, capture = cfg.capture;
        var shapeAppendCfg = {
            id: id,
            elementId: elementId,
            capture: capture,
            data: data,
            origin: __assign(__assign({}, mappingData), { data: mappingData[FIELD_ORIGIN] }),
            coordinate: coordinate,
        };
        var labelGroup = container.addGroup(__assign({ name: 'label', 
            // 如果 this.animate === false 或者 cfg.animate === false/null 则不进行动画，否则进行动画配置的合并
            animateCfg: this.animate === false || animate === null || animate === false ? false : deepMix({}, this.animate, animate) }, shapeAppendCfg));
        var labelShape;
        if ((content.isGroup && content.isGroup()) || (content.isShape && content.isShape())) {
            // 如果 content 是 Group 或者 Shape，根据 textAlign 调整位置后，直接将其加入 labelGroup
            var _a = content.getCanvasBBox(), width = _a.width, height = _a.height;
            var textAlign = get(cfg, 'textAlign', 'left');
            var x = cfg.x;
            var y = cfg.y - height / 2;
            if (textAlign === 'center') {
                x = x - width / 2;
            }
            else if (textAlign === 'right' || textAlign === 'end') {
                x = x - width;
            }
            translate(content, x, y); // 将 label 平移至 x, y 指定的位置
            labelShape = content;
            labelGroup.add(content);
        }
        else {
            var fill = get(cfg, ['style', 'fill']);
            labelShape = labelGroup.addShape('text', __assign({ attrs: __assign(__assign({ x: cfg.x, y: cfg.y, textAlign: cfg.textAlign, textBaseline: get(cfg, 'textBaseline', 'middle'), text: cfg.content }, cfg.style), { fill: isNull(fill) ? cfg.color : fill }) }, shapeAppendCfg));
        }
        if (cfg.rotate) {
            rotate(labelShape, cfg.rotate);
        }
        return labelGroup;
    };
    // 根据type对label布局
    Labels.prototype.doLayout = function (items, shapes, shapesMap) {
        return __awaiter(this, void 0, void 0, function () {
            var layouts;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!this.layout) return [3 /*break*/, 2];
                        layouts = isArray(this.layout) ? this.layout : [this.layout];
                        return [4 /*yield*/, Promise.all(layouts.map(function (layout) {
                                var layoutFn = getGeometryLabelLayout(get(layout, 'type', ''));
                                if (!layoutFn)
                                    return;
                                var labelShapes = [];
                                var geometryShapes = [];
                                each(shapesMap, function (labelShape, id) {
                                    labelShapes.push(labelShape);
                                    geometryShapes.push(shapes[labelShape.get('elementId')]);
                                });
                                // [todo] Refactor more layout into Worker.
                                return layoutFn(items, labelShapes, geometryShapes, _this.region, layout.cfg);
                            }))];
                    case 1:
                        _a.sent();
                        _a.label = 2;
                    case 2: return [2 /*return*/];
                }
            });
        });
    };
    Labels.prototype.renderLabelLine = function (labelItems, shapesMap) {
        each(labelItems, function (labelItem) {
            var coordinate = get(labelItem, 'coordinate');
            if (!labelItem || !coordinate) {
                return;
            }
            var center = coordinate.getCenter();
            var radius = coordinate.getRadius();
            if (!labelItem.labelLine) {
                // labelLine: null | false，关闭 label 对应的 labelLine
                return;
            }
            var labelLineCfg = get(labelItem, 'labelLine', {});
            var id = labelItem.id;
            var path = labelLineCfg.path;
            if (!path) {
                var start = polarToCartesian(center.x, center.y, radius, labelItem.angle);
                path = [
                    ['M', start.x, start.y],
                    ['L', labelItem.x, labelItem.y],
                ];
            }
            var labelGroup = shapesMap[id];
            if (!labelGroup.destroyed) {
                labelGroup.addShape('path', {
                    capture: false,
                    attrs: __assign({ path: path, stroke: labelItem.color ? labelItem.color : get(labelItem, ['style', 'fill'], '#000'), fill: null }, labelLineCfg.style),
                    id: id,
                    origin: labelItem.mappingData,
                    data: labelItem.data,
                    coordinate: labelItem.coordinate,
                });
            }
        });
    };
    /**
     * 绘制标签背景
     * @param labelItems
     */
    Labels.prototype.renderLabelBackground = function (labelItems, shapesMap) {
        each(labelItems, function (labelItem) {
            var coordinate = get(labelItem, 'coordinate');
            var background = get(labelItem, 'background');
            if (!background || !coordinate) {
                return;
            }
            var id = labelItem.id;
            var labelGroup = shapesMap[id];
            if (!labelGroup.destroyed) {
                var labelContentShape = labelGroup.getChildren()[0];
                if (labelContentShape) {
                    var _a = getLabelBackgroundInfo(labelGroup, labelItem, background.padding), rotation = _a.rotation, box = __rest(_a, ["rotation"]);
                    var backgroundShape = labelGroup.addShape('rect', {
                        attrs: __assign(__assign({}, box), (background.style || {})),
                        id: id,
                        origin: labelItem.mappingData,
                        data: labelItem.data,
                        coordinate: labelItem.coordinate,
                    });
                    backgroundShape.setZIndex(-1);
                    if (rotation) {
                        var matrix = labelContentShape.getMatrix();
                        backgroundShape.setMatrix(matrix);
                    }
                }
            }
        });
    };
    Labels.prototype.createOffscreenGroup = function () {
        var container = this.container;
        var GroupClass = container.getGroupBase(); // 获取分组的构造函数
        var newGroup = new GroupClass({});
        return newGroup;
    };
    Labels.prototype.adjustLabel = function (items, shapesMap) {
        each(items, function (item) {
            if (item) {
                var id = item.id;
                var labelGroup = shapesMap[id];
                if (!labelGroup.destroyed) {
                    // fix: 如果说开发者的 label content 是一个 group，此处的偏移无法对 整个 content group 生效；场景类似 饼图 spider label 是一个含 2 个 textShape 的 gorup
                    var labelShapes = labelGroup.findAll(function (ele) { return ele.get('type') !== 'path'; });
                    each(labelShapes, function (labelShape) {
                        if (labelShape) {
                            if (item.offsetX) {
                                labelShape.attr('x', labelShape.attr('x') + item.offsetX);
                            }
                            if (item.offsetY) {
                                labelShape.attr('y', labelShape.attr('y') + item.offsetY);
                            }
                        }
                    });
                }
            }
        });
    };
    return Labels;
}());
export default Labels;
//# sourceMappingURL=labels.js.map