
import { AnimationClip, IAnimationTrack } from "../AnimationClip";
import { HashObject } from "../HashObject";
import { Container, Sprite } from "../display";
import { BaseTexture, Texture } from "../texture";
import { getBezierEasing } from "./forLottie/BezierEaser"
import { buildBezierProp } from "./forLottie/buildBezierProp";
import { createImage, TextureCache } from "../utils";
import { Shape } from "../graphics";
import { AnimationNode } from "./AnimationNode";

export class Lottie extends AnimationNode {
    /**
     * 原始数据，尽量只获取，不修改
     */
    protected rawData: LottieData;
    /**
     * 总时间，秒计
     */
    get totalTime(): number {
        if (!this.rawData) return 0;
        const { op, ip } = this.rawData;
        return (op - ip) * (1 / this.fps);
    };
    /**
     * 总帧数
     */
    get totalFrames(): number {
        if (!this.rawData) return 0;
        const { op, ip } = this.rawData;
        return op - ip;
    };
    /**
     * 动画显示宽度
     */
    get videoWidth(): number {
        return this.rawData && this.rawData.w || 0;
    };
    /**
     * 动画显示高度
     */
    get videoHeight(): number {
        return this.rawData && this.rawData.h || 0;
    };
    /**
     * 重写
     * 每秒刷新帧数，没设置过直接用数据里的
     */
    get fps() {
        //没设置过就用数据里的
        return this._fps || this.rawData && this.rawData.fr || null;
    }
    set fps(v: number) {
        this._fps = v;
    }
    //lottie图层主容器
    private lottieContainer: LottieContainer;
    constructor(data: LottieData) {
        super(data);//里面执行了init
        this._instanceType = "Lottie";
    }
    /**
     * 初始化方法
     * @param data 
     * @returns 
     */
    public init(data: LottieData) {
        if (!data || data == this.rawData) return;
        this.rawData = data;
        //名字
        this.name = data.nm;
        //初始化图片 有assets但无textures
        if (data.assets && !data.textures) {
            data.textures = {};
            data.assets.forEach((a) => {
                if (a.layers) {//合成嵌套的情况
                    data.compositions = data.compositions || {};
                    data.compositions[a.id] = a.layers;
                    return;
                }
                let imgTag = createImage();
                imgTag.src = a.p;
                data.textures[a.id] = new Texture(new BaseTexture(imgTag));
            })
        }
        //原先有的，先销毁
        this.lottieContainer && this.lottieContainer.destroy();
        //初始化一个容器；
        this.lottieContainer = this.addChild(new LottieContainer());
        //生成时间轴
        var tracks = createLottieTracks(
            this.lottieContainer,
            data.layers,
            data.ip,
            data.textures,
            data.compositions
        );
        //初始值设置下
        tracks.forEach((t) => {
            //初始值重置下
            t.resetValue();
            //表达式的加一个帧率，其实是最好createLottieTracks传入，再说了
            if (t.instanceType == "LottieExpressionTrack") {
                (t as LottieExpressionTrack).frameRate = data.fr;
            }
        });
        //合成所有时间轴
        if (!this.animationClip) {
            this.animationClip = new AnimationClip(tracks, this.totalFrames);
        } else {
            this.animationClip.init(tracks, this.totalFrames)
        }
        //数据更新
        this._onRawDataUpdate();
    }
}

interface LottieData {
    "fr": number,//帧率  30  60等
    "ip": number,//开始帧
    "op": number,//结束帧
    "w": number,//宽度
    "h": number,//高度
    "nm": string,//名字
    "layers": LayerData[],
    "assets"?: {
        "id": string,//图片id，与layers里的refId对应
        "w": number,
        "h": number,
        "p": string,//base64数据
        "layers": LayerData[],//合成嵌套的
    }[],
    "textures"?: { [key: string]: Texture }//缓存的贴图，为了上面的assets里的图片数据，不进全局缓存，
    // "components"?: { [key: string]: LottieData }
    compositions?: { [key: string]: LayerData[] }
}
interface LayerData {
    "ind": number,//id唯一
    "ty": number,//类型，2是图片，0是合成
    "nm": string//"owl_sleep.png",//暂时就是图片
    "refId": string,
    "parent"?: number,//父级id
    "ks": KsData;
    "ip": number,//开始帧
    "op": number,//结束帧
    "st": number,
    "w"?: number,//合成的会有
    "h"?: number,//合成的会有
    hasMask?: boolean,
    masksProperties?: ILottieMaskData[],//遮罩数据
}
interface KsData {
    o: KeyData //透明度
    r: KeyData //旋转
    p: KeyData //位置
    a: KeyData //锚点
    s: KeyData //缩放
}
interface KeyData {
    a: number,//貌似没用,0表示无关键帧，1表示有
    "s": boolean,//true表示区分维度，比如p的x和y分开走
    k: KeyAniData[] | number[] | number,
    x: string,//可能有表达式
}

