import Container from "../display/Container";
import Texture from "../texture/Texture";
import Sprite from "../display/Sprite";
import { Event } from "../events/Event";

import { TextureCache, createImage, createCanvas } from "../utils";
import { RAD_TO_DEG } from "../const";
import { Matrix } from "../math";
import { BaseTexture } from "../texture";

/**
 * 用于播放动画
 * 帧数监听，已ued给的文件为主，不做补间增加帧数，
 * 暂无setImage,setText再加
 * 暂时无矢量图和遮罩解析
 * 暂不支持加快播放，
 * 暂不支持loops循环次数
 * 考虑是否支持加快播放，如果能加速，中间帧数监听会出问题
 * 考虑是否帧率锁步，同上问题,锁步后会可能失效currentFrame判断
 * s.addEventListener(Event.ENTER_FRAME, func = (e) => {
 *      //这个判断如果只希望执行一次得多加一个条件isInTimeFrame为true，或者及时移除
 *      if (e.target.currentFrame == 66&&e.target.isInTimeFrame) {
 *         e.target.removeEventListener(Event.ENTER_FRAME, func);
 *    }
 * });
 * 锁步时的每帧监听，用>或<判断，及时移除
 * @class MovieClip
 * @since 1.0.0
 * @public
 * @extends Container
 */
export class MovieClip extends Container {
    /**
     * 原始数据，接口在解析类上，不搞先
     */
    private rawData
    /**
     * 锁步将按时间间隔来执行动画
     */
    public lockStep: boolean = false;
    /**
     * mc的当前帧,从1开始
     * @property currentFrame
     * @public
     * @since 1.0.0
     * @type {number}
     * @default 1
     * @readonly
     */
    public get currentFrame(): number {
        return this._curFrame;
    }

    /**
     * @property _curFrame
     * @type {number}
     * @private
     * @since 2.0.0
     * @default 1
     */
    private _curFrame: number = 1;

    /**
     * 当前动画是否处于播放状态
     * @property isPlaying
     * @readOnly
     * @public
     * @since 1.0.0
     * @type {boolean}
     * @default true
     * @readonly
     */
    public get isPlaying(): boolean {
        return this._isPlaying;
    }

    /**
     * @property _isPlaying
     * @type {boolean}
     * @private
     * @since 2.0.0
     * @default true
     */
    private _isPlaying: boolean = true;

    /**
     * 动画的播放方向,是顺着播还是在倒着播
     * @property isFront
     * @public
     * @since 1.0.0
     * @type {boolean}
     * @default true
     * @readonly
     */
    get isFront(): boolean {
        return this._isFront;
    }

    /**
     * @property _isFront
     * @type {boolean}
     * @private
     * @default true
     */
    private _isFront: boolean = true;

    /**
     * 当前动画的总帧数
     * @property totalFrames
     * @public
     * @since 1.0.0
     * @type {number}
     * @default 0
     * @readonly
     */
    get totalFrames(): number {
        return this.rawData && this.rawData.frames || 0;
    }
    get videoWidth(): number {
        return this.rawData && this.rawData.videoSize.width;
    };
    get videoHeight(): number {
        return this.rawData && this.rawData.videoSize.height;
    };
    /**
     * 锁步的时间间隔，按fps定,毫秒
     */
    private timeInterval;

    /**
     * 前提引擎按60设置
     */
    private deltaFrame: number;
    /**
     * 中间帧计时
     */
    private frameCount: number = 0;
    /**
     * 构造函数
     * @method MovieClip
     * @public
     * @param mv VideoEntity格式，这里不给了
     */
    public constructor(mv?) {
        super();
        let s: any = this;
        s._instanceType = "MovieClip";
        //初始化
        if (mv) s.init(mv);
    }

