import TmpAssembler from "./utils/TmpAssembler";
import TmpFontConfig from "./utils/TmpFontConfig";
import TmpUtils from "./utils/TmpUtils";

const { ccclass, property, executeInEditMode, menu } = cc._decorator;

/**
 * TextMeshPro的排版方式
 */
export enum TmpOverflow {
    NONE,
    CLAMP,
    ELLIPSIS,
    SHRINK,
    RESIZE_HEIGHT
}

/**
 * TextMeshPro Uniform参数
 */
@ccclass("TmpUniform")
export class TmpUniform {
    @property(cc.Color)
    private _faceColor: cc.Color = cc.Color.WHITE;
    @property({ tooltip: CC_DEV && "文本主体颜色", type: cc.Color })
    public get faceColor(): cc.Color { return this._faceColor; }
    public set faceColor(v: cc.Color) {
        if (!CC_EDITOR && this._faceColor === v) { return; }
        this._faceColor = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatFace(this._comp.getMaterial(0));
    }

    @property()
    private _faceDilate: number = 0.5;
    @property({ tooltip: CC_DEV && "文本主体厚度", range: [0, 1, 0.01] })
    public get faceDilate(): number { return this._faceDilate; }
    public set faceDilate(v: number) {
        if (this._faceDilate === v) { return; }
        this._faceDilate = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatFace(this._comp.getMaterial(0));
    }

    @property()
    private _faceSoftness: number = 0.01;
    @property({ tooltip: CC_DEV && "文本主体柔和度", range: [0, 1, 0.01] })
    public get faceSoftness(): number { return this._faceSoftness; }
    public set faceSoftness(v: number) {
        if (this._faceSoftness === v) { return; }
        this._faceSoftness = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatFace(this._comp.getMaterial(0));
    }

    @property()
    private _enableOutline: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用描边效果" })
    public get enableOutline(): boolean { return this._enableOutline; }
    public set enableOutline(v: boolean) {
        if (this._enableOutline === v) { return; }
        this._enableOutline = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatOutline(this._comp.getMaterial(0));
    }

    @property(cc.Color)
    private _outlineColor: cc.Color = cc.color(255, 0, 0, 255);
    @property({
        tooltip: CC_DEV && "描边颜色",
        type: cc.Color,
        visible() { return this._enableOutline; }
    })
    public get outlineColor(): cc.Color { return this._outlineColor; }
    public set outlineColor(v: cc.Color) {
        if (!CC_EDITOR && this._outlineColor === v) { return; }
        this._outlineColor = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatOutline(this._comp.getMaterial(0));
    }

    @property()
    private _outlineThickness: number = 0.1;
    @property({
        tooltip: CC_DEV && "描边厚度",
        range: [0, 1, 0.01],
        visible() { return this._enableOutline; }
    })
    public get outlineThickness(): number { return this._outlineThickness; }
    public set outlineThickness(v: number) {
        if (this._outlineThickness === v) { return; }
        this._outlineThickness = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatOutline(this._comp.getMaterial(0));
    }

    @property()
    private _enableUnderlay: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用阴影效果" })
    public get enableUnderlay(): boolean { return this._enableUnderlay; }
    public set enableUnderlay(v: boolean) {
        if (this._enableUnderlay === v) { return; }
        this._enableUnderlay = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatUnderlay(this._comp.getMaterial(0));
    }

    @property(cc.Color)
    private _underlayColor: cc.Color = cc.color(0, 0, 0, 255);
    @property({
        tooltip: CC_DEV && "阴影颜色",
        type: cc.Color,
        visible() { return this._enableUnderlay; }
    })
    public get underlayColor(): cc.Color { return this._underlayColor; }
    public set underlayColor(v: cc.Color) {
        if (!CC_EDITOR && this._underlayColor === v) { return; }
        this._underlayColor = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatUnderlay(this._comp.getMaterial(0));
    }

