import { KeyCode, math, Quat, Vec3 } from "cc";

export function clampLength(vec: Vec3, maxLength: number) {
    if (vec.lengthSqr() > maxLength * maxLength) {
        const clampVec = new Vec3();
        Vec3.normalize(clampVec, vec);
        clampVec.multiplyScalar(maxLength);
        return clampVec;
    }

    return vec;
}

export function vec3FromAngle(degree: number, length: number) {
    const radian = math.toRadian(degree);
    return new Vec3(length * Math.cos(radian), length * Math.sin(radian), 0);
}

export function calcPunchData(start: Vec3, direction: Vec3, duration: number, vibrato: number, elasticity: number) {
    math.clamp01(elasticity);
    let strength = direction.length();
    let toIterations = Math.round(vibrato * duration);
    if (toIterations < 2) {
        toIterations = 2;
    }

    const deltaStrength = strength / toIterations;

    let durations = [];
    let sum = 0;
    for (let i = 0; i < toIterations; i++) {
        const iterationPercent = (i + 1) / toIterations;
        const deltaDuration = duration * iterationPercent;
        sum += deltaDuration;
        durations[i] = deltaDuration;
    }

    const durationMultiplier = duration / sum;
    durations = durations.map((d) => d * durationMultiplier);

    // create to vec3 array
    const tos: Vec3[] = [];
    for (let i = 0; i < toIterations; i++) {
        if (i < toIterations - 1) {
            if (i === 0) {
                tos[i] = Vec3.add(new Vec3(), start, direction);
            } else if (i % 2 !== 0) {
                const deltaVec = clampLength(direction, strength * elasticity);
                deltaVec.negative();
                tos[i] = deltaVec.add(start);
            } else {
                const deltaVec = clampLength(direction, strength);
                tos[i] = deltaVec.add(start);
            }
        } else {
            tos[i] = start;
        }

        strength -= deltaStrength;
    }

    return {
        tos,
        durations
    }
}

export function calcShakeData(start: Vec3, duration: number, strength: Vec3, vibrato: number, randomness: number, ignoreZAxis: boolean, vectorBased: boolean,
    fadeOut: boolean) {
    KeyCode
    let shakeLength = vectorBased ? strength.length() : strength.x;
    let toIterations = Math.floor(vibrato * duration);
    if (toIterations < 2) {
        toIterations = 2;
    }
    const deltaShakeLen = shakeLength / toIterations;
    let durations = [];
    let sum = 0;
    for (let i = 0; i < toIterations; i++) {
        const iterationPercent = (i + 1) / toIterations;
        const deltaDuration = fadeOut ? duration * iterationPercent : duration / toIterations;
        sum += deltaDuration;
        durations[i] = deltaDuration;
    }

    const durationMultiplier = duration / sum;
    durations = durations.map((d) => d * durationMultiplier);

    let angle = math.randomRange(0, 360);
    const tos: Vec3[] = [];

    for (let i = 0; i < toIterations; i++) {
        if (i < toIterations - 1) {
            let randQuat = new Quat();
            if (i > 0) {
                angle = angle - 180 + math.randomRange(-randomness, randomness);
            }
            // switch(randomnessMode) {
            //     case ShakeRandomnessMode.Harmonic:
            //         if (i > 0) {
            //             angle = angle - 180 + math.randomRange(0, randomness);
            //         }
            //         if (vectorBased || !ignoreZAxis) {
            //             Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(0, randomness));
            //         }
            //         break;
            //     default:
            //         if (i > 0) {
            //             angle = angle - 180 + math.randomRange(-randomness, randomness);
            //         }
            //         if (vectorBased || !ignoreZAxis) {
            //             Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(-randomness, randomness));
            //         }
            //         break;
            // }

            if (vectorBased) {
                let to = vec3FromAngle(angle, shakeLength);
                Vec3.transformQuat(to, to, randQuat);
                to.x = clampLength(to, strength.x).x;
                to.y = clampLength(to, strength.y).y;
                to.z = clampLength(to, strength.z).z;
                to.normalize().multiplyScalar(shakeLength);
                tos[i] = to.add(start);
                if (fadeOut) {
                    shakeLength -= deltaShakeLen;
                }
                strength = clampLength(strength, shakeLength);
            } else {
                if (ignoreZAxis) {
                    tos[i] = vec3FromAngle(angle, shakeLength).add(start);
                } else {
                    Quat.fromAxisAngle(randQuat, Vec3.UP, math.randomRange(-randomness, randomness));
                    let to = vec3FromAngle(angle, shakeLength);
                    Vec3.transformQuat(to, to, randQuat);
                    tos[i] = to.add(start);
                }

                if (fadeOut) {
                    shakeLength -= deltaShakeLen;
                }
            }
        } else {
            tos[i] = start;
        }
    }

    return {
        tos,
        durations
    }
}

export function quadraticCurve(t: number, p1: Vec3, cp: Vec3, p2: Vec3) {
    let out = new Vec3(0, 0, 0);
    out.x = (1 - t) * (1 - t) * p1.x + 2 * t * (1 - t) * cp.x + t * t * p2.x;
    out.y = (1 - t) * (1 - t) * p1.y + 2 * t * (1 - t) * cp.y + t * t * p2.y;
    out.z = (1 - t) * (1 - t) * p1.z + 2 * t * (1 - t) * cp.z + t * t * p2.z;
    return out;
}