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

import { Curve, readCurve, MixBlend, MixDirection } from "./utils";
import { BaseTrack } from "./BaseTrack";
import { sign } from "../../2d/utils";

export interface IBoneAniData {
    time: number,
    angle: number,//只有rotate类型有
    x: number,
    y: number,
    curve: "stepped" | [number, number, number, number]
}

export enum SpineBoneAniType {//这是按照
    //bone的
    rotate = "rotate",
    translate = "translate",
    scale = "scale",
    shear = "shear",

}

export class SpineBoneAniTrack extends BaseTrack {
    curve: Curve;
    resetValue() {
        this.bone.setToSetupPose();
    }
    constructor(
        /**
         * 需要改变属性的节点对象
         */
        public bone: Bone,
        /**
         * 动画类型
         */
        public animationType: SpineBoneAniType,
        /**
         * 记录所有时间的数组，按顺序递增，没有的按最后一个时间的数据
         */
        public times: IBoneAniData[],
    ) {
        super();
        this._instanceType = "SpineBoneAniTrack";
        times.forEach((t) => {
            t.time = t.time || 0;
            switch (animationType) {
                case SpineBoneAniType.rotate:
                    t.angle = t.angle || 0;
                    break;
                case SpineBoneAniType.scale:
                    t.x = t.x === undefined ? 1 : t.x;
                    t.y = t.y === undefined ? 1 : t.y;
                    break;
                case SpineBoneAniType.translate:
                case SpineBoneAniType.shear:
                    t.x = t.x || 0;
                    t.y = t.y || 0;
                    break;
            }


        })
        //记录缓动
        this.curve = new Curve(times.length);
        var frameIndex = 0;
        for (var i = 0; i < times.length; i++) {
            var map = times[i];
            readCurve(map, this.curve, frameIndex)
            frameIndex++;
        }
    }
    /**
     * 传入时间设置对应的属性值，time大于所有动画总时间，外部处理   blend到时考虑要怎么传
     * @param time 
     */
    // setValue1(time: number, mix: number = 1, blend: MixBlend = MixBlend.replace) {
    //     var value: number[] = this.getValue(time);//返回的是数组
    //     switch (this.animationType) {
    //         case SpineBoneAniType.translate:
    //             if (time < this.times[0].time) {
    //                 switch (blend) {
    //                     case MixBlend.setup:
    //                         this.bone.x = this.bone.data.x;
    //                         this.bone.y = this.bone.data.y;
    //                         break;
    //                     case MixBlend.first:
    //                         this.bone.x += (this.bone.data.x - this.bone.x) //* mix;
    //                         this.bone.y += (this.bone.data.y - this.bone.y) //* mix;
    //                         break;
    //                 }
    //             } else {
    //                 var x = value[0];
    //                 var y = value[1];
    //                 switch (blend) {
    //                     case MixBlend.setup:
    //                         this.bone.x = this.bone.data.x + x //* alpha;
    //                         this.bone.y = this.bone.data.y + y //* alpha;
    //                         break;
    //                     case MixBlend.first:
    //                     case MixBlend.replace:
    //                         this.bone.x += (this.bone.data.x + x - this.bone.x) //* alpha;
    //                         this.bone.y += (this.bone.data.y + y - this.bone.y) //* alpha;
    //                         break;
    //                     case MixBlend.add:
    //                         this.bone.x += x //* alpha;
    //                         this.bone.y += y //* alpha;
    //                 }
    //             }

    //             // this.node.position.set(value[0], value[1]);
    //             break;
    //         case SpineBoneAniType.scale:

    //             this.bone.scale.set(value[0], value[1]);
    //             break;
    //         case SpineBoneAniType.shear:

    //             this.bone.skewX = value[0];
    //             this.bone.skewY = value[1];
    //             break;
    //         case SpineBoneAniType.rotate:
    //             if (time < this.times[0].time) {
    //                 switch (blend) {
    //                     case MixBlend.setup:
    //                         this.bone.rotation = this.bone.data.rotation;
    //                         return;
    //                     case MixBlend.first:
    //                         var r_1 = this.bone.data.rotation - this.bone.rotation;
    //                         this.bone.rotation += (r_1 - (16384 - ((16384.499999999996 - r_1 / 360) | 0)) * 360) * 1//alpha;
    //                 }
    //             }
    //             else if (time >= this.times[this.times.length - 1].time) {
    //                 var r_2 = value[0]
    //                 switch (blend) {
    //                     case MixBlend.setup:
    //                         this.node.rotation = this.node.data.rotation + value[0] //* alpha;
    //                         break;
    //                     case MixBlend.first:
    //                     case MixBlend.replace:
    //                         r_2 += this.node.data.rotation - this.node.rotation;
    //                         this.node.rotation -= (16384 - ((16384.499999999996 - r_2 / 360) | 0)) * 360;
    //                     case MixBlend.add:
    //                         this.node.rotation += r_2 //* alpha;
    //                 }
    //             } else {
    //                 var r = value[0];
    //                 switch (blend) {
    //                     case MixBlend.setup:
    //                         this.bone.rotation = this.bone.data.rotation + (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) //* alpha;
    //                         break;
    //                     case MixBlend.first:
    //                     case MixBlend.replace:
    //                         r += this.bone.data.rotation - this.bone.rotation;
    //                     case MixBlend.add:
    //                         this.bone.rotation += (r - (16384 - ((16384.499999999996 - r / 360) | 0)) * 360) //* alpha;
    //                 }
    //             }
    //             break;
    //     }
    // }

