import {SvgaTrack} from "./SvgaTrack";
import {createImageAsync, createSpriteFrameAsync} from "./loadSvga";
import {
    _decorator,
    Asset,
    Component,
    ImageAsset,
    Layers,
    Node,
    Sprite,
    SpriteFrame,
    Texture2D,
    UITransform,
    v3
} from "cc";

const {ccclass, property} = _decorator;
import menu = _decorator.menu;
import disallowMultiple = _decorator.disallowMultiple;
import executeInEditMode = _decorator.executeInEditMode;
import playOnFocus = _decorator.playOnFocus;

@ccclass
@executeInEditMode
@disallowMultiple
@playOnFocus
@menu('自定义组件/Svga')
export default class Svga extends Component {

    static EventType = {
        END_FRAME: "SVGA_END_FRAME",
    }

    /************************ on Editor ************************/
    private edit_update: boolean = false;

    onFocusInEditor() {
        this.edit_update = true;
        this.play(0);
    }

    onLostFocusInEditor() {
        this.edit_update = false;
    }

    async resetInEditor() {
        await this._load();
        this.edit_update = true;
        this.play(0);
    }

    onEnable() {
        CC_EDITOR && this.play(0);
    }

    /************************ on Editor ************************/

    /************************  ************************/

    private aniNode: Node = null;

    @property(Asset)
    private _svga: Asset = null;

    @property(Asset)
    get svga() {
        return this._svga;
    }

    set svga(svga) {
        if (svga !== this._svga) {
            this._svga = svga;
            this._load().then(() => {
                this.play(0);
            });
        }
    }

    private tracks: SvgaTrack[] = [];

    isPlaying: boolean = false;

    vmData: SVGA.Video = null;

    async onLoad() {

        this.aniNode = this.node.getChildByName("aniNode");
        if (!this.aniNode) {
            this.aniNode = new Node("aniNode");
            this.aniNode.layer = Layers.Enum.UI_2D;
            this.aniNode.setScale(v3(1, -1, 1));
            this.node.addChild(this.aniNode);
        }

        await this._load();

        (this.autoPlay || CC_EDITOR) && this.play(0);
    }

    start() {

    }

    play(frame = 0) {
        this.curFrame = frame;
        this.isPlaying = true;
        return this;
    }

    stop(isReset: boolean = false) {
        if (isReset) this.curFrame = 0;
        this.isPlaying = false;
        return this;
    }


    // 初始化就播放
    @property({tooltip: "是否自动播放"})
    private autoPlay: boolean = true;


    // 是否循环
    @property
    private _loop: boolean = false;

    @property({tooltip: "是否循环"})
    get loop() {
        return this._loop;
    }

    set loop(loop) {
        this._loop = loop;
        CC_EDITOR && this.play();
    }

    // 总时间，秒计
    get totalTime(): number {
        return this._totalFrames * (1 / this.fps) || 0;
    };

    // 总帧数
    private _totalFrames: number = 0;
    get totalFrames() {
        return this._totalFrames;
    }

    // 帧时间
    private _frameTime = 0;

    // fps 没设置就会用vmData里的帧率
    @property
    private _fps: number = 0;

    @property
    get fps() {
        return this._fps || this.vmData?.fps || 0;
    }

    set fps(fps) {
        this._fps = fps;
        this._frameTime = 1 / fps;
    }


    // 当前时间
    private _curTime = 0;

    get curTime() {
        return this._curTime;
    }

    set curTime(time) {
        this._curTime = time;
        this._curFrame = time * this.fps;
    }


    // 当前帧
    private _curFrame = 0;

    get curFrame() {
        return this._curFrame;
    }

    set curFrame(frame) {
        this._curFrame = frame;
        this._curTime = frame / this.fps;
    }


    update(dt) {
        if (
            (CC_EDITOR && !this.edit_update)
            || !this.isPlaying
        ) return;

        this.curTime += dt;

        if (this._curFrame > this.totalFrames) {
            this.node.emit(Svga.EventType.END_FRAME);
            if (this.loop) {
                this.curTime = (this._curFrame - this.totalFrames) * this._frameTime;
            } else {
                this.stop(false);
            }
        }

        this.tracks.forEach((v) => {
            v.setValue(this._curFrame);
        });
    }

    protected onDestroy() {
        const tracks = this.tracks;
        const len = tracks.length;
        for (let i = 0; i < len; i++) {
            tracks[i].destroy();
        }
        this.tracks.length = 0;
    }

    async _load() {

        if (!this.aniNode || !this.svga) {
            console.log(!!this.aniNode, !!this.svga);
            return;
        }

        this.aniNode.removeAllChildren();

        const vm = this.svga["_file"] as SVGA.Video;

        vm["textures"] = {};
        this.vmData = vm;


        const {fps, size, images, sprites, frames,} = vm;

        if (!this.fps) this.fps = fps;
        this._totalFrames = frames;
        this.getComponent(UITransform).setContentSize(size.width, size.height);
        this.aniNode.setPosition(-size.width / 2, size.height / 2);

        const ps = [];

        for (let key in images) {
            let src = images[key];
            if (typeof src === "string") {
                if (src.indexOf("iVBO") === 0 || src.indexOf("/9j/2w") === 0) {
                    src = 'data:image/png;base64,' + src;
                }
                ps.push(
                    createSpriteFrameAsync(src).then((sp) => {
                        vm["textures"][key] = sp;
                    })
                    // createImageAsync(src).then((img) => {
                    //     const texture = new Texture2D();
                    //     texture.image = new ImageAsset(img);
                    //     const spr = new SpriteFrame();
                    //     spr.texture = texture;
                    //     vm["textures"][key] = spr;
                    // })
                );
            } else {
                const texture = new Texture2D();
                texture.setWrapMode(Texture2D.WrapMode.CLAMP_TO_EDGE, Texture2D.WrapMode.CLAMP_TO_EDGE, Texture2D.WrapMode.CLAMP_TO_EDGE);
                texture.image = new ImageAsset(src);
                const spr = new SpriteFrame();
                spr.texture = texture;
                vm["textures"][key] = spr;
            }

        }

        await Promise.all(ps);

        const len = sprites.length;
        for (let i = 0; i < len; i++) {
            const {imageKey, frames} = sprites[i];

            if (!imageKey) return;

            const node = new Node(imageKey);
            node.layer = Layers.Enum.UI_2D;
            node.addComponent(UITransform).setAnchorPoint(0, 1);

            this.aniNode.addChild(node);

            const sp = node.addComponent(Sprite);
            sp.spriteFrame = vm["textures"][imageKey];

            const track = new SvgaTrack(node, frames);
            track.resetValue();
            this.tracks.push(track);
        }

    }

}

