import { isArray, max, OBBBounds } from "@visactor/vutils";

import { AABBBounds, Matrix, normalTransform, Point, isNil, isString, isValidUrl, isBase64, isObject } from "@visactor/vutils";

import { Node } from "./node-tree";

import { EventTarget, CustomEvent } from "../event";

import { DefaultTransform } from "./config";

import { application } from "../application";

import { CustomPath2D } from "../common/custom-path2d";

import { ResourceLoader } from "../resource-loader/loader";

import { AttributeUpdateType, IContainPointMode, UpdateTag } from "../common/enums";

import { BoundsContext } from "../common/bounds-context";

import { renderCommandList } from "../common/render-command-list";

import { parsePadding } from "../common/utils";

import { builtinSymbolsMap, builtInSymbolStrMap, CustomSymbolClass } from "./builtin-symbol";

import { isSvg, XMLParser } from "../common/xml";

import { SVG_PARSE_ATTRIBUTE_MAP, SVG_PARSE_ATTRIBUTE_MAP_KEYS } from "./constants";

import { DefaultStateAnimateConfig } from "../animate/config";

import { EmptyContext2d } from "../canvas";

const _tempBounds = new AABBBounds, tempMatrix = new Matrix, tempBounds = new AABBBounds;

export const PURE_STYLE_KEY = [ "stroke", "opacity", "strokeOpacity", "lineDash", "lineDashOffset", "lineCap", "lineJoin", "miterLimit", "fill", "fillOpacity" ];

export const GRAPHIC_UPDATE_TAG_KEY = [ "lineWidth", "scaleX", "scaleY", "angle", "anchor", "visible" ];

const tempConstantXYKey = [ "x", "y" ], tempConstantScaleXYKey = [ "scaleX", "scaleY" ], tempConstantAngleKey = [ "angle" ], point = new Point;

export const NOWORK_ANIMATE_ATTR = {
    strokeSeg: 1,
    boundsPadding: 2,
    pickMode: 1,
    boundsMode: 1,
    customPickShape: 1,
    pickable: 1,
    childrenPickable: 1,
    visible: 1,
    zIndex: 1,
    layout: 1,
    keepDirIn3d: 1,
    globalZIndex: 1,
    outerBorder: 1,
    innerBorder: 1,
    lineDash: 1,
    lineCap: 1,
    lineJoin: 1,
    miterLimit: 2,
    strokeBoundsBuffer: 2,
    scaleCenter: 1,
    anchor: 1,
    anchor3d: 1,
    postMatrix: 1,
    backgroundMode: 2,
    background: 1,
    texture: 1,
    cursor: 1,
    html: 1
};