    setValue(time: number, alpha: number = 1, blend: MixBlend = MixBlend.replace, direction: MixDirection = MixDirection.in) {
        switch (this.animationType) {
            case SpineBoneAniType.translate:
                this.setPosition(time, alpha, blend)
                break;
            case SpineBoneAniType.scale:
                this.setScale(time, alpha, blend, direction)
                break;
            case SpineBoneAniType.shear:
                this.setShear(time, alpha, blend)
                break;
            case SpineBoneAniType.rotate:
                this.setRotation(time, alpha, blend)
                break;
        }
    }

    private setRotation(time: number, alpha: number = 1, blend: MixBlend = MixBlend.replace) {
        var frames = this.times;
        var bone = this.bone;
        if (time < frames[0].time) {
            switch (blend) {
                case MixBlend.setup:
                    bone.rotation = bone.data.rotation;
                    return;
                case MixBlend.first:
                    var r_1 = bone.data.rotation - bone.rotation;
                    bone.rotation += (r_1 - transRotation(r_1)) * alpha;
            }
            return;
        }
        if (time >= frames[frames.length - 1].time) {
            var r_2 = frames[frames.length - 1].angle;
            switch (blend) {
                case MixBlend.setup:
                    bone.rotation = bone.data.rotation + r_2 * alpha;
                    break;
                case MixBlend.first:
                case MixBlend.replace:
                    r_2 += bone.data.rotation - bone.rotation;
                    r_2 -= transRotation(r_2);
                case MixBlend.add:
                    bone.rotation += r_2 * alpha;
            }
            return;
        }
        var frame = this.findPreIndex(time);
        var prevRotation = frames[frame - 1].angle;
        var frameTime = frames[frame].time;
        var percent = this.curve.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1].time - frameTime));
        var r = frames[frame].angle - prevRotation;
        r = prevRotation + (r - transRotation(r)) * percent;
        switch (blend) {
            case MixBlend.setup:
                bone.rotation = bone.data.rotation + (r - transRotation(r)) * alpha;
                break;
            case MixBlend.first:
            case MixBlend.replace:
                r += bone.data.rotation - bone.rotation;
            case MixBlend.add:
                bone.rotation += (r - transRotation(r)) * alpha;
        }
    }
    private setPosition(time: number, alpha: number = 1, blend: MixBlend = MixBlend.replace) {
        var frames = this.times;
        var bone = this.bone;
        if (time < frames[0].time) {
            switch (blend) {
                case MixBlend.setup:
                    bone.x = bone.data.x;
                    bone.y = bone.data.y;
                    return;
                case MixBlend.first:
                    bone.x += (bone.data.x - bone.x) * alpha;
                    bone.y += (bone.data.y - bone.y) * alpha;
            }
            return;
        }
        var x = 0, y = 0;
        if (time >= frames[frames.length - 1].time) {
            x = frames[frames.length - 1].x;
            y = frames[frames.length - 1].y;
        }
        else {
            var frame = this.findPreIndex(time);
            x = frames[frame - 1].x;
            y = frames[frame - 1].y;
            var frameTime = frames[frame].time;
            var percent = this.curve.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1].time - frameTime));
            x += (frames[frame].x - x) * percent;
            y += (frames[frame].y - y) * percent;
        }
        switch (blend) {
            case MixBlend.setup:
                bone.x = bone.data.x + x * alpha;
                bone.y = bone.data.y + y * alpha;
                break;
            case MixBlend.first:
            case MixBlend.replace:
                bone.x += (bone.data.x + x - bone.x) * alpha;
                bone.y += (bone.data.y + y - bone.y) * alpha;
                break;
            case MixBlend.add:
                bone.x += x * alpha;
                bone.y += y * alpha;
        }
    }
    private setScale(time: number, alpha: number = 1, blend: MixBlend = MixBlend.replace, direction: MixDirection = MixDirection.in) {
        var frames = this.times;
        var bone = this.bone;
        if (time < frames[0].time) {
            switch (blend) {
                case MixBlend.setup:
                    bone.scaleX = bone.data.scaleX;
                    bone.scaleY = bone.data.scaleY;
                    return;
                case MixBlend.first:
                    bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
                    bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
            }
            return;
        }
        var x = 0, y = 0;
        if (time >= frames[frames.length - 1].time) {
            x = frames[frames.length - 1].x * bone.data.scaleX;
            y = frames[frames.length - 1].y * bone.data.scaleY;
        }
        else {
            var frame = this.findPreIndex(time);
            x = frames[frame - 1].x;
            y = frames[frame - 1].y;
            var frameTime = frames[frame].time;
            var percent = this.curve.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1].time - frameTime));
            x = (x + (frames[frame].x - x) * percent) * bone.data.scaleX;
            y = (y + (frames[frame].y - y) * percent) * bone.data.scaleY;
        }
        if (alpha == 1) {
            if (blend == MixBlend.add) {
                bone.scaleX += x - bone.data.scaleX;
                bone.scaleY += y - bone.data.scaleY;
            }
            else {
                bone.scaleX = x;
                bone.scaleY = y;
            }
        }
        else {
            var bx = 0, by = 0;
            if (direction == MixDirection.out) {
                switch (blend) {
                    case MixBlend.setup:
                        bx = bone.data.scaleX;
                        by = bone.data.scaleY;
                        bone.scaleX = bx + (Math.abs(x) * sign(bx) - bx) * alpha;
                        bone.scaleY = by + (Math.abs(y) * sign(by) - by) * alpha;
                        break;
                    case MixBlend.first:
                    case MixBlend.replace:
                        bx = bone.scaleX;
                        by = bone.scaleY;
                        bone.scaleX = bx + (Math.abs(x) * sign(bx) - bx) * alpha;
                        bone.scaleY = by + (Math.abs(y) * sign(by) - by) * alpha;
                        break;
                    case MixBlend.add:
                        bx = bone.scaleX;
                        by = bone.scaleY;
                        bone.scaleX = bx + (Math.abs(x) * sign(bx) - bone.data.scaleX) * alpha;
                        bone.scaleY = by + (Math.abs(y) * sign(by) - bone.data.scaleY) * alpha;
                }
            }
            else {
                switch (blend) {
                    case MixBlend.setup:
                        bx = Math.abs(bone.data.scaleX) * sign(x);
                        by = Math.abs(bone.data.scaleY) * sign(y);
                        bone.scaleX = bx + (x - bx) * alpha;
                        bone.scaleY = by + (y - by) * alpha;
                        break;
                    case MixBlend.first:
                    case MixBlend.replace:
                        bx = Math.abs(bone.scaleX) * sign(x);
                        by = Math.abs(bone.scaleY) * sign(y);
                        bone.scaleX = bx + (x - bx) * alpha;
                        bone.scaleY = by + (y - by) * alpha;
                        break;
                    case MixBlend.add:
                        bx = sign(x);
                        by = sign(y);
                        bone.scaleX = Math.abs(bone.scaleX) * bx + (x - Math.abs(bone.data.scaleX) * bx) * alpha;
                        bone.scaleY = Math.abs(bone.scaleY) * by + (y - Math.abs(bone.data.scaleY) * by) * alpha;
                }
            }
        }
    }
    private setShear(time: number, alpha: number = 1, blend: MixBlend = MixBlend.replace) {
        var frames = this.times;
        var bone = this.bone;
        if (time < frames[0].time) {
            switch (blend) {
                case MixBlend.setup:
                    bone.skewX = bone.data.shearX;
                    bone.skewY = bone.data.shearY;
                    return;
                case MixBlend.first:
                    bone.skewX += (bone.data.shearX - bone.skewX) * alpha;
                    bone.skewY += (bone.data.shearY - bone.skewY) * alpha;
            }
            return;
        }
        var x = 0, y = 0;
        if (time >= frames[frames.length - 1].time) {
            x = frames[frames.length - 1].x;
            y = frames[frames.length - 1].y;
        }
        else {
            var frame = this.findPreIndex(time);
            x = frames[frame - 1].y;
            y = frames[frame - 1].y;
            var frameTime = frames[frame].time;
            var percent = this.curve.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1].time - frameTime));
            x = x + (frames[frame].x - x) * percent;
            y = y + (frames[frame].y - y) * percent;
        }
        switch (blend) {
            case MixBlend.setup:
                bone.skewX = bone.data.shearX + x * alpha;
                bone.skewY = bone.data.shearY + y * alpha;
                break;
            case MixBlend.first:
            case MixBlend.replace:
                bone.skewX += (bone.data.shearX + x - bone.skewX) * alpha;
                bone.skewY += (bone.data.shearY + y - bone.skewY) * alpha;
                break;
            case MixBlend.add:
                bone.skewX += x * alpha;
                bone.skewY += y * alpha;
        }
    }

    /**
     * 外部已经剔除了第一帧和最后一帧。到时试试二元法，估计更快点
     * @param time 
     */
    private findPreIndex(time: number) {
        var lastIndex = 0;
        for (var i = 0; i < this.times.length; i++) {
            if (this.times[i].time > time) {
                lastIndex = i;
                break
            }
        }
        return lastIndex;
    }


    destroy() {
        this.bone = null;
        this.times = null;
    }
}

function transRotation(r: number) {
    return (16384 - ((16384.499999999996 - r / 360) | 0)) * 360
}