    /**
     * 可以手动用init，
     * @param mv 
     */
    init(mv) {
        if (!mv) return;
        //移除所有子级
        this.removeAllChildren();
        //移除循环监听事件;
        if (this.startAniRangeFun) this.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this);
        //记录源数据
        this.rawData = mv;
        //记录基本信息，fps，每秒输出帧数，frames，总帧数，videoSize暂时不管
        //如果fps小于60怎么处理。update时怎么处理
        this.timeInterval = 1000 / mv.FPS;
        this.startTime = Date.now();
        this.startFrame = 1;
        //间隔帧数，
        this.deltaFrame = 60 / mv.FPS;
        this.frameCount = this.deltaFrame;
        this._curFrame = 1;

        //考虑是否要加遮罩，用videoSize，暂时不用，为了性能
        // var mask = new Graphics();
        // mask.beginFill(0xffffff, 1);
        // mask.drawRect(0, 0, mv.videoSize.width, mv.videoSize.height);
        // mask.endFill();
        // this.mask = mask;

        //缓存图片
        if (mv.images && !mv.textures) {//带图片数据的待测试
            mv.textures = {};
            for (var key in mv.images) {
                var src = mv.images[key];
                if (src.indexOf("iVBO") === 0 || src.indexOf("/9j/2w") === 0) {
                    src = 'data:image/png;base64,' + src;
                }//图片链接时的宽高适配再说，暂时没有遇到过
                let imgTag = createImage();
                imgTag.src = src;
                mv.textures[key] = new Texture(new BaseTexture(imgTag));//这种方法不会缓存进全局
            }
        }
        this.initChildren(mv.sprites);

        //缓存所有图片
        // const images = mv.images;
        // if (mv.hasBeenCached) {
        //     //如已被缓存，则直接取了赋值
        //     for (var keyName in images) {

        //         this.textures[keyName] = TextureCache[keyName];
        //     }
        //     this.initChildren(mv.sprites)
        // } else {
        //     //合图有bug，先不合了
        //     // if (GlobalPro.stageRenderType == RENDERER_TYPE.WEBGL) {
        //     //     //缓存成一张canvas，考虑要不要把所有资源搞成图集
        //     //     DrawAllToCanvas(images, (t) => {
        //     //         this.textures = t;
        //     //         this.initChildren(mv.sprites)
        //     //         //缓存
        //     //         for (var key in this.textures) {
        //     //             Texture.addToCache(this.textures[key], key)
        //     //         }
        //     //         mv.hasBeenCached = true;
        //     //     })
        //     // } else {
        //     //canvas直接缓存
        //     for (var key in images) {
        //         var bitmap = images[key];
        //         // let imgTag = document.createElement('img');

