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.
     */
    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;
        }
    }
	
	
    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 });
        }
    }
    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 });
        }
    }
    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 });
        }
    }
	
	equals(value) {
		return (this.x == value.x && this.y == value.y && this.z == value.z);
	}
	equals_xz(value) {
		return (this.x == value.x && this.z == value.z);
	}
	
	toCommand() {
		return `${this.x.toFixed(2)} ${this.y.toFixed(2)} ${this.z.toFixed(2)}`;
	}
	toLocation() {
		return { x: this.x, y: this.y, z: this.z };
	}
	
}

export function distance(a, b) {
    return Math.sqrt(distanceSquared(a,b));
}

// Prefer using distance squared over distance to avoid calculating square root
// Consider using 'distanceSquared(a,b) < targetDistance * targetDistance' when making 'in range' checks
export function distanceSquared(a, b) {
    const difference = (new Vector3(a)).subtract(b);
    return difference.x * difference.x + difference.y * difference.y + difference.z * difference.z;
}

export function cross(a, b) {
    // a2 * b3 - a3 * b2, a3 * b1 - a1 * b3, a1 * b2 - a2 * b1
    const c = new Vector3({
        x: a.y * b.z - a.z * b.y,
        y: a.z * b.x - a.x * b.z,
        z: a.x * b.y - a.y * b.x
    });
    return c;
}

export function copy(value)
{
	return new Vector3(value)
}
export function copyFloor(value)
{
	return new Vector3({x: Math.floor(value.x), y: Math.floor(value.y), z: Math.floor(value.z)});
}

// dim - what dimensions to check - x, xz, xyz, etc
export function locationIsInArea(location, corner1, corner2, dim)
{
	if (dim.includes("x") && !numIsInRange(location.x, corner1.x, corner2.x))
		return false;
	if (dim.includes("y") && !numIsInRange(location.y, corner1.y, corner2.y))
		return false;
	if (dim.includes("z") && !numIsInRange(location.z, corner1.z, corner2.z))
		return false;
	return true;
}

export function numIsInRange(num, edge1, edge2)
{
	if (edge1 <= num && num <= edge2) 
		return true;
	if (edge2 <= num && num <= edge1)
		return true;
	return false;
}

export function vectorToRotation(vector) {    
    // Calculate the angle using the arctangent function
    const angle = Math.atan2(vector.x, vector.z);
    
    // Convert the angle from radians to degrees if needed
    const angleInDegrees = angle * (180 / Math.PI);

    return angleInDegrees;
}
export function rotationToFacing(rotation)
{
	let rot = rotation.y;
	if (rot < 0)
		rot += 360;
	
	if (rot < 45) return "south";
	if (rot < 135) return "west";
	if (rot < 225) return "north"; 
	if (rot < 315) return "east";
	return "south";
	// 0 south 90 west 180 north 270 east
}
export function rotationToFixedRotation(rotation)
{
	let rot = rotation.y;
	if (rot < 0)
		rot += 360;
	
	if (rot < 45) return 0;
	if (rot < 135) return 90;
	if (rot < 225) return 180; 
	if (rot < 315) return 270;
	return 0;
	// 0 south 90 west 180 north 270 east
}

export function areaToSelector(corner1, corner2) 
{
    //return "x=" + corner1.x + ",y=" + corner1.y + ",z=" + corner1.z + ",dx=" + (corner2.x - corner1.x) + ",dy=" + (corner2.y - corner1.y) + ",dz=" + (corner2.z - corner1.z);
    return "x=" + corner1.x + ",y=" + -64 + ",z=" + corner1.z + ",dx=" + (corner2.x - corner1.x) + ",dy=" + 384 + ",dz=" + (corner2.z - corner1.z);
    //return "x=" + corner1.x + ",z=" + corner1.z + ",dx=" + (corner2.x - corner1.x) + ",dz=" + (corner2.z - corner1.z);
}

export const Down = new Vector3({ x: 0, y: -1, z: 0 });
export const Down2 = new Vector3({ x: 0, y: -2, z: 0 });
export const Up = new Vector3({ x: 0, y: 1, z: 0 });
export const Up2 = new Vector3({ x: 0, y: 2, z: 0 });
export const North = new Vector3({ x: 0, y: 0, z: -1 });
export const South = new Vector3({ x: 0, y: 0, z: 1 });
export const West = new Vector3({ x: -1, y: 0, z: 0 });
export const East = new Vector3({ x: 1, y: 0, z: 0 });

export function areLocationsInRangeXZ(loc1, loc2, range) {
    let dx = Math.abs(loc1.x - loc2.x);
    let dz = Math.abs(loc1.z - loc2.z);
    return dx <= range && dz <= range;
}