    @property(cc.Vec2)
    private _underlayOffset: cc.Vec2 = cc.v2(0, 0);
    @property({
        tooltip: CC_DEV && "阴影偏移",
        type: cc.Vec2,
        range: [-1, 1],
        visible() { return this._enableUnderlay; }
    })
    public get underlayOffset(): cc.Vec2 { return this._underlayOffset; }
    public set underlayOffset(v: cc.Vec2) {
        if (!CC_EDITOR && this._underlayOffset === v) { return; }
        this._underlayOffset = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatUnderlay(this._comp.getMaterial(0));
    }

    @property()
    private _underlayDilate: number = 0.5;
    @property({
        tooltip: CC_DEV && "阴影厚度",
        range: [0, 1, 0.01],
        visible() { return this._enableUnderlay; }
    })
    public get underlayDilate(): number { return this._underlayDilate; }
    public set underlayDilate(v: number) {
        if (this._underlayDilate === v) { return; }
        this._underlayDilate = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatUnderlay(this._comp.getMaterial(0));
    }

    @property()
    private _underlaySoftness: number = 0.1;
    @property({
        tooltip: CC_DEV && "阴影柔和度",
        range: [0, 1, 0.01],
        visible() { return this._enableUnderlay; }
    })
    public get underlaySoftness(): number { return this._underlaySoftness; }
    public set underlaySoftness(v: number) {
        if (this._underlaySoftness === v) { return; }
        this._underlaySoftness = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatUnderlay(this._comp.getMaterial(0));
    }