interface KeyAniData {
    t: number,
    s: number[],

    i: { x: number | number[], y: number | number[] },
    o: { x: number | number[], y: number | number[] },

    to: number[],
    ti: number[],

    h: number,//暂时用是否存在判断。有标识瞬切，不缓动，待测试

    //后面记录的东西
    bezierData?,//to存在时的贝塞尔数据
    fnc?,//贝塞尔函数，可能是数组，

}
interface ILottieMaskData {
    inv: boolean,//true需要处理矩形wh的矩形遮罩
    mode: "n" | "a",//遮罩模式，一般是a
    pt: {
        "a": number,
        "k": {
            "i": [number, number][],
            "o": [number, number][],
            "v": [number, number][],
            "c": boolean
        },
        "ix": number
    },
    "o": {
        "a": number,
        "k": number,
        "ix": number
    },
    "x": {
        "a": number,
        "k": number,
        "ix": number
    },
    "nm": string
}

enum LoopType {
    pingpong = "pingpong",
    cycle = "cycle",
}

interface LoopData {
    loopInOrOut: 0 | 1 | 2,//0表示没有,1表示in前面循环，2表示out后续循环
    type: LoopType,
    duration: number,//为0表示全循环
}
/**
 * 内部使用,不对外导出,这里track的时间统统是帧时间,所以对于AnimationClip的update传的时间做换算
 * 注意lottie的透明度是不影响子级的
 */
class LottieBaseTrack extends HashObject implements IAnimationTrack {
    constructor(
        protected obj: Sprite,
        private type?: "r" | "o" | "s" | "p",
        private times?: KeyAniData[],
        private loop?: LoopData,
        private ip: number = 0,//偏移
        private xy: "x" | "y" = null
    ) {
        super();
        this._instanceType = "LottieBaseTrack";
    }
    /**
     * 子类重写
     * @param time 
     */
    setValue(time: number) {
        time -= this.ip;
        // if (!this.obj.visible) return
        setValue(this.obj, this.cacValue(time), this.type, this.xy)
        // var value = this.cacValue(time);
        // switch (this.type) {
        //     case "r":
        //         this.obj.rotation = value[0];
        //         break;
        //     case "o":
        //         this.obj.alpha = value[0] / 100;
        //         break;
        //     case "s":
        //         this.obj.scale.set(value[0] / 100, value[1] / 100);
        //         break;
        //     case "p":
        //         this.obj.position.set(value[0] - this.obj.anchorX, value[1] - this.obj.anchorY);
        //         break;
        // }
    }
    /**
     * 
     * @param time 
     * @returns 
     */
    protected cacValue(time: number) {
        //计算真实时间，因为部分又表达式的
        if (this.loop && this.loop.loopInOrOut) {
            time = (this.loop.loopInOrOut == 1 ? loopIn : loopOut)(this.loop.type, this.loop.duration, this.times, time)
        }
        if (time <= this.times[0].t) return this.times[0].s;
        if (time >= this.times[this.times.length - 1].t) return this.times[this.times.length - 1].s;
        //其他的计算补间了要，找前后两个索引
        var after = this.findIndex(time);
        var before = after - 1;
        var da = this.times[after];
        var db = this.times[before];
        var beforeValue = db.s, afterValue = da.s;
        var value: number[]
        //有路径的
        if (db.to) {
            db.fnc = db.fnc || getBezierEasing(db.o.x, db.o.y, db.i.x, db.i.y).get;
            value = buildBezierProp(
                beforeValue,
                afterValue,
                db.to,
                db.ti,
                db.t,
                da.t,
                time,
                db.fnc
            )
        }
        //有h的
        else if (db.h === 1) {
            value = beforeValue
        } else {
            db.fnc = db.fnc || generateFuncs(db.o, db.i, beforeValue.length);
            var newValue = [], perc: number, delta = da.t - db.t;
            for (var i = 0; i < beforeValue.length; i += 1) {
                if (time >= da.t) {
                    perc = 1;
                } else if (time < db.t) {
                    perc = 0;
                } else {
                    if (!db.fnc) {//线性
                        perc = (time - db.t) / delta;
                    }
                    else if (db.fnc.constructor === Array) {
                        perc = db.fnc[i]((time - db.t) / delta);
                    } else {
                        perc = db.fnc((time - db.t) / delta);
                    }
                }
                newValue[i] = beforeValue[i] + (afterValue[i] - beforeValue[i]) * perc;;
            }
            value = newValue
        }
        return value;
    }