        //         let imgTag = getCreateImage()();
        //         let backCanvas;
        //         if (bitmap.indexOf("iVBO") === 0 || bitmap.indexOf("/9j/2w") === 0) {
        //             imgTag.src = 'data:image/png;base64,' + bitmap;
        //         }
        //         else {
        //             imgTag.src = bitmap;
        //             //这里有问题，再说//这里估计是直接用图片链接的，为了适配画在canvas，以后遇到再说
        //             // if (frames[0] && frames[0].layout) {
        //             //     backCanvas = document.createElement('canvas');
        //             //     backCanvas.width = frames[0].layout.width
        //             //     backCanvas.height = frames[0].layout.height
        //             //     imgTag.onload = function () {
        //             //         backCanvas.getContext('2d').drawImage(imgTag, 0, 0, frames[0].layout.width, frames[0].layout.height)
        //             //     }
        //             // }
        //         }
        //         this.textures[key] = Texture.from(backCanvas || imgTag);
        //         // this.textures[key].once("loaded",()=>{
        //         //     是否监听加载完成？
        //         // })
        //         //考虑到key和已有缓存冲突，svga的都单独自己缓存，外部不可用，以后有时间搞
        //         Texture.addToCache(this.textures[key], key)
        //     }
        //     mv.hasBeenCached = true;
        //     this.initChildren(mv.sprites)
        //     // }
        // }
    }

    private initChildren(sprites) {
        for (var i = 0, len = sprites.length; i < len; i++) {
            var ele = sprites[i];
            if (ele.imageKey) {
                // var child = new Sprite(this.textures[ele.imageKey]);
                var child = this.addChild(new Sprite(
                    this.rawData.textures ?
                        this.rawData.textures[ele.imageKey] ://自身没存的，取全局的，有图片单独处理出去
                        TextureCache[ele.imageKey] ||
                        TextureCache[ele.imageKey + ".png"] || null
                ));
                //直接赋值矩阵
                child["frames"] = ele.frames;
                //透明度处理
                if (ele.frames[0].alpha < 0.05) {
                    // child.visible = false;
                    child.alpha = 0;
                } else {
                    child.alpha = ele.frames[0].alpha;
                    //透明度不为0才赋值吧
                    child.transform.localMatrix.copy(ele.frames[0].transform);
                    child.transform._parentID = -1;
                }
                //记录一个源数据
                child["framesOri"] = ele.frames;
                //记录一个源imageKey
                child["imageKey"] = ele.imageKey;
                //记录一个标识
                // child["matteKey"] = ele.matteKey;
            }
        }
    }

    /**
     * 
     * @param imagekey 对应的imagekey
     * @param imageUrl 图片路径，以后考虑支持多种形式
     */
    setImage(imagekey: string, imageUrl: string) {//新修改，待测试TODO
        //先获取原先贴图，为了宽高适配，，这种如何保证base64已经加载完毕，考虑lottie的图片宽高信息存过来？
        let cs: Sprite[] = [];//找都找了，就全记录吧
        for (var i = 0; i < this.children.length; i++) {
            if (this.children[i]["imageKey"] == imagekey) {
                cs.push(this.children[i] as Sprite)
            }
        }
        if (!cs.length) return;
        var texture: Texture = cs[0].texture;
        var width = texture.width;
        var height = texture.height;
        let image = createImage();
        var self = this;
        image.onload = function () {
            let newTexture: Texture
            if (image.width == width && image.height == height) {
                newTexture = Texture.from(image);
            } else {
                var canvas = createCanvas()//document.createElement("canvas");
                canvas.width = width;
                canvas.height = height;
                var ctx = canvas.getContext("2d")
                //适配绘制,为了全部显示在canvas中
                var scaleCan = width / height;
                var scale = image.width / image.height;
                if (scaleCan > scale) {
                    //定高
                    ctx.drawImage(image, 0, 0, image.width, image.height
                        , (width - scale * height) / 2, 0, scale * height, height)
                } else {
                    //定宽
                    ctx.drawImage(image, 0, 0, image.width, image.height
                        , 0, (height - width / scale) / 2, width, width / scale)
                }
                newTexture = Texture.fromCanvas(canvas)
            }
            //修改相应sprite
            cs.forEach((c) => { c.texture = newTexture; })
        }
        image.src = imageUrl;
    }

    /**
     * 调用止方法将停止当前帧
     * @method stop
     * @public
     * @since 1.0.0
     */
    public stop(): void {
        let s = this;
        s._isPlaying = false;
    }

    /**
     * 将播放头向后移一帧并停在下一帧,如果本身在最后一帧则不做任何反应
     * @method nextFrame
     * @since 1.0.0
     * @public
     */
    public nextFrame(): void {
        let s = this;
        if (s._curFrame < s.totalFrames) {
            s._curFrame++;
        }
        s._isPlaying = false;
    }

    /**
     * 将播放头向前移一帧并停在下一帧,如果本身在第一帧则不做任何反应
     * @method prevFrame
     * @since 1.0.0
     * @public
     */
    public prevFrame(): void {
        let s = this;
        if (s._curFrame > 1) {
            s._curFrame--;
        }
        s._isPlaying = false;
    }

    /**
     * 将播放头跳转到指定帧并停在那一帧,如果本身在第一帧则不做任何反应
     * @method gotoAndStop
     * @public
     * @since 1.0.0
     * @param {number} frameIndex 批定帧的帧数或指定帧的标签名
     */
    public gotoAndStop(frameIndex: number): void {
        let s: any = this;
        s._isPlaying = false;
        if (frameIndex > s.totalFrames) {
            frameIndex = s.totalFrames;
        }
        if (frameIndex < 1) {
            frameIndex = 1;
        }
        s._curFrame = <number>frameIndex;
    }

    /**
     * 如果当前时间轴停在某一帧,调用此方法将继续播放.
     * @method play
     * @public
     * @since 1.0.0
     */
    public play(isFront: boolean = true): void {
        let s = this;
        s.frameCount = s.deltaFrame;
        s.startTime = Date.now();
        s.startFrame = s._curFrame;
        s._isPlaying = true;
        s._isFront = isFront;
    }

    /**
     * @property _lastFrame
     * @type {number}
     * @private
     * @default 0
     */
    private _lastFrame: number = 0;

    /**
     * 刚执行到的帧数，用于帧监听时判断用，刚好执行到当前帧，而不是之前保留的状态
     * 不是60fps的videoItem的中间有几帧curFrame会不变，判断只执行一次监听时会出错，刚好动画满帧60fps时就无所谓
     */
    public get isInTimeFrame(): boolean {
        //相等时就是刚开始的curFrame
        return this.frameCount == this.deltaFrame;
    }


    /**
     * 将播放头跳转到指定帧并从那一帧开始继续播放
     * @method gotoAndPlay
     * @public
     * @since 1.0.0
     * @param {number} frameIndex 批定帧的帧数或指定帧的标签名
     * @param {boolean} isFront 跳到指定帧后是向前播放, 还是向后播放.不设置些参数将默认向前播放
     */
    public gotoAndPlay(frameIndex: number, isFront: boolean = true): void {
        let s: any = this;
        s._isFront = isFront;
        s._isPlaying = true;
        if (frameIndex > s.totalFrames) {
            frameIndex = s.totalFrames;
        }
        if (frameIndex < 1) {
            frameIndex = 1;
        }
        s.frameCount = s.deltaFrame;
        s.startTime = Date.now();
        s._curFrame = <number>frameIndex;
        s.startFrame = s._curFrame;
    }

    /**
     * 记录的startAniRange对应的fun
     */
    private startAniRangeFun: Function
    /**
     * 优先级最高，会覆盖
     * @param beginFrame 默认1
     * @param endFrame 默认 this.totalFrames
     * @param loops 默认1 小于等于0为无线循环，
     * @param callback 动画循环次数播放结束时回调，无限循环时不会有回调
     */
    public startAniRange(
        beginFrame: number = 1,
        endFrame: number = this.totalFrames,
        loops: number = 1,
        callback?: Function
    ) {
        //兼容下传0或null等等
        beginFrame = beginFrame || 1;
        endFrame = endFrame || this.totalFrames;
        loops = loops || 0;
        loops = Math.max(0, loops);//去掉负数

        if (beginFrame < 1) {
            beginFrame = 1;
        }
        if (beginFrame > this.totalFrames) {
            beginFrame = this.totalFrames;
        }
        if (endFrame < 1) {
            endFrame = 1;
        }
        if (endFrame > this.totalFrames) {
            endFrame = this.totalFrames;
        }
        if (beginFrame === endFrame) {
            this.gotoAndStop(beginFrame)
            //如果相等
            return
        } else if (beginFrame < endFrame) {
            this._isFront = true;

        } else {
            this._isFront = false;
            // var temp = beginFrame;//不能颠倒，待测试
            // beginFrame = endFrame;
            // endFrame = temp;
        }
        //移除原先的绑定吧
        if (this.startAniRangeFun) this.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this)
        this._curFrame = beginFrame;
        //赋值count最大
        this.frameCount = this.deltaFrame;
        this.startTime = Date.now();
        this.startFrame = this._curFrame;
        this._isPlaying = true;

        let loopCount = loops ? (loops + 0.5 >> 0) : Infinity;
        this.addEventListener(Event.ENTER_FRAME, this.startAniRangeFun = (e) => {
            if (e.target._isFront) {
                //用大于等于可以避免锁步时出现的问题
                if (e.target.currentFrame >= endFrame || e.target._endMark) {
                    loopCount--;
                    if (loopCount <= 0) {
                        e.target.gotoAndStop(endFrame);
                        e.target.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this);
                        this.startAniRangeFun = null;
                        callback && callback();
                    } else {
                        e.target.gotoAndPlay(beginFrame);
                    }
                }
            } else {
                if (e.target.currentFrame <= beginFrame || e.target._endMark) {
                    loopCount--
                    if (loopCount <= 0) {
                        e.target.gotoAndStop(beginFrame);
                        e.target.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this);
                        this.startAniRangeFun = null;
                        callback && callback();
                    } else {
                        e.target.gotoAndPlay(endFrame, false);
                    }
                }
            }
        }, this)
    }

    /**
     * 开始时间，每次有play的时候就需要重置now
     * 锁步思想，设置开始时间，后面每帧实际时间与开始时间相减，得到当前帧数
     * 
     */
    private startTime: number;
    /**
     * 开始时的frame
     */
    private startFrame: number;

    /**
     * 与startFrame相间隔的帧数量，绝对值
     */
    private lastDeltaFrame: number
    /**
     * 锁步时的每次end的标识
     */
    private _endMark: boolean

    commonDeltaTime = 1000 / 60;
    updateFrame() {
        var s = this;
        //1帧的时候也有相应的frameCount，无用，return
        if (s.totalFrames == 0 || s.totalFrames == 1) return;
        let isNeedUpdate = false;
        if (s._lastFrame != s._curFrame) {
            //帧不相等
            isNeedUpdate = true;
            s._lastFrame = s._curFrame;

            //锁步的时候
            s.startTime = Date.now();
            s.startFrame = s._curFrame;

            s._endMark = false;
        } else {
            if (s._isPlaying) {
                if (s.lockStep) {
                    isNeedUpdate = s.getCurFrameWhenLockStep();
                } else {
                    if (--s.frameCount == 0) {
                        s.frameCount = s.deltaFrame;
                        isNeedUpdate = true;
                        if (s._isFront) {
                            s._curFrame++;
                            if (s._curFrame > s.totalFrames) {
                                s._curFrame = 1;
                            }
                        } else {
                            s._curFrame--;
                            if (s._curFrame < 1) {
                                s._curFrame = s.totalFrames;
                            }
                        }
                        s._lastFrame = s._curFrame;
                    }
                }
            }
        }
        //如果需要更新
        if (isNeedUpdate) {
            //对每个child还原对应的transform，alpha为0的默认visible设为false,避免计算
            for (var i = 0, len = s.children.length; i < len; i++) {
                var child = s.children[i]
                //只修改动画加入的child，不修改手动加入的，,所以想要修改动画中的元素属性，直接去掉frames属性，将不会执行动画
                if (child["frames"] && child["frames"][s._curFrame - 1]) {
                    var frame = child["frames"][s._curFrame - 1];
                    //layout不晓得干嘛用，暂不管
                    if (frame.alpha < 0.05) {
                        // child.visible = false;
                        child.alpha = 0;
                    } else {
                        // child.visible = true;
                        child.alpha = frame.alpha;
                        //先判断transform是否相等
                        if (!Matrix.isEqual(child.transform.localMatrix, frame.transform)) {
                            child.transform.localMatrix.copy(frame.transform)
                            child.transform._parentID = -1;
                        }
                    }
                }
            }
            //事件播放结束监听
            if (!s.lockStep) {
                if (((s._curFrame == 1 && !s._isFront) || (s._curFrame == s.totalFrames && s._isFront)) && s.hasEventListener(Event.END_FRAME)) {
                    s.dispatchEvent(Event.END_FRAME, {
                        frameIndex: s._curFrame,
                        frameName: "endFrame"
                    });
                }
            } else {
                //锁步的时候另外判断
                if (s._endMark && s.hasEventListener(Event.END_FRAME)) {
                    s.dispatchEvent(Event.END_FRAME, {
                        frameIndex: s._curFrame,
                        frameName: "endFrame"
                    });
                }
            }
        }

    }

    private getCurFrameWhenLockStep() {
        var dateNow = Date.now()
        //相差
        var deltaFrame = ((dateNow - this.startTime) / this.timeInterval) >> 0;
        //间隔帧数与上一帧一致，就不执行
        if (deltaFrame == this.lastDeltaFrame) {
            //设置不等
            this.frameCount = 0;
            return false
        }
        this._endMark = false;
        //相等，刚好执行切换
        this.frameCount = this.deltaFrame;
        this.lastDeltaFrame = deltaFrame
        if (this._isFront) {
            //取余数
            this._curFrame = (this.startFrame + deltaFrame) % this.totalFrames;
            if (this._curFrame == 0) {
                this._curFrame = this.totalFrames;
                this._endMark = true;
            }
            //当上一帧大于_curFrame,并且上一帧不是totalFrames时，说明跳过了最后一帧
            else if (this._lastFrame > this._curFrame &&
                this._lastFrame != this.totalFrames) {
                this._endMark = true;
            }
        } else {
            this._curFrame = (this.startFrame - deltaFrame) % this.totalFrames;
            if (this._curFrame == 0) {
                this._curFrame = this.totalFrames;
            } else if (this._curFrame < 0) {
                this._curFrame += this.totalFrames;
            }
            if (this._curFrame == 1) {
                this._endMark = true;
            }
            //当上一帧小于_curFrame,并且上一帧不是1时，说明跳过了第一帧
            else if (this._lastFrame < this._curFrame &&
                this._lastFrame != 1) {
                this._endMark = true;
            }
        }
        this._lastFrame = this._curFrame;
        return true
    }
    /**
     * 重写刷新
     * @method update
     * @public
     * @since 1.0.0
     */
    public update(): void {
        //更新帧数据
        this.updateFrame();
        super.update();
    }

    public destroy(): void {
        //todo-清除相应的数据引用
        this.rawData = null;
        super.destroy();
    }

    /**
     * 用源数据拷贝一份，用相应参数
     * @param frames 源数据
     * @param x 偏移x，默认0
     * @param y 偏移y，默认0
     * @param scaleX 相对缩放x，默认1
     * @param scaleY 相对缩放y，默认1
     * @param rotation 相对旋转,角度制，默认0
     * @param anchorX 相对锚点x，默认0
     * @param anchorY 相对锚点y，默认0
     */
    static deepCopyFrames(
        frames,
        x: number = 0,
        y: number = 0,
        scaleX: number = 1,
        scaleY: number = 1,
        rotation: number = 0,
        anchorX: number = 0,
        anchorY: number = 0
    ) {
        var cf = [];
        rotation *= Math.PI / 180;
        //@ts-ignore
        var lt: Matrix = {};
        lt.a = Math.cos(rotation) * scaleX;
        lt.b = Math.sin(rotation) * scaleX;
        lt.c = -Math.sin(rotation) * scaleY;
        lt.d = Math.cos(rotation) * scaleY;
        lt.tx = x + anchorX - ((anchorX * lt.a) + (anchorY * lt.c));
        lt.ty = y + anchorY - ((anchorX * lt.b) + (anchorY * lt.d));
        for (var j = 0; j < frames.length; j++) {
            var frame = frames[j];
            const pt = frame.transform;
            var f = { alpha: 0, transform: null };
            //透明度
            f.alpha = frame.alpha;
            f.transform = {
                a: (lt.a * pt.a) + (lt.b * pt.c),
                b: (lt.a * pt.b) + (lt.b * pt.d),
                c: (lt.c * pt.a) + (lt.d * pt.c),
                d: (lt.c * pt.b) + (lt.d * pt.d),
                tx: (lt.tx * pt.a) + (lt.ty * pt.c) + pt.tx,
                ty: (lt.tx * pt.b) + (lt.ty * pt.d) + pt.ty,
            };
            cf.push(f)
        }
        return cf;
    }
}