    @property()
    private _enableGlow: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用辉光效果" })
    public get enableGlow(): boolean { return this._enableGlow; }
    public set enableGlow(v: boolean) {
        if (this._enableGlow === v) { return; }
        this._enableGlow = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    @property(cc.Color)
    private _glowColor: cc.Color = cc.color(0, 255, 0, 255);
    @property({
        tooltip: CC_DEV && "辉光颜色",
        type: cc.Color,
        visible() { return this._enableGlow; }
    })
    public get glowColor(): cc.Color { return this._glowColor; }
    public set glowColor(v: cc.Color) {
        if (!CC_EDITOR && this._glowColor === v) { return; }
        this._glowColor = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    @property()
    private _glowOffset: number = 0.5;
    @property({
        tooltip: CC_DEV && "辉光偏移",
        range: [0, 1, 0.01],
        visible() { return this._enableGlow; }
    })
    public get glowOffset(): number { return this._glowOffset; }
    public set glowOffset(v: number) {
        if (this._glowOffset === v) { return; }
        this._glowOffset = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    @property()
    private _glowInner: number = 0.01;
    @property({
        tooltip: CC_DEV && "辉光向内的厚度",
        range: [0, 1, 0.01],
        visible() { return this._enableGlow; }
    })
    public get glowInner(): number { return this._glowInner; }
    public set glowInner(v: number) {
        if (this._glowInner === v) { return; }
        this._glowInner = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    @property()
    private _glowOuter: number = 0.01;
    @property({
        tooltip: CC_DEV && "辉光向外的厚度",
        range: [0, 1, 0.01],
        visible() { return this._enableGlow; }
    })
    public get glowOuter(): number { return this._glowOuter; }
    public set glowOuter(v: number) {
        if (this._glowOuter === v) { return; }
        this._glowOuter = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    @property()
    private _glowPower: number = 1;
    @property({
        tooltip: CC_DEV && "辉光强度",
        range: [0, 1, 0.01],
        visible() { return this._enableGlow; }
    })
    public get glowPower(): number { return this._glowPower; }
    public set glowPower(v: number) {
        if (this._glowPower === v) { return; }
        this._glowPower = v;
        if (!this._comp) { return; }
        this._comp.updateTmpMatGlow(this._comp.getMaterial(0));
    }

    private _comp: TextMeshPro = null;
    public get comp(): TextMeshPro { return this._comp; }

    public init(text: TextMeshPro) {
        this._comp = text;

        if (CC_EDITOR) {
            return;
        }
        let material = this._comp.getMaterial(0);
        this._comp.updateTmpMatFace(material);
        this._comp.updateTmpMatOutline(material);
        this._comp.updateTmpMatUnderlay(material);
        this._comp.updateTmpMatGlow(material);
    }
}

@ccclass
@executeInEditMode
@menu("TextMeshPro组件/TextMeshPro")
export default class TextMeshPro extends cc.RenderComponent {

    @property
    private _string: string = "";
    @property({ multiline: true })
    public get string(): string { return this._string; }
    public set string(v: string) {
        if (this._string === v) { return; }
        this._string = v;
        this["setVertsDirty"]();
        this._checkStringEmpty();
    }

    @property(cc.JsonAsset)
    private _font: cc.JsonAsset = null;
    @property({ tooltip: CC_DEV && "字体资源\n依赖的纹理请勿打入图集\n在编辑器内拖拽此文件时，纹理必须和此文件处于同一目录下", type: cc.JsonAsset })
    private get font(): cc.JsonAsset { return this._font; }
    private set font(v: cc.JsonAsset) {
        if (this._font === v) { return; }
        this._font = v;
        if (CC_EDITOR) {
            this.editorInit();
        } else {
            if (!this.enabledInHierarchy) { return; }
            this.forceUpdateRenderData();
        }
    }

    @property({ type: cc.Label.HorizontalAlign })
    private _horizontalAlign: cc.Label.HorizontalAlign = cc.Label.HorizontalAlign.LEFT;
    @property({ type: cc.Label.HorizontalAlign })
    public get horizontalAlign(): cc.Label.HorizontalAlign { return this._horizontalAlign; }
    public set horizontalAlign(v: cc.Label.HorizontalAlign) {
        if (this._horizontalAlign === v) { return; }
        this._horizontalAlign = v;
        this["setVertsDirty"]();
    }

    @property({ type: cc.Label.VerticalAlign })
    private _verticalAlign: cc.Label.VerticalAlign = cc.Label.VerticalAlign.TOP;
    @property({ type: cc.Label.VerticalAlign })
    public get verticalAlign(): cc.Label.VerticalAlign { return this._verticalAlign; }
    public set verticalAlign(v: cc.Label.VerticalAlign) {
        if (this._verticalAlign === v) { return; }
        this._verticalAlign = v;
        this["setVertsDirty"]();
    }

    @property
    private _actualFontSize: number = 0;
    @property({ visible() { return this._overflow === TmpOverflow.SHRINK; } })
    public get actualFontSize(): number { return this._actualFontSize; }

    @property
    public get bmfontOriginalSize(): number { return this.font ? this.font.json.size : -1; }

    @property
    private _fontSize: number = 32;
    @property({ range: [0, 1024] })
    public get fontSize(): number { return this._fontSize; }
    public set fontSize(v: number) {
        if (this._fontSize === v) { return; }
        this._fontSize = v;
        this["setVertsDirty"]();
    }

    @property
    private _lineHeight: number = 32;
    @property
    public get lineHeight(): number { return this._lineHeight; }
    public set lineHeight(v: number) {
        if (this._lineHeight === v) { return; }
        this._lineHeight = v;
        this["setVertsDirty"]();
    }

    @property
    private _spacingX: number = 0;
    @property
    public get spacingX(): number { return this._spacingX; }
    public set spacingX(v: number) {
        if (this._spacingX === v) { return; }
        this._spacingX = v;
        this["setVertsDirty"]();
    }

    @property({ type: cc.Enum(TmpOverflow) })
    private _overflow: TmpOverflow = TmpOverflow.NONE;
    @property({ tooltip: CC_DEV && "文本的排版方式", type: cc.Enum(TmpOverflow) })
    public get overflow(): TmpOverflow { return this._overflow; }
    public set overflow(v: TmpOverflow) {
        if (this._overflow === v) { return; }
        this._overflow = v;
        this["setVertsDirty"]();
    }

    @property
    private _enableWrapText: boolean = true;
    @property({
        tooltip: CC_DEV && "是否启用自动换行",
        visible() {
            return this._overflow === TmpOverflow.CLAMP || this._overflow === TmpOverflow.ELLIPSIS;
        }
    })
    public get enableWrapText(): boolean { return this._enableWrapText; }
    public set enableWrapText(v: boolean) {
        if (this._enableWrapText === v) { return; }
        this._enableWrapText = v;
        this["setVertsDirty"]();
    }

    @property
    private _enableItalic: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用斜体" })
    public get enableItalic(): boolean { return this._enableItalic; }
    public set enableItalic(v: boolean) {
        if (this._enableItalic === v) { return; }
        this._enableItalic = v;
        this["setVertsDirty"]();
    }

    @property
    private _enableUnderline: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用下划线" })
    public get enableUnderline(): boolean { return this._enableUnderline; }
    public set enableUnderline(v: boolean) {
        if (this._enableUnderline === v) { return; }
        this._enableUnderline = v;
        this["setVertsDirty"]();
    }

    @property
    private _underlineOffset: number = 0;
    @property({ tooltip: CC_DEV && "下划线高度偏移", visible() { return this._enableUnderline; } })
    public get underlineOffset(): number { return this._underlineOffset; }
    public set underlineOffset(v: number) {
        if (this._underlineOffset === v) { return; }
        this._underlineOffset = v;
        this["setVertsDirty"]();
    }

    @property
    private _enableStrikethrough: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用删除线" })
    public get enableStrikethrough(): boolean { return this._enableStrikethrough; }
    public set enableStrikethrough(v: boolean) {
        if (this._enableStrikethrough === v) { return; }
        this._enableStrikethrough = v;
        this["setVertsDirty"]();
    }

    @property
    private _strikethroughOffset: number = 0;
    @property({ tooltip: CC_DEV && "删除线高度偏移", visible() { return this._enableStrikethrough; } })
    public get strikethroughOffset(): number { return this._strikethroughOffset; }
    public set strikethroughOffset(v: number) {
        if (this._strikethroughOffset === v) { return; }
        this._strikethroughOffset = v;
        this["setVertsDirty"]();
    }

    @property
    private _colorGradient: boolean = false;
    @property({ tooltip: CC_DEV && "是否启用颜色渐变，会和顶点颜色混合为最终的顶点颜色" })
    public get colorGradient(): boolean { return this._colorGradient; }
    public set colorGradient(v: boolean) {
        if (this._colorGradient === v) { return; }
        this._colorGradient = v;
        this._colorExtraDirty = true;
    }

    @property(cc.Color)
    private _colorLB: cc.Color = cc.Color.WHITE;
    @property({ tooltip: CC_DEV && "左下顶点", type: cc.Color, visible() { return this._colorGradient; } })
    public get colorLB(): cc.Color { return this._colorLB; }
    public set colorLB(v: cc.Color) {
        if (!CC_EDITOR && this._colorLB === v) { return; }
        this._colorLB = v;
        this._colorExtraDirty = true;
    }

    @property(cc.Color)
    private _colorRB: cc.Color = cc.Color.WHITE;
    @property({ tooltip: CC_DEV && "右下顶点", type: cc.Color, visible() { return this._colorGradient; } })
    public get colorRB(): cc.Color { return this._colorRB; }
    public set colorRB(v: cc.Color) {
        if (!CC_EDITOR && this._colorRB === v) { return; }
        this._colorRB = v;
        this._colorExtraDirty = true;
    }

    @property(cc.Color)
    private _colorLT: cc.Color = cc.Color.WHITE;
    @property({ tooltip: CC_DEV && "左上顶点", type: cc.Color, visible() { return this._colorGradient; } })
    public get colorLT(): cc.Color { return this._colorLT; }
    public set colorLT(v: cc.Color) {
        if (!CC_EDITOR && this._colorLT === v) { return; }
        this._colorLT = v;
        this._colorExtraDirty = true;
    }

    @property(cc.Color)
    private _colorRT: cc.Color = cc.Color.WHITE;
    @property({ tooltip: CC_DEV && "右上顶点", type: cc.Color, visible() { return this._colorGradient; } })
    public get colorRT(): cc.Color { return this._colorRT; }
    public set colorRT(v: cc.Color) {
        if (!CC_EDITOR && this._colorRT === v) { return; }
        this._colorRT = v;
        this._colorExtraDirty = true;
    }

    @property({ tooltip: CC_DEV && "材质参数", type: TmpUniform })
    public tmpUniform: TmpUniform = new TmpUniform();

    @property({ tooltip: CC_DEV && "字体所依赖的纹理", type: cc.Texture2D, readonly: true })
    public textures: cc.Texture2D[] = [];

    private _fontConfig: TmpFontConfig = null;
    /** 字体配置管理 */
    public get fontConfig(): TmpFontConfig { return this._fontConfig; }

    protected _assembler: TmpAssembler = null;
    private _worldVertsDirty: boolean = false;
    private _colorExtraDirty: boolean = false;

    private _richTextDeltaX: number = 0;
    /** 记录letterRight与nextTokenX的差值，供富文本排版使用 */
    public get richTextDeltaX(): number { return this._richTextDeltaX; }

    private editorInit(): void {
        if (CC_EDITOR) {
            // 加载图集
            if (!this._font || !this._font["_uuid"]) {
                this.textures = [];
                this.forceUpdateRenderData();
                return;
            }
            // cc.log(this._font["_uuid"]);
            Editor.assetdb.queryUrlByUuid(this._font["_uuid"], (error: any, url: string) => {
                // cc.log(url);
                if (!url) {
                    return;
                }
                let start = 12;
                let end = url.lastIndexOf("/");
                let dir = url.slice(start, end + 1);
                let arr: Promise<cc.Texture2D>[] = [];
                this._font.json.pageData.forEach((v) => {
                    let imgUrl = dir + v.file;
                    arr.push(TmpUtils.load<cc.Texture2D>(imgUrl));
                });
                Promise.all(arr).then((v) => {
                    this.textures = v;
                    this._fontConfig = new TmpFontConfig(this._font.json, this.textures);

                    if (!this.enabledInHierarchy) { return; }
                    this.forceUpdateRenderData();
                    this._onBMFontTextureLoaded();
                });

            });
        }
    }

    protected resetInEditor(): void {
        if (CC_EDITOR) {
            TmpUtils.load<cc.Material>(TmpUtils.TMP_MAT).then((mat) => {
                if (mat) {
                    this.setMaterial(0, mat);
                }
            });
        }
    }

    protected onLoad(): void {
        this.editorInit();
        this.tmpUniform.init(this);
        if (!this._fontConfig && this.font && this.textures.length > 0) {
            this._fontConfig = new TmpFontConfig(this._font.json, this.textures);
        }
    }

    protected onEnable(): void {
        super.onEnable();
        this.node.on(cc.Node.EventType.SIZE_CHANGED, this._nodeSizeChanged, this);
        this.node.on(cc.Node.EventType.ANCHOR_CHANGED, this["setVertsDirty"], this);

        this.forceUpdateRenderData();
    }

    protected onDisable(): void {
        super.onDisable();
        this.node.off(cc.Node.EventType.SIZE_CHANGED, this._nodeSizeChanged, this);
        this.node.off(cc.Node.EventType.ANCHOR_CHANGED, this["setVertsDirty"], this);
    }

    protected lateUpdate(dt: number): void {
        if (this._worldVertsDirty) {
            this._worldVertsDirty = false;
            this._assembler.updateWorldVerts(this);
        }
        if (this._colorExtraDirty) {
            this._colorExtraDirty = false;
            this._assembler.updateColorExtra(this);
        }
    }

    private _nodeSizeChanged(): void {
        // Because the content size is automatically updated when overflow is NONE.
        // And this will conflict with the alignment of the CCWidget.
        if (CC_EDITOR || this.overflow !== TmpOverflow.NONE) {
            this["setVertsDirty"]();
        }
    }

    private _validateRender(): void {
        if (!this.string) {
            this["disableRender"]();
            return;
        }

        if (this.getMaterial(0)) {
            if (this.textures.length > 0) {
                return;
            }
        }
        this["disableRender"]();
    }

    protected _resetAssembler(): void {
        cc.RenderComponent.prototype["_resetAssembler"].call(this);
    }

    private _checkStringEmpty(): void {
        this["markForRender"](!!this.string);
    }

    private _on3DNodeChanged(): void {
        this._resetAssembler();
        this._applyFontTexture();
    }

    private _onBMFontTextureLoaded(): void {
        this["markForRender"](true);
        this._updateMaterial();
    }

    private _onBlendChanged(): void {
        if (!this.enabledInHierarchy) return;

        this.forceUpdateRenderData();
    }

    private _applyFontTexture(): void {
        this["markForValidate"]();
    }

    private _updateMaterial(): void {
        let material = this.getMaterial(0);
        if (!material) {
            return;
        }

        cc.BlendFunc.prototype["_updateMaterialBlendFunc"].call(this, material);

        // 更新材质参数
        this._updateTmpMatTexture(material);
        if (!this.tmpUniform || !this.tmpUniform.comp) {
            return;
        }
        this.updateTmpMatFace(material);
        this.updateTmpMatOutline(material);
        this.updateTmpMatUnderlay(material);
        this.updateTmpMatGlow(material);
    }

    public _updateTmpMatTexture(material: cc.Material): void {
        if (!material || this.textures.length <= 0) {
            return;
        }
        material.define("USE_TEXTURE_LEVEL_1", this.textures.length > 0);
        material.define("USE_TEXTURE_LEVEL_2", this.textures.length > 1);
        material.define("USE_TEXTURE_LEVEL_3", this.textures.length > 2);
        material.define("USE_TEXTURE_LEVEL_4", this.textures.length > 4);
        for (let i = 0; i < this.textures.length; i++) {
            material.setProperty(`texture${i}`, this.textures[i]);
        }
        material["_effect"]._dirty = true;
    }

    public updateTmpMatFace(material: cc.Material): void {
        if (!material) {
            return;
        }
        material.setProperty("faceColor", this.tmpUniform.faceColor);
        material.setProperty("faceDilate", this.tmpUniform.faceDilate);
        material.setProperty("faceSoftness", this.tmpUniform.faceSoftness);
        material["_effect"]._dirty = true;
    }

    public updateTmpMatOutline(material: cc.Material): void {
        if (!material) {
            return;
        }
        material.define("USE_OUTLINE", this.tmpUniform.enableOutline);
        if (this.tmpUniform.enableOutline) {
            material.setProperty("outlineColor", this.tmpUniform.outlineColor);
            material.setProperty("outlineThickness", this.tmpUniform.outlineThickness);
        }
        material["_effect"]._dirty = true;
    }

    public updateTmpMatUnderlay(material: cc.Material): void {
        if (!material) {
            return;
        }
        material.define("USE_UNDERLAY", this.tmpUniform.enableUnderlay);
        if (this.tmpUniform.enableUnderlay) {
            material.setProperty("underlayColor", this.tmpUniform.underlayColor);
            material.setProperty("underlayOffsetX", this.tmpUniform.underlayOffset.x);
            material.setProperty("underlayOffsetY", this.tmpUniform.underlayOffset.y);
            material.setProperty("underlayDilate", this.tmpUniform.underlayDilate);
            material.setProperty("underlaySoftness", this.tmpUniform.underlaySoftness);
        }
        material["_effect"]._dirty = true;
    }

    public updateTmpMatGlow(material: cc.Material): void {
        if (!material) {
            return;
        }
        material.define("USE_GLOW", this.tmpUniform.enableGlow);
        if (this.tmpUniform.enableGlow) {
            material.setProperty("glowColor", this.tmpUniform.glowColor);
            material.setProperty("glowOffset", this.tmpUniform.glowOffset);
            material.setProperty("glowInner", this.tmpUniform.glowInner);
            material.setProperty("glowOuter", this.tmpUniform.glowOuter);
            material.setProperty("glowPower", this.tmpUniform.glowPower);
        }
        material["_effect"]._dirty = true;
    }

    /**
     * 立即更新渲染数据
     */
    public forceUpdateRenderData(): void {
        this["setVertsDirty"]();
        this._resetAssembler();
        this._applyFontTexture();
        this._assembler && this._assembler.updateRenderData(this);
        this.node["_renderFlag"] |= cc["RenderFlow"].FLAG_COLOR;
    }

    /**
     * 设置字体，必须调用此接口去动态设置字体
     */
    public setFont(font: cc.JsonAsset, textures: cc.Texture2D[]): void {
        if (!font || textures.length < 0) {
            cc.error(`please check your font!`);
            return;
        }

        this._font = font;
        this.textures = textures;
        this._fontConfig = new TmpFontConfig(this._font.json, this.textures);
        if (!this.enabledInHierarchy) { return; }
        this.forceUpdateRenderData();
        this._onBMFontTextureLoaded();
    }

    //#region 顶点数据操作接口，必须保证组件启用且节点激活才可使用这些接口
    /**
     * 根据字符下标判断此字符是否可见
     */
    public isVisible(index: number): boolean {
        if (!this.enabledInHierarchy) { return false; }
        return this._assembler.isVisble(index);
    }

    /**
     * 根据字符下标设置字符是否可见
     */
    public setVisible(index: number, visible: boolean): void {
        if (!this.enabledInHierarchy) { return; }
        this._assembler.setVisible(this, index, visible);
    }

    /**
     * 根据字符下标获取颜色顶点数据，顺序为[左下, 右下, 左上, 右上]
     */
    public getColorExtraVertices(index: number): [cc.Color, cc.Color, cc.Color, cc.Color] | null {
        if (!this.enabledInHierarchy) { return null; }
        return this._assembler.getColorExtraVertices(index);
    }

    /**
     * 根据字符下标设置颜色顶点数据，会和节点颜色混合为最终的顶点颜色，顺序为[左下, 右下, 左上, 右上]
     */
    public setColorExtraVertices(index: number, data: [cc.Color, cc.Color, cc.Color, cc.Color]): void {
        if (!this.enabledInHierarchy) { return; }
        this._assembler.setColorExtraVertices(index, data);
    }

    /**
     * 根据字符下标获取坐标顶点数据，顺序为[左下, 右下, 左上, 右上]
     */
    public getPosVertices(index: number): [cc.Vec2, cc.Vec2, cc.Vec2, cc.Vec2] | null {
        if (!this.enabledInHierarchy) { return null; }
        return this._assembler.getPosVertices(index);
    }

    /**
     * 根据字符下标设置坐标顶点数据，顺序为[左下, 右下, 左上, 右上]
     */
    public setPosVertices(index: number, data: [cc.Vec2, cc.Vec2, cc.Vec2, cc.Vec2]): void {
        if (!this.enabledInHierarchy) { return; }
        this._assembler.setPosVertices(index, data);
        this._worldVertsDirty = true;
    }
    //#endregion
}

cc["Assembler"].register(TextMeshPro, {
    getConstructor(comp: TextMeshPro) {
        return TmpAssembler;
    }
});