export class Graphic extends Node {
    static mixin(source) {
        const keys = Object.keys(source);
        for (let i = 0; i < keys.length; ++i) {
            const propertyName = keys[i];
            Object.defineProperty(Graphic.prototype, propertyName, Object.getOwnPropertyDescriptor(source, propertyName));
        }
    }
    get AABBBounds() {
        return this.tryUpdateAABBBounds();
    }
    get OBBBounds() {
        return this.tryUpdateOBBBounds();
    }
    get globalAABBBounds() {
        return this.tryUpdateGlobalAABBBounds();
    }
    get transMatrix() {
        return this.tryUpdateLocalTransMatrix(!0);
    }
    get globalTransMatrix() {
        return this.tryUpdateGlobalTransMatrix(!0);
    }
    constructor(params = {}) {
        var _a;
        super(), this._AABBBounds = new AABBBounds, this._updateTag = UpdateTag.INIT, this.attribute = params, 
        this.valid = this.isValid(), this.updateAABBBoundsStamp = 0, params.background ? this.loadImage(null !== (_a = params.background.background) && void 0 !== _a ? _a : params.background, !0) : params.shadowGraphic && this.setShadowGraphic(params.shadowGraphic);
    }
    getGraphicService() {
        var _a, _b;
        return null !== (_b = null === (_a = this.stage) || void 0 === _a ? void 0 : _a.graphicService) && void 0 !== _b ? _b : application.graphicService;
    }
    getAttributes() {
        return this.attribute;
    }
    setMode(mode) {
        "3d" === mode ? this.set3dMode() : this.set2dMode();
    }
    set3dMode() {
        this.in3dMode = !0;
    }
    set2dMode() {
        this.in3dMode = !1;
    }
    getOffsetXY(attr, includeScroll = !1) {
        var _a, _b;
        const {dx: dx = attr.dx, dy: dy = attr.dy} = this.attribute;
        if (includeScroll && this.parent) {
            const attribute = this.parent.attribute;
            point.x = dx + (null !== (_a = attribute.scrollX) && void 0 !== _a ? _a : 0), point.y = dy + (null !== (_b = attribute.scrollY) && void 0 !== _b ? _b : 0);
        } else point.x = dx, point.y = dy;
        return point;
    }
    onAnimateBind(animate) {
        this._emitCustomEvent("animate-bind", animate);
    }
    tryUpdateAABBBounds() {
        const full = "imprecise" === this.attribute.boundsMode;
        if (!this.shouldUpdateAABBBounds()) return this._AABBBounds;
        if (!this.valid) return this._AABBBounds.clear(), this._AABBBounds;
        this.getGraphicService().beforeUpdateAABBBounds(this, this.stage, !0, this._AABBBounds);
        const bounds = this.doUpdateAABBBounds(full);
        return this.getGraphicService().afterUpdateAABBBounds(this, this.stage, this._AABBBounds, this, !0), 
        "empty" === this.attribute.boundsMode && bounds.clear(), bounds;
    }
    tryUpdateOBBBounds() {
        if (this._OBBBounds || (this._OBBBounds = new OBBBounds), this.tryUpdateAABBBounds(), 
        this.updateOBBBoundsStamp === this.updateAABBBoundsStamp) return this._OBBBounds;
        if (this.updateOBBBoundsStamp = this.updateAABBBoundsStamp, !this.valid) return this._OBBBounds.clear(), 
        this._OBBBounds;
        return this.doUpdateOBBBounds();
    }
    combindShadowAABBBounds(bounds) {
        if (this.shadowRoot) {
            const b = this.shadowRoot.AABBBounds.clone();
            bounds.union(b);
        }
    }
    doUpdateOBBBounds() {
        return this._OBBBounds;
    }
    getClipPath() {
        const {clipConfig: clipConfig} = this.attribute;
        if (!clipConfig) return null;
        this.clipPathMap || (this.clipPathMap = new Map);
        const {shape: shape} = clipConfig;
        let path = this.clipPathMap.get(shape) || null;
        return path || (this.clipPathMap.size > 10 && this.clipPathMap.clear(), path = this.parsePath(shape), 
        path && this.clipPathMap.set(shape, path)), path;
    }
    parsePath(symbolType) {
        if (!symbolType) return null;
        let path = builtinSymbolsMap[symbolType];
        if (path) return path;
        if (path = Graphic.userSymbolMap[symbolType], path) return path;
        const _symbolType = builtInSymbolStrMap[symbolType];
        if (!0 === isSvg(symbolType = _symbolType || symbolType)) {
            const parser = new XMLParser, {svg: svg} = parser.parse(symbolType);
            if (!svg) return null;
            const path = isArray(svg.path) ? svg.path : [ svg.path ];
            _tempBounds.clear();
            const cacheList = [];
            path.forEach((item => {
                const cache = (new CustomPath2D).fromString(item.d), attribute = {};
                SVG_PARSE_ATTRIBUTE_MAP_KEYS.forEach((k => {
                    item[k] && (attribute[SVG_PARSE_ATTRIBUTE_MAP[k]] = item[k]);
                })), cacheList.push({
                    path: cache,
                    attribute: attribute
                }), _tempBounds.union(cache.bounds);
            }));
            const width = _tempBounds.width(), height = _tempBounds.height(), scale = 1 / max(width, height);
            cacheList.forEach((cache => cache.path.transform(0, 0, scale, scale)));
            const _parsedPath = new CustomSymbolClass(symbolType, cacheList, !0);
            return Graphic.userSymbolMap[symbolType] = _parsedPath, _parsedPath;
        }
        const cache = (new CustomPath2D).fromString(symbolType), width = cache.bounds.width(), height = cache.bounds.height(), scale = 1 / max(width, height);
        cache.transform(0, 0, scale, scale);
        const _parsedPath = new CustomSymbolClass(symbolType, cache);
        return Graphic.userSymbolMap[symbolType] = _parsedPath, _parsedPath;
    }
    doUpdateAABBBounds(full) {
        this.updateAABBBoundsStamp++;
        const graphicTheme = this.getGraphicTheme();
        this._AABBBounds.clear();
        const attribute = this.attribute, bounds = this.updateAABBBounds(attribute, graphicTheme, this._AABBBounds, full), {boundsPadding: boundsPadding = graphicTheme.boundsPadding} = attribute, paddingArray = parsePadding(boundsPadding);
        return paddingArray && bounds.expand(paddingArray), this.clearUpdateBoundTag(), 
        bounds;
    }
    updatePathProxyAABBBounds(aabbBounds) {
        const path = "function" == typeof this.pathProxy ? this.pathProxy(this.attribute) : this.pathProxy;
        if (!path) return !1;
        const boundsContext = new BoundsContext(aabbBounds);
        return renderCommandList(path.commandList, boundsContext, 0, 0), !0;
    }
    tryUpdateGlobalAABBBounds() {
        const b = this.AABBBounds;
        return this._globalAABBBounds ? this._globalAABBBounds.setValue(b.x1, b.y1, b.x2, b.y2) : this._globalAABBBounds = b.clone(), 
        this._globalAABBBounds.empty() || this.parent && this._globalAABBBounds.transformWithMatrix(this.parent.globalTransMatrix), 
        this._globalAABBBounds;
    }
    tryUpdateGlobalTransMatrix(clearTag = !0) {
        if (this._globalTransMatrix) {
            if (this.parent) {
                const m = this.parent.globalTransMatrix;
                this._globalTransMatrix.setValue(m.a, m.b, m.c, m.d, m.e, m.f);
            }
        } else this._globalTransMatrix = this.parent ? this.parent.globalTransMatrix.clone() : this.transMatrix.clone();
        return this.shouldUpdateGlobalMatrix() && this.doUpdateGlobalMatrix(), this._globalTransMatrix;
    }
    shouldUpdateGlobalMatrix() {
        return !0;
    }
    tryUpdateLocalTransMatrix(clearTag = !0) {
        return this._transMatrix || (this._transMatrix = new Matrix), this.shouldUpdateLocalMatrix() && (this.doUpdateLocalMatrix(), 
        clearTag && this.clearUpdateLocalPositionTag()), this._transMatrix;
    }
    shouldUpdateAABBBounds() {
        return this.shadowRoot ? (!!(this._updateTag & UpdateTag.UPDATE_BOUNDS) || this.shadowRoot.shouldUpdateAABBBounds()) && this.getGraphicService().validCheck(this.attribute, this.getGraphicTheme(), this._AABBBounds, this) : !!(this._updateTag & UpdateTag.UPDATE_BOUNDS) && this.getGraphicService().validCheck(this.attribute, this.getGraphicTheme(), this._AABBBounds, this);
    }
    shouldSelfChangeUpdateAABBBounds() {
        return this.shadowRoot ? !!(this._updateTag & UpdateTag.UPDATE_BOUNDS) || this.shadowRoot.shouldUpdateAABBBounds() : !!(this._updateTag & UpdateTag.UPDATE_BOUNDS);
    }
    shouldUpdateLocalMatrix() {
        return !!(this._updateTag & UpdateTag.UPDATE_LOCAL_MATRIX);
    }
    isValid() {
        var _a, _b;
        const attribute = this.attribute;
        return Number.isFinite((null !== (_a = attribute.x) && void 0 !== _a ? _a : 0) + (null !== (_b = attribute.y) && void 0 !== _b ? _b : 0));
    }
    _validNumber(num) {
        return null == num || Number.isFinite(num);
    }
    shouldUpdateShape() {
        return !!(this._updateTag & UpdateTag.UPDATE_SHAPE);
    }
    clearUpdateShapeTag() {
        this._updateTag &= UpdateTag.CLEAR_SHAPE;
    }
    containsPoint(x, y, mode, picker) {
        if (!picker) return !1;
        if (mode === IContainPointMode.GLOBAL) {
            const point = new Point(x, y);
            this.parent && this.parent.globalTransMatrix.transformPoint(point, point), x = point.x, 
            y = point.y;
        }
        return picker.containsPoint(this, {
            x: x,
            y: y
        });
    }
    setWidthHeightWithoutTransform(aabbBounds) {
        this.widthWithoutTransform = aabbBounds.x2 - aabbBounds.x1, this.heightWithoutTransform = aabbBounds.y2 - aabbBounds.y1;
    }
    setAttributesAndPreventAnimate(params, forceUpdateTag = !1, context) {
        this.setAttributes(params, forceUpdateTag, context), this.animates && this.animates.forEach((animate => {
            Object.keys(params).forEach((key => {
                animate.preventAttr(key);
            }));
        }));
    }
    setAttributes(params, forceUpdateTag = !1, context) {
        (params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(params, this.attribute, null, context) || params).background ? this.loadImage(params.background, !0) : params.shadowGraphic && this.setShadowGraphic(params.shadowGraphic), 
        this._setAttributes(params, forceUpdateTag, context);
    }
    _setAttributes(params, forceUpdateTag = !1, context) {
        const keys = Object.keys(params);
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            this.attribute[key] = params[key];
        }
        this.valid = this.isValid(), this.updateShapeAndBoundsTagSetted() || !forceUpdateTag && !this.needUpdateTags(keys) ? this.addUpdateBoundTag() : this.addUpdateShapeAndBoundsTag(), 
        this.addUpdatePositionTag(), this.addUpdateLayoutTag(), this.onAttributeUpdate(context);
    }
    setAttribute(key, value, forceUpdateTag, context) {
        var _a;
        const params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            [key]: value
        }, this.attribute, key, context);
        params ? this._setAttributes(params, forceUpdateTag, context) : isNil(null === (_a = this.normalAttrs) || void 0 === _a ? void 0 : _a[key]) ? (this.attribute[key] = value, 
        this.valid = this.isValid(), this.updateShapeAndBoundsTagSetted() || !forceUpdateTag && !this.needUpdateTag(key) ? this.addUpdateBoundTag() : this.addUpdateShapeAndBoundsTag(), 
        this.addUpdatePositionTag(), this.addUpdateLayoutTag(), this.onAttributeUpdate(context)) : this.normalAttrs[key] = value, 
        "background" === key ? this.loadImage(value, !0) : "shadowGraphic" === key && this.setShadowGraphic(value);
    }
    needUpdateTags(keys, k = GRAPHIC_UPDATE_TAG_KEY) {
        for (let i = 0; i < k.length; i++) {
            const attrKey = k[i];
            if (-1 !== keys.indexOf(attrKey)) return !0;
        }
        return !1;
    }
    needUpdateTag(key, k = GRAPHIC_UPDATE_TAG_KEY) {
        for (let i = 0; i < k.length; i++) {
            if (key === k[i]) return !0;
        }
        return !1;
    }
    initAttributes(params) {
        const context = {
            type: AttributeUpdateType.INIT
        };
        params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(params, this.attribute, null, context) || params, 
        this.attribute = params, params.background ? this.loadImage(params.background, !0) : params.shadowGraphic && this.setShadowGraphic(params.shadowGraphic), 
        this._updateTag = UpdateTag.INIT, this.valid = this.isValid(), this.onAttributeUpdate(context);
    }
    translate(x, y) {
        var _a, _b;
        if (0 === x && 0 === y) return this;
        const context = {
            type: AttributeUpdateType.TRANSLATE
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            x: x,
            y: y
        }, this.attribute, tempConstantXYKey, context);
        params && (x = params.x, y = params.y, delete params.x, delete params.y, this._setAttributes(params));
        const attribute = this.attribute, postMatrix = attribute.postMatrix;
        return postMatrix ? application.transformUtil.fromMatrix(postMatrix, postMatrix).translate(x, y) : (attribute.x = (null !== (_a = attribute.x) && void 0 !== _a ? _a : DefaultTransform.x) + x, 
        attribute.y = (null !== (_b = attribute.y) && void 0 !== _b ? _b : DefaultTransform.y) + y), 
        this.addUpdatePositionTag(), this.addUpdateBoundTag(), this.addUpdateLayoutTag(), 
        this.onAttributeUpdate(context), this;
    }
    translateTo(x, y) {
        const attribute = this.attribute;
        if (attribute.x === x && attribute.y === y) return this;
        const context = {
            type: AttributeUpdateType.TRANSLATE_TO
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            x: x,
            y: y
        }, this.attribute, tempConstantXYKey, context);
        return params ? (this._setAttributes(params, !1, context), this) : (attribute.x = x, 
        attribute.y = y, this.addUpdatePositionTag(), this.addUpdateBoundTag(), this.addUpdateLayoutTag(), 
        this.onAttributeUpdate(context), this);
    }
    scale(scaleX, scaleY, scaleCenter) {
        var _a, _b;
        if (1 === scaleX && 1 === scaleY) return this;
        const context = {
            type: AttributeUpdateType.SCALE
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            scaleX: scaleX,
            scaleY: scaleY,
            scaleCenter: scaleCenter
        }, this.attribute, tempConstantScaleXYKey, context);
        params && (scaleX = params.scaleX, scaleY = params.scaleY, delete params.scaleX, 
        delete params.scaleY, this._setAttributes(params));
        const attribute = this.attribute;
        if (scaleCenter) {
            let {postMatrix: postMatrix} = this.attribute;
            postMatrix || (postMatrix = new Matrix, attribute.postMatrix = postMatrix), application.transformUtil.fromMatrix(postMatrix, postMatrix).scale(scaleX, scaleY, scaleCenter);
        } else attribute.scaleX = (null !== (_a = attribute.scaleX) && void 0 !== _a ? _a : DefaultTransform.scaleX) * scaleX, 
        attribute.scaleY = (null !== (_b = attribute.scaleY) && void 0 !== _b ? _b : DefaultTransform.scaleY) * scaleY;
        return this.addUpdatePositionTag(), this.addUpdateBoundTag(), this.addUpdateLayoutTag(), 
        this.onAttributeUpdate(context), this;
    }
    scaleTo(scaleX, scaleY) {
        const attribute = this.attribute;
        if (attribute.scaleX === scaleX && attribute.scaleY === scaleY) return this;
        const context = {
            type: AttributeUpdateType.SCALE_TO
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            scaleX: scaleX,
            scaleY: scaleY
        }, this.attribute, tempConstantScaleXYKey, context);
        return params ? (this._setAttributes(params, !1, context), this) : (attribute.scaleX = scaleX, 
        attribute.scaleY = scaleY, this.addUpdatePositionTag(), this.addUpdateBoundTag(), 
        this.addUpdateLayoutTag(), this.onAttributeUpdate(context), this);
    }
    rotate(angle, rotateCenter) {
        var _a;
        if (0 === angle) return this;
        const context = {
            type: AttributeUpdateType.ROTATE
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate({
            angle: angle,
            rotateCenter: rotateCenter
        }, this.attribute, tempConstantAngleKey, context);
        params && (delete params.angle, this._setAttributes(params, !1, context));
        const attribute = this.attribute;
        if (rotateCenter) {
            let {postMatrix: postMatrix} = this.attribute;
            postMatrix || (postMatrix = new Matrix, attribute.postMatrix = postMatrix), application.transformUtil.fromMatrix(postMatrix, postMatrix).rotate(angle, rotateCenter);
        } else attribute.angle = (null !== (_a = attribute.angle) && void 0 !== _a ? _a : DefaultTransform.angle) + angle;
        return this.addUpdatePositionTag(), this.addUpdateBoundTag(), this.addUpdateLayoutTag(), 
        this.onAttributeUpdate(context), this;
    }
    rotateTo(angle) {
        const attribute = this.attribute;
        if (attribute.angle === angle) return this;
        const context = {
            type: AttributeUpdateType.ROTATE_TO
        }, params = this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(angle, this.attribute, tempConstantAngleKey, context);
        return params ? (this._setAttributes(params, !1, context), this) : (attribute.angle = angle, 
        this.addUpdatePositionTag(), this.addUpdateBoundTag(), this.addUpdateLayoutTag(), 
        this.onAttributeUpdate(context), this);
    }
    skewTo(b, c) {
        return this;
    }
    onAttributeUpdate(context) {
        context && context.skipUpdateCallback || (this.getGraphicService().onAttributeUpdate(this), 
        this._emitCustomEvent("afterAttributeUpdate", context));
    }
    update(d) {
        d ? (d.bounds && this.tryUpdateAABBBounds(), d.trans && this.tryUpdateLocalTransMatrix()) : (this.tryUpdateAABBBounds(), 
        this.tryUpdateLocalTransMatrix());
    }
    hasState(stateName) {
        return !(!this.currentStates || !this.currentStates.length) && (!!isNil(stateName) || this.currentStates.includes(stateName));
    }
    getState(stateName) {
        var _a;
        return null === (_a = this.states) || void 0 === _a ? void 0 : _a[stateName];
    }
    applyStateAttrs(attrs, stateNames, hasAnimation, isClear) {
        var _a, _b;
        if (hasAnimation) {
            const keys = Object.keys(attrs), noWorkAttrs = this.getNoWorkAnimateAttr(), animateAttrs = {};
            let noAnimateAttrs;
            keys.forEach((key => {
                noWorkAttrs[key] ? (noAnimateAttrs || (noAnimateAttrs = {}), noAnimateAttrs[key] = attrs[key]) : animateAttrs[key] = isClear && void 0 === attrs[key] ? this.getDefaultAttribute(key) : attrs[key];
            }));
            const stateAnimateConfig = null !== (_b = null !== (_a = this.context && this.context.stateAnimateConfig) && void 0 !== _a ? _a : this.stateAnimateConfig) && void 0 !== _b ? _b : DefaultStateAnimateConfig;
            this.applyAnimationState([ "state" ], [ {
                name: "state",
                animation: {
                    type: "state",
                    to: animateAttrs,
                    duration: stateAnimateConfig.duration,
                    easing: stateAnimateConfig.easing
                }
            } ]), noAnimateAttrs && this.setAttributesAndPreventAnimate(noAnimateAttrs, !1, {
                type: AttributeUpdateType.STATE
            });
        } else this.stopStateAnimates(), this.setAttributesAndPreventAnimate(attrs, !1, {
            type: AttributeUpdateType.STATE
        });
        this._emitCustomEvent("afterStateUpdate", {
            type: AttributeUpdateType.STATE
        });
    }
    updateNormalAttrs(stateAttrs) {
        const newNormalAttrs = {};
        this.normalAttrs ? (Object.keys(stateAttrs).forEach((key => {
            key in this.normalAttrs ? (newNormalAttrs[key] = this.normalAttrs[key], delete this.normalAttrs[key]) : newNormalAttrs[key] = this.getNormalAttribute(key);
        })), Object.keys(this.normalAttrs).forEach((key => {
            stateAttrs[key] = this.normalAttrs[key];
        }))) : Object.keys(stateAttrs).forEach((key => {
            newNormalAttrs[key] = this.getNormalAttribute(key);
        })), this.normalAttrs = newNormalAttrs;
    }
    stopStateAnimates(type = "end") {
        this.animates && this.animates.forEach((animate => {
            animate.stateNames && (animate.stop(type), this.animates.delete(animate.id));
        }));
    }
    getNormalAttribute(key) {
        var _a, _b;
        const value = this.attribute[key];
        return this.animates ? null === (_a = this.finalAttribute) || void 0 === _a ? void 0 : _a[key] : null != value ? value : null === (_b = this.finalAttribute) || void 0 === _b ? void 0 : _b[key];
    }
    clearStates(hasAnimation) {
        this.hasState() && this.normalAttrs ? (this.currentStates = [], this.applyStateAttrs(this.normalAttrs, this.currentStates, hasAnimation, !0)) : this.currentStates = [], 
        this.normalAttrs = null;
    }
    removeState(stateName, hasAnimation) {
        if (this.currentStates) {
            const filter = isArray(stateName) ? s => !stateName.includes(s) : s => s !== stateName, newStates = this.currentStates.filter(filter);
            newStates.length !== this.currentStates.length && this.useStates(newStates, hasAnimation);
        }
    }
    toggleState(stateName, hasAnimation) {
        if (this.hasState(stateName)) this.removeState(stateName, hasAnimation); else {
            if ((this.currentStates ? this.currentStates.indexOf(stateName) : -1) < 0) {
                const nextStates = this.currentStates ? this.currentStates.slice() : [];
                nextStates.push(stateName), this.useStates(nextStates, hasAnimation);
            }
        }
    }
    addState(stateName, keepCurrentStates, hasAnimation) {
        var _a;
        if (this.currentStates && this.currentStates.includes(stateName) && (keepCurrentStates || 1 === this.currentStates.length)) return;
        const newStates = keepCurrentStates && (null === (_a = this.currentStates) || void 0 === _a ? void 0 : _a.length) ? this.currentStates.concat([ stateName ]) : [ stateName ];
        this.useStates(newStates, hasAnimation);
    }
    useStates(states, hasAnimation) {
        var _a;
        if (!states.length) return void this.clearStates(hasAnimation);
        if (!((null === (_a = this.currentStates) || void 0 === _a ? void 0 : _a.length) !== states.length || states.some(((stateName, index) => this.currentStates[index] !== stateName)))) return;
        const stateAttrs = {};
        states.forEach((stateName => {
            var _a;
            const attrs = this.stateProxy ? this.stateProxy(stateName, states) : null === (_a = this.states) || void 0 === _a ? void 0 : _a[stateName];
            attrs && Object.assign(stateAttrs, attrs);
        })), this.updateNormalAttrs(stateAttrs), this.currentStates = states, this.applyStateAttrs(stateAttrs, states, hasAnimation);
    }
    addUpdateBoundTag() {
        this._updateTag |= UpdateTag.UPDATE_BOUNDS, this.parent && this.parent.addChildUpdateBoundTag(), 
        this.glyphHost && this.glyphHost.addUpdateBoundTag();
    }
    addUpdateShapeTag() {
        this._updateTag |= UpdateTag.UPDATE_SHAPE;
    }
    addUpdateShapeAndBoundsTag() {
        this._updateTag |= UpdateTag.UPDATE_SHAPE_AND_BOUNDS, this.parent && this.parent.addChildUpdateBoundTag(), 
        this.glyphHost && this.glyphHost.addUpdateBoundTag();
    }
    updateShapeAndBoundsTagSetted() {
        return (this._updateTag & UpdateTag.UPDATE_SHAPE_AND_BOUNDS) === UpdateTag.UPDATE_SHAPE_AND_BOUNDS;
    }
    clearUpdateBoundTag() {
        this._updateTag &= UpdateTag.CLEAR_BOUNDS;
    }
    addUpdatePositionTag() {
        this.shadowRoot && this.shadowRoot.addUpdateGlobalPositionTag(), this._updateTag |= UpdateTag.UPDATE_GLOBAL_LOCAL_MATRIX;
    }
    addUpdateGlobalPositionTag() {
        this.shadowRoot && this.shadowRoot.addUpdateGlobalPositionTag(), this._updateTag |= UpdateTag.UPDATE_GLOBAL_MATRIX;
    }
    clearUpdateLocalPositionTag() {
        this._updateTag &= UpdateTag.CLEAR_LOCAL_MATRIX;
    }
    clearUpdateGlobalPositionTag() {
        this._updateTag &= UpdateTag.CLEAR_GLOBAL_MATRIX;
    }
    addUpdateLayoutTag() {
        this._updateTag |= UpdateTag.UPDATE_LAYOUT;
    }
    clearUpdateLayoutTag() {
        this._updateTag &= UpdateTag.CLEAR_LAYOUT;
    }
    needUpdateLayout() {
        return !!(this._updateTag & UpdateTag.UPDATE_LAYOUT);
    }
    getAnchor(anchor, params, resetScale) {
        const _anchor = [ 0, 0 ], getBounds = () => {
            if (params.b) return params.b;
            const graphic = this.clone();
            return graphic.attribute.angle = 0, graphic.attribute.scaleCenter = null, resetScale && (graphic.attribute.scaleX = 1, 
            graphic.attribute.scaleY = 1), params.b = graphic.AABBBounds, params.b;
        };
        if ("string" == typeof anchor[0]) {
            const ratio = parseFloat(anchor[0]) / 100, bounds = getBounds();
            _anchor[0] = bounds.x1 + (bounds.x2 - bounds.x1) * ratio;
        } else _anchor[0] = anchor[0];
        if ("string" == typeof anchor[1]) {
            const ratio = parseFloat(anchor[1]) / 100, bounds = getBounds();
            _anchor[1] = bounds.y1 + (bounds.y2 - bounds.y1) * ratio;
        } else _anchor[1] = anchor[1];
        return _anchor;
    }
    doUpdateLocalMatrix() {
        const {x: x = DefaultTransform.x, y: y = DefaultTransform.y, scaleX: scaleX = DefaultTransform.scaleX, scaleY: scaleY = DefaultTransform.scaleY, angle: angle = DefaultTransform.angle, scaleCenter: scaleCenter, anchor: anchor, postMatrix: postMatrix} = this.attribute;
        let _anchor = [ 0, 0 ];
        const params = {};
        if (anchor && angle && (_anchor = this.getAnchor(anchor, params)), !scaleCenter || 1 === scaleX && 1 === scaleY) normalTransform(this._transMatrix, this._transMatrix.reset(), x, y, scaleX, scaleY, angle, anchor && _anchor); else {
            const m = this._transMatrix;
            m.reset(), m.translate(_anchor[0], _anchor[1]), m.rotate(angle), m.translate(-_anchor[0], -_anchor[1]), 
            m.translate(x, y), _anchor = this.getAnchor(scaleCenter, params, !0), application.transformUtil.fromMatrix(m, m).scale(scaleX, scaleY, {
                x: _anchor[0],
                y: _anchor[1]
            });
        }
        const p = this.getOffsetXY(DefaultTransform);
        if (this._transMatrix.e += p.x, this._transMatrix.f += p.y, postMatrix) {
            const m1 = tempMatrix.setValue(postMatrix.a, postMatrix.b, postMatrix.c, postMatrix.d, postMatrix.e, postMatrix.f), m2 = this._transMatrix;
            m1.multiply(m2.a, m2.b, m2.c, m2.d, m2.e, m2.f), m2.setValue(m1.a, m1.b, m1.c, m1.d, m1.e, m1.f);
        }
    }
    doUpdateGlobalMatrix() {
        if (this.parent) {
            this._globalTransMatrix.multiply(this.transMatrix.a, this.transMatrix.b, this.transMatrix.c, this.transMatrix.d, this.transMatrix.e, this.transMatrix.f);
            const {scrollX: scrollX = 0, scrollY: scrollY = 0} = this.parent.attribute;
            this._globalTransMatrix.translate(scrollX, scrollY);
        }
    }
    setStage(stage, layer) {
        if (this.stage !== stage) {
            if (this.stage = stage, this.layer = layer, this.setStageToShadowRoot(stage, layer), 
            this.animates && this.animates.size) {
                const timeline = stage.getTimeline();
                this.animates.forEach((a => {
                    a.timeline.isGlobal && (a.setTimeline(timeline), timeline.addAnimate(a));
                }));
            }
            this._onSetStage && this._onSetStage(this, stage, layer), this.getGraphicService().onSetStage(this, stage);
        }
    }
    setStageToShadowRoot(stage, layer) {
        this.shadowRoot && this.shadowRoot.setStage(stage, layer);
    }
    onAddStep(step) {}
    onStop(props) {
        props && this.setAttributes(props, !1, {
            type: AttributeUpdateType.ANIMATE_END
        });
    }
    getDefaultAttribute(name) {
        return this.getGraphicTheme()[name];
    }
    getComputedAttribute(name) {
        var _a;
        return null !== (_a = this.attribute[name]) && void 0 !== _a ? _a : this.getDefaultAttribute(name);
    }
    onSetStage(cb, immediate = !1) {
        this._onSetStage = cb, immediate && this.stage && cb(this, this.stage);
    }
    attachShadow(shadowRoot) {
        return shadowRoot && (shadowRoot.shadowHost = this), this.shadowRoot = null != shadowRoot ? shadowRoot : application.graphicService.creator.shadowRoot(this), 
        this.addUpdateBoundTag(), this.shadowRoot.setStage(this.stage, this.layer), this.shadowRoot;
    }
    detachShadow() {
        this.shadowRoot && (this.addUpdateBoundTag(), this.shadowRoot.release(!0), this.shadowRoot = null);
    }
    toJson() {
        return {
            attribute: this.attribute,
            _uid: this._uid,
            type: this.type,
            name: this.name,
            children: this.children.map((item => item.toJson()))
        };
    }
    createPathProxy(path) {
        return isString(path, !0) ? this.pathProxy = (new CustomPath2D).fromString(path) : this.pathProxy = new CustomPath2D, 
        this.pathProxy;
    }
    loadImage(image, background = !1) {
        if (!image || background && backgroundNotImage(image)) return;
        const url = image;
        this.resources || (this.resources = new Map);
        const cache = {
            data: "init",
            state: null
        };
        this.resources.set(url, cache), "string" == typeof image ? (cache.state = "loading", 
        image.startsWith("<svg") ? (ResourceLoader.GetSvg(image, this), this.backgroundImg = this.backgroundImg || background) : (isValidUrl(image) || image.includes("/") || isBase64(image)) && (ResourceLoader.GetImage(image, this), 
        this.backgroundImg = this.backgroundImg || background)) : isObject(image) ? (cache.state = "success", 
        cache.data = image, this.backgroundImg = this.backgroundImg || background) : cache.state = "fail";
    }
    setShadowGraphic(graphic) {
        if (graphic) {
            this.attachShadow().add(graphic);
        } else this.detachShadow();
    }
    imageLoadSuccess(url, image, cb) {
        if (!this.resources) return;
        const res = this.resources.get(url);
        res && (res.state = "success", res.data = image, cb && cb(), this.addUpdateBoundTag(), 
        this.stage && this.stage.renderNextFrame());
    }
    imageLoadFail(url, cb) {
        if (!this.resources) return;
        const res = this.resources.get(url);
        res && (res.state = "fail", cb && cb());
    }
    _stopAnimates(animates) {
        animates && animates.forEach((animate => {
            animate.stop();
        }));
    }
    stopAnimates(stopChildren = !1) {
        this._stopAnimates(this.animates), this.shadowRoot && this.shadowRoot.stopAnimates(!0), 
        this.isContainer && stopChildren && this.forEachChildren((c => {
            c.stopAnimates(stopChildren);
        }));
    }
    release() {
        this.releaseStatus = "released", this.stopAnimates(), application.graphicService.onRelease(this);
    }
    _emitCustomEvent(type, context) {
        var _a, _b;
        if (this._events && type in this._events) {
            const changeEvent = new CustomEvent(type, context);
            changeEvent.bubbles = !1, changeEvent.manager = null === (_b = null === (_a = this.stage) || void 0 === _a ? void 0 : _a.eventSystem) || void 0 === _b ? void 0 : _b.manager, 
            this.dispatchEvent(changeEvent);
        }
    }
    toCustomPath() {
        var _a, _b, _c;
        const renderer = null === (_c = null === (_b = (null === (_a = this.stage) || void 0 === _a ? void 0 : _a.renderService) || application.renderService) || void 0 === _b ? void 0 : _b.drawContribution) || void 0 === _c ? void 0 : _c.getRenderContribution(this);
        if (renderer) {
            const context = new EmptyContext2d(null, 1);
            return renderer.drawShape(this, context, 0, 0, {}, {}), context.path;
        }
        return null;
    }
}

function backgroundNotImage(image) {
    return !(!image.fill && !image.stroke);
}

Graphic.userSymbolMap = {}, Graphic.mixin(EventTarget);
//# sourceMappingURL=graphic.js.map
