import { clamp } from "../../2d/utils";

/**
 * 为了方便提前计算贝塞尔的补间
 */
export class Curve {
    static LINEAR = 0;
    static STEPPED = 1;
    static BEZIER = 2;
    static BEZIER_SIZE = 10 * 2 - 1;

    private curves: Float32Array

    constructor(frameCount: number) {
        this.curves = new Float32Array((frameCount - 1) * Curve.BEZIER_SIZE);
    }
    /** The number of key frames for this timeline. */
    getFrameCount() {
        return this.curves.length / Curve.BEZIER_SIZE + 1;
    }

    /** Sets the specified key frame to linear interpolation. */
    setLinear(frameIndex: number) {
        this.curves[frameIndex * Curve.BEZIER_SIZE] = Curve.LINEAR;
    }

    /** Sets the specified key frame to stepped interpolation. */
    setStepped(frameIndex: number) {
        this.curves[frameIndex * Curve.BEZIER_SIZE] = Curve.STEPPED;
    }

    /** Returns the interpolation type for the specified key frame.
     * @returns Linear is 0, stepped is 1, Bezier is 2. */
    getCurveType(frameIndex: number): number {
        let index = frameIndex * Curve.BEZIER_SIZE;
        if (index == this.curves.length) return Curve.LINEAR;
        let type = this.curves[index];
        if (type == Curve.LINEAR) return Curve.LINEAR;
        if (type == Curve.STEPPED) return Curve.STEPPED;
        return Curve.BEZIER;
    }

    /** Sets the specified key frame to Bezier interpolation. `cx1` and `cx2` are from 0 to 1,
     * representing the percent of time between the two key frames. `cy1` and `cy2` are the percent of the
     * difference between the key frame's values. */
    setCurve(frameIndex: number, cx1: number, cy1: number, cx2: number, cy2: number) {
        let tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03;
        let dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006;
        let ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
        let dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667;

        let i = frameIndex * Curve.BEZIER_SIZE;
        let curves = this.curves;
        curves[i++] = Curve.BEZIER;

        let x = dfx, y = dfy;
        for (let n = i + Curve.BEZIER_SIZE - 1; i < n; i += 2) {
            curves[i] = x;
            curves[i + 1] = y;
            dfx += ddfx;
            dfy += ddfy;
            ddfx += dddfx;
            ddfy += dddfy;
            x += dfx;
            y += dfy;
        }
    }

    /** Returns the interpolated percentage for the specified key frame and linear percentage. */
    getCurvePercent(frameIndex: number, percent: number) {
        percent = clamp(percent, 0, 1);
        let curves = this.curves;
        let i = frameIndex * Curve.BEZIER_SIZE;
        let type = curves[i];
        if (type == Curve.LINEAR) return percent;
        if (type == Curve.STEPPED) return 0;
        i++;
        let x = 0;
        for (let start = i, n = i + Curve.BEZIER_SIZE - 1; i < n; i += 2) {
            x = curves[i];
            if (x >= percent) {
                let prevX: number, prevY: number;
                if (i == start) {
                    prevX = 0;
                    prevY = 0;
                } else {
                    prevX = curves[i - 2];
                    prevY = curves[i - 1];
                }
                return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
            }
        }
        let y = curves[i - 1];
        return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
    }
}

/**
 * 根据map的curve属性，给curve设置缓动方法
 * @param map 
 * @param curve 
 * @param frameIndex 
 */
export function readCurve(map: any, curve: Curve, frameIndex: number) {
    if (!map.hasOwnProperty("curve")) return;
    if (map.curve === "stepped")
        curve.setStepped(frameIndex);
    else if (Object.prototype.toString.call(map.curve) === '[object Array]') {
        var curveArr = map.curve;
        curve.setCurve(frameIndex, curveArr[0], curveArr[1], curveArr[2], curveArr[3]);
    } else {//兼容下
        curve.setCurve(frameIndex, map.curve, map.c2 || 0, map.c3 || 1, map.c4 || 1);
    }
}


export enum MixBlend {
    setup = 0,
    first,
    replace,
    add
}
//用于混合，暂时都是in
export enum MixDirection {
    in = 0,
    out
}
/**
 * 这种数组查找会快点，到时也可以缝一下，找最接近的下一个的索引
 * @param values 
 * @param target 
 */
export function binarySearch(values: Float32Array | number[], target: number, step: number = 1) {
    var low = 0;
    var high = values.length / step - 2;
    if (high == 0)
        return step;
    var current = high >>> 1;
    while (true) {
        if (values[(current + 1) * step] <= target)
            low = current + 1;
        else
            high = current;
        if (low == high)
            return (low + 1) * step;
        current = (low + high) >>> 1;
    }
};


export function arrayCopy(source, sourceStart, dest, destStart, numElements) {
    for (var i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) {
        dest[j] = source[i];
    }
};

export function setArraySize(array, size, value = 0) {
    var oldSize = array.length;
    if (oldSize == size)
        return array;
    array.length = size;
    if (oldSize < size) {
        for (var i = oldSize; i < size; i++)
            array[i] = value;
    }
    return array;
};

export function newArray(size: number, defaultValue: any) {
    var array = new Array(size);
    for (var i = 0; i < size; i++)
        array[i] = defaultValue;
    return array;
}