    /**
     * 首尾已判断，得到后一位索引
     * @param times 
     * @param time 
     * @returns 
     */
    protected findIndex(time: number): number {
        var low = 0;
        var high = this.times.length - 2;
        if (high == 0) return 1;
        var current = high >>> 1;
        while (true) {
            if (this.times[current + 1].t <= time)
                low = current + 1;
            else
                high = current;
            if (low == high)
                return low + 1;
            current = (low + high) >>> 1;
        }
    }
    resetValue() {
        this.setValue(0)
    }
    destroy() {
        this.obj = null;
    }
}
/**
 * 部件的出场时间,就是个范围,
 * 每个对象都有,优先判断
 */
class LottieVisibleTrack extends HashObject implements IAnimationTrack {
    constructor(
        protected obj: Container,
        private inTime: number,
        private outTime: number,
        private startTime: number,
        private ip: number = 0,//偏移
    ) {
        super();
        this._instanceType = "LottieVisibleTrack";
    }
    /**
     * @param time 
     */
    setValue(time: number) {
        time -= this.ip;
        // this.obj.visible = this.inTime <= time && this.outTime >= time
        this.obj.visible = this.inTime <= time && this.outTime > time;//out不能等，否则重合
    }
    resetValue() {
        this.setValue(0);
    }
    destroy() {
        this.obj = null
    }
}
/**
 * 纯表达式的时间轴
 */
class LottieExpressionTrack extends HashObject implements IAnimationTrack {
    /**
     * 帧率，外部给吧
     */
    public frameRate: number;
    private node
    constructor(
        protected obj: Sprite,
        private type: "r" | "o" | "s" | "p",
        private expression: string,
        private origionValue: number | number[],
        private ip: number = 0,//偏移
    ) {
        super();
        this._instanceType = "LottieExpressionTrack";
        let e = this.expression.split("$bm_rt = ")[1]//取后面的//前面的var $bm_rt;\n$bm_rt = 没用
        this.node = parseExpression(e);
        // console.log(this.node)
    }
    /**
     * @param time 
     */
    setValue(time: number) {
        time -= this.ip;
        time /= this.frameRate;//注意修改FPS时需要遍历该类时间轴
        //   if(this.type=="o")  console.log(runExpressionNode(this.node, time))
        setValue(
            this.obj,
            runExpressionNode(this.node, time),
            this.type
        )
    }
    resetValue() {
        setValue(this.obj, this.origionValue, this.type);
    }
    destroy() {
        this.obj = null
    }
}

