import { world, Vector2, Vector3 } from "@minecraft/server"

export const GeneralUtils = {
    "math": {
        "vectors": {
            /**
             * @param {Vector3} orbitAround - The point to orbit around
             * @param {number} pitch - The pitch that the orbit is at
             * @param {number} yaw - The yaw that the orbit is at
             * @param {number} distance - The distance that the point is from the orbitAround location
             * @param {number} hieghtOffset - The hieght that the point is offset from the final camera location
             */
            calculateOrbitPosition: (orbitAround = { x: 0, y: 0, z: 0 }, pitch = 0, yaw = 0, distance = 0, hieghtOffset = 0) => {
                return {
                    x: orbitAround.x - Math.sin(-yaw) * Math.cos(pitch) * distance,
                    y: (orbitAround.y + Math.sin(pitch) * distance) + hieghtOffset,
                    z: orbitAround.z - Math.cos(-yaw) * Math.cos(pitch) * distance
                };
            }
        },
        /**
         * @param {number} value - The value to be clamped
         * @param {number} min - The lower clamp
         * @param {number} max - The upper clamp
         */
        clamp: (value, min, max) => Math.max(min, Math.min(max, value)),
        /**
         * @param {number} value - The value (in degrees) to be turned into radians
         */
        toRad: (value) => value * Math.PI / 180,
        /**
         * @param {number} value - The value (in radians) to be turned into degrees
         */
        toDeg: (value) => value * 180 / Math.PI,
        /**
         * @param {number} angle - The angle (in degrees) to be normalized
         */
        normalize: (angle) => ((angle + 180) % 360 + 360) % 360 - 180,
        /**
         * @param {Vector3} vec - The vector to be clamped
         * @param {number} maxSpeed - The max value to clamp to
         */
        clampVector: (vec, maxSpeed) => {
            const mag = Math.hypot(vec.x, vec.y, vec.z);
            if (mag > maxSpeed) {
                const scale = maxSpeed / mag;
                vec.x *= scale;
                vec.y *= scale;
                vec.z *= scale;
            }
            return vec;
        }
    },
    "world": {
        dimensions: ["overworld", "nether", "the_end"],
        /**
         * @param {Entity} entity - The entity to get player from
         */
        getPlayerFromEntity: (entity) => entity ? world.getPlayers().find(p => p.id === entity.id) : undefined,
        /**
         * @param {Entity} entity - The entity to rotate
         * @param {Vector2} targetRot - The target rotation
         * @param {number} turnSpeed - The turn speed (in degrees per second)
         * @param {Vector2} forcedRotation - rotation that is forced on after the rotation is calculated
         */
        rotateByClamped: (entity, targetRot, turnSpeed, forcedRotation = { x: 0, y: 0 }) => {
            const pitch = entity.getDynamicProperty("af:slow-pitch");
            const yaw = entity.getDynamicProperty("af:slow-yaw");

            var currentRot = entity.getRotation();
            if (pitch != undefined && yaw != undefined) {
                currentRot = {
                    x: pitch,
                    y: yaw
                }
            }

            if (targetRot == undefined) {
                targetRot = currentRot;
            }

            const deltaPerTick = turnSpeed / 20;

            let deltaX = GeneralUtils.math.normalize(targetRot.x - currentRot.x);
            let deltaY = GeneralUtils.math.normalize(targetRot.y - currentRot.y);

            deltaX = Math.abs(deltaX) > deltaPerTick ? deltaPerTick * Math.sign(deltaX) : deltaX;
            deltaY = Math.abs(deltaY) > deltaPerTick ? deltaPerTick * Math.sign(deltaY) : deltaY;

            var newPitch = currentRot.x + deltaX + forcedRotation.x;
            var newYaw = currentRot.y + deltaY + forcedRotation.y;

            const safePitch = Number.isFinite(newPitch) ? newPitch : 0;
            const safeYaw = Number.isFinite(newYaw) ? newYaw : 0;

            entity.setDynamicProperty("af:slow-pitch", safePitch);
            entity.setDynamicProperty("af:slow-yaw", safeYaw);

            return {
                x: safePitch,
                y: safeYaw
            };
        }
    }
}