import { HashObject } from "../../2d/HashObject";
import { Object3D } from "../Object3D";
import { Mesh3D } from "../Mesh3D";
import { clamp } from "../../2d/utils";
import { Quaternion } from "../math/Quaternion";
import { IAnimationTrack } from "../../2d/AnimationClip";
/**
 * 动画类型
 */
export enum AnimationType3D {
    /**
     * 旋转
     */
    quaternion = "quaternion",
    /**
     * 缩放
     */
    scale = "scale",
    /**
     * 顶点权重
     */
    morphTargetInfluences = 'morphTargetInfluences',
    /**
     * 位移
     */
    position = "position"
}
/**
 * 记录动画的轨迹，实时计算，还是用每帧的，暂时按时间间隔来搞，实时计算吧
 * 统统用线性的吧，貌似时间都打过点
 */
export class AnimationTrack3D extends HashObject implements IAnimationTrack {
    constructor(
        /**
         * 需要改变属性的节点对象
         */
        public node: Object3D | Mesh3D,
        /**
         * 动画类型
         */
        public animationType: AnimationType3D,
        /**
         * 记录所有时间的数组，按顺序递增，没有的按最后一个时间的数据
         */
        public times: Float32Array | number[],
        /**
         * 上述时间要对应的数值，values.length/times.length就是步长
         */
        public values: Float32Array | number[]
    ) {
        super()
        this._instanceType = "AnimationTrack";
        //赋值函数，恢复录入时的初始状态
        // this.resetValue=()=>{
        switch (this.animationType) {
            case AnimationType3D.position:
            case AnimationType3D.scale:
            case AnimationType3D.quaternion:
                let value = this.node[this.animationType].clone().toArray();
                this.resetValue = () => {
                    this.node[this.animationType].fromArray(value);
                }
                break;
            case AnimationType3D.morphTargetInfluences:
                if (this.node.instanceType == "Mesh3D" || this.node.instanceType == "SkinnedMesh3D") {
                    let value = this.node["morphTargetInfluences"] ? this.node["morphTargetInfluences"].slice() : [];
                    this.resetValue = () => {
                        var arr = this.node["morphTargetInfluences"];
                        for (var i = 0; i < arr.length; i++) {//这里用morphTargetInfluences本身的长度
                            arr[i] = value[i] || 0;
                        }
                    }
                }
                break;
        }
        //给个空的
        if (!this.resetValue) this.resetValue = () => { }
    }
    //恢复初始状态并停止
    resetValue: () => void
    /**
     * 传入时间设置对应的属性值，time大于所有动画总时间，外部处理
     * @param time 
     */
    setValue(time: number) {
        var value: Float32Array | number[] = this.getValue(time);
        switch (this.animationType) {
            case AnimationType3D.position:
            case AnimationType3D.scale:
            case AnimationType3D.quaternion:
                this.node[this.animationType].fromArray(value);
                break;
            case AnimationType3D.morphTargetInfluences:
                if (this.node.instanceType == "Mesh3D" || this.node.instanceType == "SkinnedMesh3D") {
                    var arr = this.node["morphTargetInfluences"]
                    for (var i = 0; i < arr.length; i++) {
                        arr[i] = value[i] || 0;
                    }
                }
                break;
        }
    }

    private getValue(time: number) {
        var size = this.getValueSize();
        //为0直接用第一帧数据
        if (time <= 0) return this.values.slice(0, size);
        //如果超过最后时间，给最后一帧数据
        if (time >= this.times[this.times.length - 1]) return this.values.slice(-size);

        //取最近的两个时间索引，
        var t1 = this.findPreIndex(time);
        var t0 = t1 - 1;
        if (t0 < 0) return this.values.slice(0, size);
        //计算比例插值
        var scale = (time - this.times[t0]) / (this.times[t1] - this.times[t0]);
        //获取数组，
        var v0 = this.values.slice(t0 * size, t0 * size + size);
        var v1 = this.values.slice(t1 * size, t1 * size + size);
        // time = clamp(time, 0, this.times[this.times.length - 1]);
        //暂时都是线性的，到时再说
        if (this.animationType == AnimationType3D.quaternion) {
            return quaternionLinearInterpolate(scale, v0, v1);
        }
        return linearInterpolate(scale, v0, v1);
    }
    //每项尺寸
    private getValueSize() {
        return this.values.length / this.times.length;
    }

    /**
     * 外部已经剔除了第一帧和最后一帧
     * @param time 
     */
    private findPreIndex(time: number) {
        var lastIndex = 0;
        for (var i = 0; i < this.times.length; i++) {
            if (this.times[i] > time) {
                lastIndex = i;
                break
            }
        }
        return lastIndex;
    }


    destroy() {
        this.node = null;
        this.values = null;
        this.times = null;
    }
}

//线性插值，除了四元数
function linearInterpolate(s: number, v0: Float32Array | number[], v1: Float32Array | number[]) {
    var result = [];
    // values = this.sampleValues,
    // stride = this.valueSize,
    // offset1 = i1 * stride,
    // offset0 = offset1 - stride,

    // weight1 = (t - t0) / (t1 - t0),
    // weight0 = 1 - weight1;
    for (var i = 0; i !== v0.length; ++i) {
        // result[i] =
        //     values[offset0 + i] * weight0 +
        //     values[offset1 + i] * weight1;
        result[i] = v0[i] * (1 - s) + v1[i] * s;
    }
    return result;
}

function quaternionLinearInterpolate(s: number, v0: Float32Array | number[], v1: Float32Array | number[]) {
    var result = [];
    Quaternion.slerpFlat(result, 0, v0, 0, v1, 0, s);
    return result;
}