function loopOut(type: LoopType, duration: number, keyframes: KeyAniData[], currentFrame: number) {
    var lastKeyFrame = keyframes[keyframes.length - 1].t;
    if (currentFrame <= lastKeyFrame) return currentFrame;

    if (!duration || duration > keyframes.length - 1) {
        duration = keyframes.length - 1;
    }
    var firstKeyFrame = keyframes[keyframes.length - 1 - duration].t;
    var cycleDuration = lastKeyFrame - firstKeyFrame;

    // var i, len, ret;
    if (type === 'pingpong') {
        var iterations = Math.floor((currentFrame - firstKeyFrame) / cycleDuration);
        if (iterations % 2 !== 0) {
            return cycleDuration - (currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame;
        }
    }
    //其他的再说
    // else if(){

    // }
    return (currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame;
}


//给出时间就行,自行判断是否计算，根据
function loopIn(type: LoopType, duration: number, keyframes: KeyAniData[], currentFrame: number) {
    var firstKeyFrame = keyframes[0].t;
    if (currentFrame >= firstKeyFrame) return currentFrame;
    //0时取全部
    if (!duration || duration > keyframes.length - 1) {
        duration = keyframes.length - 1;
    }
    var lastKeyFrame = keyframes[duration].t;
    var cycleDuration = lastKeyFrame - firstKeyFrame;

    // var i, len, ret;
    if (type === LoopType.pingpong) {
        var iterations = Math.floor((firstKeyFrame - currentFrame) / cycleDuration);
        if (iterations % 2 === 0) {
            return (firstKeyFrame - currentFrame) % cycleDuration + firstKeyFrame;
        }
    }
    //其他的再说
    // else if(){

    // }
    return cycleDuration - (firstKeyFrame - currentFrame) % cycleDuration + firstKeyFrame;
}

function generateFuncs(outV, inV, len) {
    //无参数时直接返回null
    if (!outV || !inV) return null;
    var outX, outY, inX, inY
    if (outV.x.constructor === Array) {
        var fncts = [];
        for (var i = 0; i < len; i++) {
            outX = (typeof outV.x[i] === 'undefined') ? outV.x[0] : outV.x[i];
            outY = (typeof outV.y[i] === 'undefined') ? outV.y[0] : outV.y[i];
            inX = (typeof inV.x[i] === 'undefined') ? inV.x[0] : inV.x[i];
            inY = (typeof inV.y[i] === 'undefined') ? inV.y[0] : inV.y[i];
            fncts[i] = getBezierEasing(outX, outY, inX, inY).get;
        }
        return fncts;
    } else {
        outX = outV.x;
        outY = outV.y;
        inX = inV.x;
        inY = inV.y;
        return getBezierEasing(outX, outY, inX, inY).get;
    }
}

function getLoopData(x: string): LoopData {
    if (!x) return null;
    //取数字
    var rr = +x.replace(/[^0-9]/ig, "");
    var loopInOrOut: 0 | 1 | 2 = 0;
    if (x.indexOf("loopOut") >= 0) {
        loopInOrOut = 2
    } else if (x.indexOf("loopIn") >= 0) {
        loopInOrOut = 1
    }
    var type: LoopType;
    if (x.indexOf("pingpong") >= 0) {
        type = LoopType.pingpong
    } else if (x.indexOf("cycle") >= 0) {
        type = LoopType.cycle
    }
    return { loopInOrOut, type, duration: rr }
}

function setValue(obj: Sprite, value: number | number[], type: "r" | "o" | "s" | "p", xy?: "x" | "y") {
    //转下数字的为数组
    if (typeof value == "number") value = [value];
    switch (type) {
        case "r":
            obj.rotation = value[0];
            break;
        case "o":
            obj.alpha = value[0] / 100;
            break;
        case "s":
            if (xy) {
                obj[xy == "x" ? "scaleX" : "scaleY"] = value[0] / 100;
            } else {
                obj.scale.set(value[0] / 100, value[1] / 100);
            }
            break;
        case "p":
            if (xy) {
                obj[xy] = value[0] - (xy == "x" ? obj.anchorX : obj.anchorY);
            } else {
                obj.position.set(value[0] - obj.anchorX, value[1] - obj.anchorY);
            }
            break;
    }
}
/**
 * 
 */
interface ILottieLater {
    _mark: boolean;
    _isLottieLayer: boolean;
    _ind: number
    _parentId: number
}
/**
 * 一个子级完全平铺，但是子级却存在父级id记录的容器类，lottie会继承自他，还有合成的也继承自他
 */
class LottieContainer extends Container {
    /**
     * 是图片，但有额外属性
     */
    children: ((Sprite | LottieContainer) & ILottieLater)[];
    /**
     * 对所有的进行刷新，，根据cParent进行迭代刷新
     * 层级有问题，只能平铺，手动计算矩阵
     * 因为要平铺，所以记录cParent和ind  从1开始，也许只需要+1就行，还是用ind记录查找吧
     * 遍历找
     */
    updateTransform() {
        //super不行，到时查
        this.displayObjectUpdateTransform();
        this.children.forEach((c) => {
            this._recursivePostUpdateTransformAA(c);
        })
        this.children.forEach((c) => {
            c._mark = false;
        })
    }

    private findChildByInd(ind: number) {
        for (var i = 0; i < this.children.length; i++) {
            if (this.children[i]._ind === ind) return this.children[i]
        }
        return null
    }

    private _recursivePostUpdateTransformAA(c: (Sprite | LottieContainer) & ILottieLater) {
        if (c._isLottieLayer && c._parentId) {
            //ind从1开始，所以不用考虑0，且不应该存在 p没有的情况
            var p = this.findChildByInd(c._parentId)
            this._recursivePostUpdateTransformAA(p);
            if (!c._mark) {
                c._mark = true;
                // c.transform.updateWorldMatrix(p.transform);
                //用临时父级的方式，否则合成嵌套的子级不会更新
                const cacheParent = c.parent;
                c.parent = p;
                c.updateTransform();
                c.parent = cacheParent;
                //透明度单独计算，不跟p保持，这个到时要测试下，可以需要向上递归取透明度才是对的
                c._worldAlpha = c.alpha * c.parent._worldAlpha;
            }
        }
        //直接进行tans
        else if (!c._mark) {
            c.updateTransform();//alpha跟父级有关，这里透明度也一样
            c._mark = true
        }
    }
}


/**
 * 递归创建lottie时间轴，
 * @param root 
 * @param layers 
 * @param offset 
 * @param textures 
 * @param compositions 
 * @param tracks 
 * @returns 
 */
function createLottieTracks(
    root: LottieContainer,
    layers: LayerData[],
    offset: number,
    textures: { [key: string]: Texture },
    compositions?: { [key: string]: LayerData[] },
    tracks: (LottieVisibleTrack | LottieBaseTrack | LottieExpressionTrack)[] = [],
) {
    for (var i = layers.length - 1; i >= 0; i--) {
        let layer = layers[i];
        const { ty, refId, ks, parent, ind, ip, op, st, hasMask, masksProperties } = layer;
        let c: (Sprite | LottieContainer) & ILottieLater;
        //如果存在组件嵌套
        if (ty == 0 && compositions) {
            c = root.addChild(new LottieContainer()) as LottieContainer & ILottieLater;
            //递归创建，宽高和名字暂时不需要，
            createLottieTracks(
                c,
                compositions[refId],
                offset + ip,
                textures,
                compositions,
                tracks
            );
        }
        //图片图层，不能判断2.因为有部分是Container
        else {
            c = root.addChild(new Sprite(
                refId ?
                    textures && textures[refId] ||
                    TextureCache[refId] ||
                    TextureCache[refId + ".png"] : null
            ) as Sprite & ILottieLater);
        }
        //遮罩数据，遮罩矩阵和c一致，所以加在c里面
        if (hasMask && masksProperties && masksProperties.length) {
            c.mask = c.addChild(createLottieMask(masksProperties, layer.w, layer.h));
        }
        //标记下
        c._isLottieLayer = true;
        //记录一下数据，查找父级的时候有用
        c._ind = ind, c._parentId = parent;
        //处理下锚点，基本不会有锚点动画
        var ad = typeof ks.a.k[0] == "number" ? ks.a.k : ks.a.k[0].s;
        c.anchor.set(ad[0], ad[1]);
        //加显示隐藏的
        tracks.push(new LottieVisibleTrack(c, ip, op, st, offset));
        ["o", "r", "p", "s"].forEach((type: "o" | "r" | "p" | "s") => {
            let k: KeyAniData[] | number[] | number = ks[type].k;
            let expression: string = ks[type].x;
            //考虑x和y分开的情况，不会有加减
            if (!k) {
                //以防万一，判断下r和o
                if (!ks[type].s || type == "r" || type == "o") return;
                //这时候x就是维度了，不再是表达式
                ["x", "y"].forEach((xy: "x" | "y") => {
                    let kk = ks[type][xy].k;
                    let eexpression: string = ks[type][xy].x;
                    if (kk.length) {
                        tracks.push(
                            new LottieBaseTrack(c as Sprite, type, kk, getLoopData(eexpression), offset, xy)
                        )
                    } else {
                        setValue(c as Sprite, kk, type, xy);
                        //不管加减表达式
                    }
                })
                return;
            }
            //@ts-ignore
            if (((type == "o" || type == "r") && k.length) ||//透明度和旋转，k有长度就是关键帧，
                ((type == "s" || type == "p") && typeof k[0] != "number")) {//透明度和旋转，k有长度就是关键帧，位置和缩放得k的元素里不是number
                tracks.push(
                    new LottieBaseTrack(c as Sprite, type, k as KeyAniData[], getLoopData(expression), offset)
                )
            } else {
                //没有关键帧的设置下初始值
                setValue(c as Sprite, k as number, type);
                //表达式判断，但是不管loop那些表达式，因为已经进BaseTrack了的，也不管位移和缩放的先
                if (expression && expression.indexOf("loop") == -1 && type !== "s" && type !== "p") {
                    tracks.push(
                        new LottieExpressionTrack(c as Sprite, type, expression, k as number, offset)
                    )
                }
            }
        })
    }
    return tracks;
}


/**
 * 生成lottie的遮罩矢量
 * @param masksProperties 
 * @param w 
 * @param h 
 * @returns 
 */
function createLottieMask(masksProperties: ILottieMaskData[], w: number, h: number): Shape {
    var mask = new Shape();
    mask.beginFill();
    masksProperties.forEach((m) => {
        if (m.mode !== 'n') {
            if (m.inv) {
                mask.moveTo(0, 0);
                mask.lineTo(w, 0);
                mask.lineTo(w, h);
                mask.lineTo(0, h);
                mask.lineTo(0, 0);
            }
            var data = m.pt.k;
            mask.moveTo(data.v[0][0], data.v[0][1]);
            var j, jLen = data.v.length;
            for (j = 1; j < jLen; j++) {
                mask.bezierCurveTo(
                    data.o[j - 1][0] + data.v[j - 1][0],
                    data.o[j - 1][1] + data.v[j - 1][1],
                    data.i[j][0] + data.v[j][0],
                    data.i[j][1] + data.v[j][1],
                    data.v[j][0],
                    data.v[j][1]
                );
            }
            mask.bezierCurveTo(
                data.o[j - 1][0] + data.v[j - 1][0],
                data.o[j - 1][1] + data.v[j - 1][1],
                data.i[0][0] + data.v[0][0],
                data.i[0][1] + data.v[0][1],
                data.v[0][0],
                data.v[0][1]
            );
            if (data.c) {
                //TODO
            }
        }
    })
    mask.endFill();
    return mask
}

//表达式的各种范式匹配
//
//30+time*10   "var $bm_rt;\n$bm_rt = $bm_sum(30, $bm_mul(time, 10));"
//time*30    "var $bm_rt;\n$bm_rt = $bm_mul(time, 30);"
//           "var $bm_rt;\n$bm_rt = $bm_sum([\n    time * 10,\n    time * 5\n], value);"
function $bm_sum(a, b) {
    // var tOfA = typeof a;
    // var tOfB = typeof b;
    // if(tOfA === 'string' || tOfB === 'string'){
    //     return a + b;
    // }
    // if(isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
    //     return a + b;
    // }
    // if($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)){
    //     a = a.slice(0);
    //     a[0] = a[0] + b;
    //     return a;
    // }
    // if(isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)){
    //     b = b.slice(0);
    //     b[0] = a + b[0];
    //     return b;
    // }
    // if($bm_isInstanceOfArray(a) && $bm_isInstanceOfArray(b)){

    //     var i = 0, lenA = a.length, lenB = b.length;
    //     var retArr = [];
    //     while(i<lenA || i < lenB){
    //         if((typeof a[i] === 'number' || a[i] instanceof Number) && (typeof b[i] === 'number' || b[i] instanceof Number)){
    //             retArr[i] = a[i] + b[i];
    //         }else{
    //             retArr[i] = b[i] === undefined ? a[i] : a[i] || b[i];
    //         }
    //         i += 1;
    //     }
    //     return retArr;
    // }
    // return 0;
}
function $bm_sub(a, b) {
    // var tOfA = typeof a;
    // var tOfB = typeof b;
    // if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
    //     if (tOfA === 'string') {
    //         a = parseInt(a);
    //     }
    //     if (tOfB === 'string') {
    //         b = parseInt(b);
    //     }
    //     return a - b;
    // }
    // if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
    //     a = a.slice(0);
    //     a[0] = a[0] - b;
    //     return a;
    // }
    // if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
    //     b = b.slice(0);
    //     b[0] = a - b[0];
    //     return b;
    // }
    // if ($bm_isInstanceOfArray(a) && $bm_isInstanceOfArray(b)) {
    //     var i = 0, lenA = a.length, lenB = b.length;
    //     var retArr = [];
    //     while (i < lenA || i < lenB) {
    //         if ((typeof a[i] === 'number' || a[i] instanceof Number) && (typeof b[i] === 'number' || b[i] instanceof Number)) {
    //             retArr[i] = a[i] - b[i];
    //         } else {
    //             retArr[i] = b[i] === undefined ? a[i] : a[i] || b[i];
    //         }
    //         i += 1;
    //     }
    //     return retArr;
    // }
    // return 0;
}
function $bm_mul(a, b) {
    // var tOfA = typeof a;
    // var tOfB = typeof b;
    // var arr;
    // if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
    //     return a * b;
    // }

    // var i, len;
    // if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
    //     len = a.length;
    //     arr = createTypedArray('float32', len);
    //     for (i = 0; i < len; i += 1) {
    //         arr[i] = a[i] * b;
    //     }
    //     return arr;
    // }
    // if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
    //     len = b.length;
    //     arr = createTypedArray('float32', len);
    //     for (i = 0; i < len; i += 1) {
    //         arr[i] = a * b[i];
    //     }
    //     return arr;
    // }
    // return 0;
}
function $bm_div(a, b) {
    // var tOfA = typeof a;
    // var tOfB = typeof b;
    // var arr;
    // if (isNumerable(tOfA, a) && isNumerable(tOfB, b)) {
    //     return a / b;
    // }
    // var i, len;
    // if ($bm_isInstanceOfArray(a) && isNumerable(tOfB, b)) {
    //     len = a.length;
    //     arr = createTypedArray('float32', len);
    //     for (i = 0; i < len; i += 1) {
    //         arr[i] = a[i] / b;
    //     }
    //     return arr;
    // }
    // if (isNumerable(tOfA, a) && $bm_isInstanceOfArray(b)) {
    //     len = b.length;
    //     arr = createTypedArray('float32', len);
    //     for (i = 0; i < len; i += 1) {
    //         arr[i] = a / b[i];
    //     }
    //     return arr;
    // }
    // return 0;
}
function $bm_isInstanceOfArray(arr) {
    return arr.constructor === Array || arr.constructor === Float32Array;
}
function isNumerable(tOfV, v) {
    return tOfV === 'number' || tOfV === 'boolean' || tOfV === 'string' || v instanceof Number;
}
//解析表达式，暂时只有一维数据的加减乘除
function parseExpression(source: string) {
    let pos = 0
    let word = ''
    let stack = []
    let node
    let char
    while (char = source[pos++]) {
        switch (char) {
            case '(':
                node = { op: word, args: [] }
                word = ''
                stack.push(node)
                break;
            case ')':
                push()
                node = stack[stack.length - 1]
                stack.pop()
                break;
            case ',':
                push()
                break;
            case ' ':
                break;
            default:
                word += char
        }
    }
    return node

    function push() {
        let val = word
        // if (!isNaN(word)) {
        //     val = parseFloat(word)
        // }
        stack[stack.length - 1].args.push(word ? val : node)
        word = ''
    }
}
//四种函数的映射
const expressionFuns = {
    $bm_sum: (a, b) => a + b,
    $bm_sub: (a, b) => a - b,
    $bm_mul: (a, b) => a * b,
    $bm_div: (a, b) => a / b
};
function runExpressionNode(node, time: number) {
    return opp(node.op, node.args[0], node.args[1], time);
    function opp(op: string, a, b, time) {
        if (a.op) {
            a = opp(a.op, a.args[0], a.args[1], time);
        } else if (a == "time") {
            a = time
        }
        if (b.op) {
            b = opp(b.op, b.args[0], b.args[1], time);
        } else if (b == "time") {
            b = time;
        }
        return expressionFuns[op](+a, +b)
    }
}

