/**
 * @remarks A class to extend Minecraft's Vector3 with additional functions for vector math.
 */
export class Vector3 {
    /**
     * @remarks Create a new Vector3.
     * @param value The value to crate a new Vector3. Either a single value used for x, y and z. Or an object with an x, y and z value.
     * @example
     * Number Example
     * ```typescript
     * const vector = new Vector3(0); // Creates a vector with value {x: 0, y: 0, z: 0};
     * ```
     * @example
     * Vector Example
     * ```typescript
     * const vector = new Vector3({x: 0, y: 0, z: 0});
     * ```
     */
    constructor(value) {
        if (typeof value === 'object' && value !== null) {
            this.x = value.x;
            this.y = value.y;
            this.z = value.z;
        }
        else {
            this.x = value;
            this.y = value;
            this.z = value;
        }
    }
    /**
     * @remarks Adds another Vector3 to this Vector3.
     * @param value The Vector3 to be added to this.
     * @returns A new Vector3.
     * @example
     * ```typescript
     * vector = vector.add({x: 0, y: 10, z: 0}); // Creates a vector where y is 10 higher.
     * ```
     */
    add(value) {
        if (typeof value === 'object' && value !== null) {
            return new Vector3({ x: this.x + value.x, y: this.y + value.y, z: this.z + value.z });
        }
        else {
            return new Vector3({ x: this.x + value, y: this.y + value, z: this.z + value });
        }
    }
    /**
     * @remarks Subtracts another Vector3 from this Vector3.
     * @param value The Vector3 to be subtracted from this.
     * @returns A new Vector3.
     * @example
     * ```typescript
     * vector = vector.subtract({x: 0, y: 10, z: 0}); // Creates a vector where y is 10 lower.
     * ```
     */
    subtract(value) {
        if (typeof value === 'object' && value !== null) {
            return new Vector3({ x: this.x - value.x, y: this.y - value.y, z: this.z - value.z });
        }
        else {
            return new Vector3({ x: this.x - value, y: this.y - value, z: this.z - value });
        }
    }
    /**
     * @remarks Multiplies another Vector3 by this Vector3.
     * @param value The Vector3 to be multiplied to this.
     * @returns A new Vector3.
     * @example
     * ```typescript
     * new Vector3(1).multiply(10); // Creates the vector {x: 10, y: 10, z: 10}
     * ```
     */
    multiply(value) {
        if (typeof value === 'object' && value !== null) {
            return new Vector3({ x: this.x * value.x, y: this.y * value.y, z: this.z * value.z });
        }
        else {
            return new Vector3({ x: this.x * value, y: this.y * value, z: this.z * value });
        }
    }
    /**
     * @remarks Divides another Vector3 by this Vector3.
     * @param value The Vector3 to be divided by this.
     * @returns A new Vector3.
     * ```typescript
     * new Vector3(10).divide(10); // Creates the vector {x: 1, y: 1, z: 1}
     * ```
     */
    divide(value) {
        if (typeof value === 'object' && value !== null) {
            return new Vector3({ x: this.x / value.x, y: this.y / value.y, z: this.z / value.z });
        }
        else {
            return new Vector3({ x: this.x / value, y: this.y / value, z: this.z / value });
        }
    }
    /**
     * @remarks Normalizes this vector.
     * @returns This vector normalized.
     * @example
     * ```typescript
     * vector = vector.normalize();
     * ```
     */
    normalize() {
        const newVector = this;
        const v = Math.sqrt((newVector.x * newVector.x) + (newVector.y * newVector.y) + (newVector.z * newVector.z));
        if (v !== 0) {
            newVector.x = newVector.x / v;
            newVector.y = newVector.y / v;
            newVector.z = newVector.z / v;
        }
        return newVector;
    }
    /**
     * @remarks Gets the distance between this and a target vector.
     * @param vector The vector to check the distance against.
     * @returns The distance between this and the target vector.
     * @example
     * ```typescript
     * function getDistance(source: SafetyCheckedEntity, target: SafetyCheckedEntity): number {
     * 	return source.location.distance(target.location);
     * }
     * ```
     */
    distance(vector) {
        const x = (this.x - vector.x) ** 2;
        const y = (this.y - vector.y) ** 2;
        const z = (this.z - vector.z) ** 2;
        return Math.sqrt(x + y + z);
    }
    /**
     * @remarks Rotates this vector by a provided angle.
     * @param angle The angle by which to rotate the vector.
     * @returns The rotated vector.
     * @example
     * https://github.com/Dotzip-Developments/add-on-biome-pets/blob/main/scripts/needle_hog.ts
     * ```typescript
     * for (let angle = look_angle; angle < look_angle + 360; angle += 45) {
     * 	const direction = needle_hog.getViewDirection().rotateByAngle(angle);
     * 	const origin = needle_hog.getHeadLocation().add(direction.multiply(0.5));
     * 	const projectile = new utils.SafetyCheckedEntity(event.sourceEntity.dimension.spawnEntity(projectile_ID, origin));
     * 	projectile.applyImpulse(direction.multiply(2));
     * 	projectile.setRotation({x: 0, y: angle});
     * }
     * ```
     */
    rotateByAngle(angle) {
        const sin = Math.sin(angle * (3.14 / 180));
        const cos = Math.cos(angle * (3.14 / 180));
        const vectorA = new Vector3({ x: cos, y: 0, z: sin });
        const vectorB = new Vector3({ x: -sin, y: 0, z: cos });
        const newVector = vectorA.multiply(this.x).add(vectorB.multiply(this.z));
        newVector.y = this.y;
        return newVector;
    }
    /**
     * @remarks Rounds a Vector3 to whole numbers.
     * @returns This Vector3 rounded to the nearest whole number.
     * @example
     * ```typescript
     * const vector = new Vector3({x: 0.6, y: 10.2, z: 14.9}).round(); // Returns a vector with the value {x: 1, y: 10, z: 15}
     * ```
     */
    round() {
        const x = Math.round(this.x);
        const y = Math.round(this.y);
        const z = Math.round(this.z);
        return new Vector3({ x, y, z });
    }
    /**
     * @remarks Floors a Vector3 to whole numbers.
     * @returns This Vector3 floored to the nearest whole number.
     * @example
     * ```typescript
     * const vector = new Vector3({x: 0.6, y: 10.2, z: 14.9}).floor(); // Returns a vector with the value {x: 0, y: 10, z: 14}
     * ```
     */
    floor() {
        const x = Math.floor(this.x);
        const y = Math.floor(this.y);
        const z = Math.floor(this.z);
        return new Vector3({ x, y, z });
    }
    /**
     * @remarks Ceils a Vector3 to whole numbers.
     * @returns This Vector3 ceiled to the nearest whole number.
     * @example
     * ```typescript
     * const vector = new Vector3({x: 0.6, y: 10.2, z: 14.9}).ceil(); // Returns a vector with the value {x: 1, y: 11, z: 15}
     * ```
     */
    ceil() {
        const x = Math.ceil(this.x);
        const y = Math.ceil(this.y);
        const z = Math.ceil(this.z);
        return new Vector3({ x, y, z });
    }
    /**
     * @remarks Returns a block center, useful for spawning mobs at the center of a block.
     * @returns This Vector3 centered.
     * @example
     * ```typescript
     * const vector = new Vector3({x: 0.6, y: 10.2, z: 14.9}).center(); // Returns a vector with the value {x: 0.5, y: 10, z: 14.5}
     * ```
     */
    center() {
        return this.floor().add({ x: 0.5, y: 0, z: 0.5 });
    }
    /**
     * @remarks Returns a Vector3 some blocks up of this Vector3.
     * @param [amount=1] The number of blocks up from this Vector3 to return, defaults to 1.
     * @returns The location above this Vector.
     */
    up(amount = 1) {
        return this.add(Vector3.UP.multiply(amount));
    }
    /**
     * @remarks Returns a Vector3 one block down of this Vector3.
     * @param [amount=1] The number of blocks down from this Vector3 to return, defaults to 1.
     * @returns The location below this Vector.
     */
    down(amount = 1) {
        return this.add(Vector3.DOWN.multiply(amount));
    }
    /**
     * @remarks Returns a Vector3 none block north of this Vector3.
     * @param [amount=1] The number of blocks north from this Vector3 to return, defaults to 1.
     * @returns The location north of this Vector.
     */
    north(amount = 1) {
        return this.add(Vector3.NORTH.multiply(amount));
    }
    /**
     * @remarks Returns a Vector3 sone block south of this Vector3.
     * @param [amount=1] The number of blocks south from this Vector3 to return, defaults to 1.
     * @returns The location south of this Vector.
     */
    south(amount = 1) {
        return this.add(Vector3.SOUTH.multiply(amount));
    }
    /**
     * @remarks Returns a Vector3 one block east of this Vector3.
     * @param [amount=1] The number of blocks east from this Vector3 to return, defaults to 1.
     * @returns The location east of this Vector.
     */
    east(amount = 1) {
        return this.add(Vector3.EAST.multiply(amount));
    }
    /**
     * @remarks Returns a Vector3 one block west of this Vector3.
     * @param [amount=1] The number of blocks west from this Vector3 to return, defaults to 1.
     * @returns The location west of this Vector.
     */
    west(amount = 1) {
        return this.add(Vector3.WEST.multiply(amount));
    }
    /**
     * @remarks Checks if two vectors are approximately equal.
     * @param vector The vector to compare
     * @returns True if these vectors are very close to eachother. False otherwise.
     * @example
     * ```typescript
     * const locationA = new Vector3(1.1);
     * const locationB = new Vector3(1.2);
     * const locationC = new Vector3(4.0);
     * locationA.approximatelyEqual(locationB); // Returns true.
     * locationA.approximatelyEqual(locationC); // Returns false.
     * ```
     */
    approximatelyEqual(vector) {
        const originX = Math.floor(this.x);
        const originY = Math.floor(this.y);
        const originZ = Math.floor(this.z);
        const targetX = Math.floor(vector.x);
        const targetY = Math.floor(vector.y);
        const targetZ = Math.floor(vector.z);
        return (originX === targetX) && (originY === targetY) && (originZ === targetZ);
    }
    /**
     * @remarks Represents a Vector3 as spaced out numbers, can be used in commands.
     * @returns A string representation of the Vector3.
     * @example
     * ```typescript
     * const player = getAllPlayersSafe()[0];
     * player.runCommand(`setblock ${location.toString()} stone`); // toString can be called manually.
     * player.runCommand(`setblock ${location} stone`); // toString is also automatically called when using string conversion.
     * ```
     */
    toString() {
        return `${this.x} ${this.y} ${this.z}`;
    }
    /**
     * @deprecated Result moved to toString to allow default string conversion in the command format
     * Converts a vector to a string used for command strings.
     * @returns A string formated for command inputs.
     */
    toCommandString() {
        return `${this.x} ${this.y} ${this.z}`;
    }
}
Vector3.ZERO = new Vector3({ x: 0, y: 0, z: 0 });
Vector3.UP = new Vector3({ x: 0, y: 1, z: 0 });
Vector3.DOWN = new Vector3({ x: 0, y: -1, z: 0 });
Vector3.NORTH = new Vector3({ x: 0, y: 0, z: -1 });
Vector3.SOUTH = new Vector3({ x: 0, y: 0, z: 1 });
Vector3.EAST = new Vector3({ x: 1, y: 0, z: 0 });
Vector3.WEST = new Vector3({ x: -1, y: 0, z: 0 });
