import { system, world } from "@minecraft/server"

////////////////////////
////////////////////////
//// version 1.0.46 ////
////////////////////////
////////////////////////

// ¯\_(ツ)_/¯

// export function vect(vect) {
// 	function stringify() {
// 		return `${vect.x} ${vect.y} ${vect.z}`
// 	}
// }

// export class vector {
//     constructor(x,y,z) {
//         this.x = x
//         this.y = y
//         this.z = z
//     }

//     toString() {
//         return `${this.x} ${this.y} ${this.z}`
//     }
// }

export function vectorToString(vect) {
	return `${vect.x} ${vect.y} ${vect.z}`
}

/**
 * returns distance squared, avoiding costly square root calculations
 * @param {*} vect1 
 * @param {*} vect2 
 * @returns 
 */
export function cheapDist(vect1, vect2) {
    const dx = vect1.x - vect2.x
    const dy = vect1.y - vect2.y
    const dz = vect1.z - vect2.z
    return (dx * dx + dy * dy + dz * dz)
}

/**
 * returns actual distance
 * @param {*} vect1 
 * @param {*} vect2 
 * @returns 
 */
export function distance(vect1, vect2) {
    return Math.sqrt(cheapDist(vect1,vect2))
}

export function isWithinRadius(queryPos,targetPos,radius) {
	const dist = cheapDist(queryPos,targetPos)
	return dist <= radius * radius
}

/**
 * Note that {dist} can be negative, if desired.
 * @param {Vector3} vector 
 * @param {number} dist 
 * @returns 
 */
export function vectorAbove(vector,dist=1) {
	return {x:vector.x,y:vector.y + dist,z:vector.z}
}

export function bottomCenter(position) {
	return {
		x:Math.floor(position.x) + 0.5,
		y:Math.floor(position.y),
		z:Math.floor(position.z) + 0.5
	}
}

export function center(position) {
    const result = bottomCenter(position)
    result.y += 0.5
    return result
}

export function createVector(x, y = undefined, z = undefined) {
    if (x !== undefined && y === undefined && z === undefined && typeof x === "string") {
        const parts = x.replace(/\s+/g, ' ') // Replace multiple spaces with a single space
        .split(/[\s,]+/) // Split by spaces or commas
        if (parts.length !== 3) throw new Error(`invalid vector input: ${JSON.stringify(x)}`)
        const [newX, newY, newZ] = parts.map(Number)
        x = newX
        y = newY
        z = newZ
    }
    
    return {x:Number(x), y:Number(y), z:Number(z)}
}

export function vectorEquals(vect1, vect2) {
    if (!vect1 || !vect2) return false
    return (vect1.x === vect2.x && vect1.y === vect2.y && vect1.z === vect2.z)
}

export function vectorMultiplyByNumber(vect1,multiple) {
    return {
        x:vect1.x * multiple,
        y:vect1.y * multiple,
        z:vect1.z * multiple
    }
}

export function vectorRoundDown(vect) {
	return {
		x:Math.floor(vect.x),
		y:Math.floor(vect.y),
		z:Math.floor(vect.z)
	}
}

export function vectorRound(vect, places) {
	const operand = places * 10
	return {
		x:Math.round(vect.x * operand)/operand,
		y:Math.round(vect.y * operand)/operand,
		z:Math.round(vect.z * operand)/operand
	}
}

export function vectorSubtract(vect1, vect2, excludeY = false) {
	if (vect1 === undefined || vect2 === undefined) throw new Error (`vector undefined, cannot subtract! ${JSON.stringify(vect1)}, ${JSON.stringify(vect2)}, ${excludeY}`)
    return {
        x:vect1.x - vect2.x,
        y:excludeY ? 0 : vect1.y - vect2.y,
        z:vect1.z - vect2.z
    }
}

export function vectorAdd(vect1, vect2) {
	if (vect1 === undefined || vect2 === undefined) throw new Error (`vector undefined, cannot add! ${JSON.stringify(vect1)}, ${JSON.stringify(vect2)}`)
    return {
        x:vect1.x + vect2.x,
        y:vect1.y + vect2.y,
        z:vect1.z + vect2.z
    }
}

export function vectorMagnitude(vect1) {
	if (vect1 === undefined) throw new Error (`vector undefined, cannot get magnitude! ${JSON.stringify(vect1)}`)
    return Math.sqrt((vect1.x * vect1.x) + (vect1.y * vect1.y) + (vect1.z * vect1.z))
}

export function vectorH_Angle(vect) {
    return -(Math.atan2(vect.x, vect.z) * (180 / Math.PI))
}

export function sendDebugMessage(message) {
	if (message instanceof Error) message = `§4${message.name}:§r\n${message.stack}${message.message}`
	world.getPlayers({tags: ["lifeboat:debug"]}).forEach(op => op.sendMessage(`§6[AS] §dDebug§r: ${message}`))
}

function hasTimePassed(start, length) {
    if (system.currentTick - start >= length) return true
    return false
}

/**
 * when used with the await keyword, pauses execution for <ticks> number of game ticks.
 * @param {int} ticks 
 * @returns 
 */
export async function pause(ticks = 1, debugReason) {
    if (ticks <= 0) return true
    if (ticks >= 600) sendDebugMessage(`${ticks} ticks is a long time!  That's ${ticks / 20} seconds - is this intentional??`)
    sendDebugMessage(`Pausing for ${ticks} tick${ticks > 1 ? "s" : ""}${debugReason ? `, reason: ${debugReason}` : "..."}`)
    const start = system.currentTick
    return new Promise(resolve => {
        const waiting = () => {
            const result = hasTimePassed(start,ticks)
            if (result) {
                resolve(true)
            } else system.run(waiting)
        }
        waiting()
    })
}

/**
 * 
 * @param {Array} recipients - Entity array.  Accepts a single, non array player, or an array of players.
 * @param {String} translationKey - translation key
 * @param {Array} textArgs - if translation is desired, each string MUST be formatted beginning with "text.".  Otherwise, the given text will be printed.
 * @param {String} translationKey2 - translation key
 * @param {Array} textArgs2 - if translation is desired, each string MUST be formatted beginning with "text.".  Otherwise, the given text will be printed.
 */
// export function sendAddonMessage(recipients, translationKey, textArgs = [], translationKey2 = undefined, textArgs2 = []) {
// 	const rawText = {
// 		rawtext:[
// 			{text:"["},
// 			{translate:"text.addonName"},
// 			{text:"]: "},
// 			{translate:"text." + translationKey}
// 		]
// 	}
// 	if (textArgs.length > 0) {
// 		rawText.rawtext[3].with = {
// 			rawtext: textArgs.map(arg => arg.startsWith("text.") ? { translate: arg } : { text: arg })
// 		}
// 	}
// 	if (translationKey2) {
// 		rawText.rawtext.push({text: " "})
// 		rawText.rawtext.push(
// 			{translate:"text." + translationKey2}
// 		)
// 		if (textArgs2.length > 0) {
// 			rawText.rawtext[rawText.rawtext.length-1].with = {
// 				rawtext: textArgs2.map(arg => arg.startsWith("text.") ? { translate: arg } : { text: arg })
// 			}
// 		}
// 	}
// 	if (recipients instanceof Array) {
// 		recipients.forEach(player => player.sendMessage(rawText))
// 	} else {
// 		recipients.sendMessage(rawText)
// 	}
// }

export function sendAddonMessage(recipients, sender = "text.lb_as.Add-OnNameTag", ...translationArgs) {
    if (Array.isArray(sender)) {
        translationArgs = [sender]
        sender = "text.lb_as.Add-OnNameTag"
    }
    const rawText = {
        rawtext: [
            { text: "[" },
            { translate: sender },
            { text: "§r]: " }
        ]
    };

    translationArgs.forEach(([translationKey, textArgs]) => {
        rawText.rawtext.push({ translate: "text.lb_as." + translationKey });
        if (textArgs && textArgs.length > 0) {
            textArgs = textArgs.map(String)
            rawText.rawtext[rawText.rawtext.length - 1].with = {
                rawtext: textArgs.map(arg => arg.startsWith("text.lb_as.") ? { translate: arg } : { text: arg })
            };
        }
        rawText.rawtext.push({ text: " " }); // Add a space after each translation
    });

    // Remove the last space if no translation keys were provided
    if (translationArgs.length > 0) {
        rawText.rawtext.pop(); // Remove the last space added
    }

    if (recipients instanceof Array) {
        recipients.forEach(player => player.sendMessage(rawText));
    } else {
        recipients.sendMessage(rawText);
    }
}

export function isInsideHeightLimits(height,dimension) {
    if (height >= dimension.heightRange.min && height <= dimension.heightRange.max) return true
    return false
}

export function isMovingTowards(entity1, entity2) {
    const velocity1 = entity1.getVelocity()
    const velocity2 = entity2.getVelocity()

    const relativeX = velocity2.x - velocity1.x
    const relativeZ = velocity2.z - velocity1.z

    const deltaX = entity2.location.x - entity1.location.x;
    const deltaZ = entity2.location.z - entity1.location.z

    return (relativeX * deltaX + relativeZ * deltaZ) < 0
}

export function queryBlock(location,dimension) {
    try {
        if (isInsideHeightLimits(location.y,dimension)) {
            return dimension.getBlock(location)
        } else return
    } catch (error) {
        sendDebugMessage(error)
    }
}

export function toDegrees(radians) {
    return radians * (180/Math.PI)
}

export function toRadians(degrees) {
    return degrees * (Math.PI/180)
}

export function directionToVect(directionString) {
    switch(directionString.toLowerCase()) {
        case "north":
            return createVector(0, 0, -1)
        case "east":
            return createVector(1, 0, 0)
        case "south":
            return createVector(0, 0, 1)
        case "west":
            return createVector(-1, 0, 0)
        case "northeast":
            return createVector(1, 0, -1)
        case "southeast":
            return createVector(1, 0, 1)
        case "southwest":
            return createVector(-1, 0, 1)
        case "northwest":
            return createVector(-1, 0, -1)
        case "up":
            return createVector(0, 1, 0)
        case "down":
            return createVector(0, -1, 0)
        default:
            sendDebugMessage(`§4unknown direction ${directionString}`)
            return undefined
    }
}

export const pistonDirectTranslate = [
    "down",
    "up",
    "south",
    "north",
    "east",
    "west"
]

export function reverseDirection(direction) {
    const index = pistonDirectTranslate.indexOf(direction.toLowerCase())
    if (index < 0) throw new Error(`${direction} has no index`)
    return pistonDirectTranslate[index % 2 === 0 ? index + 1 : index - 1]
}

export function perpendiculars(direction) {
    switch(direction.toLowerCase()) {
        case "north":
        case "south":
            return ["east", "west"]
        case "east":
        case "west":
            return ["north", "south"]
        default:
            sendDebugMessage(`§4unable to find perpendiculars for "${direction}"`)
            return undefined
    }
}

export function getCardinalDirection(vector) {
    const angle = vectorH_Angle(vector)
    if (Math.abs(angle) <= 45) return "south"
    if (Math.abs(angle) <= 135) {
        if (angle < 0) return "east"
        else return "west"
    } else return "north"
}

export function blockFaceToDirection(face) {
    face = face.toLowerCase()
    switch(face) {
        case "up":
            return "above"
        case "down":
            return "below"
        default:
            return face
    }
}