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



// Global varaibles
const whistleRange_single = 16;
const whistleRange_group = 32;





// Function for getting the entity the player clicked on
// Since afterevents does not have "entity being used on" built-in,
// and playerinteractwithentity is STILL in beta,
// we get the entity with a ray trace
// I'm sure this is'nt 100% accurate, but hopefully accurate enough
// returns undefined if the ray did not hit anything
export function playerRayEntity(player) {
	// get the most-likely (perhaps even 100% accurate) entity that was clicked on
	let entityArray = player.getEntitiesFromViewDirection( { maxDistance: whistleRange_single } );

	// verify that the player did actually click something
	if(entityArray.length > 0) {
		return entityArray[0].entity
	}
	// if the ray did not hit anything,
	// return undefined
	return undefined;
}



// function to update the lore of the player's selected item
function updateWhistleItemName(player, newName) {
	let inventory = player.getComponent(EntityComponentTypes.Inventory);
	if (inventory === undefined || inventory.container === undefined) {
		console.warn("An error occurred whilst setting the name of the selected item.")
		return;
	}

	let item = inventory.container.getItem(player.selectedSlotIndex);
	
	// update the item's name
	item.nameTag = "§rHorse Whistle: " + newName;

	// commented out since colored action-words are good enough
	// // update the item's lore
	// // NOTE that this parameter has to be an array of strings!
	// // either pass the multi-string array in when calling this function,
	// // or convert it here as we have done, but we do that here
	// // because we're only passing in one line - this wouldn't work for multiple lines!
	// item.setLore();


	// required to update the item's name
	inventory.container.setItem(player.selectedSlotIndex, item);
}



// function to set the whistle mode dyn prop of the player,
// and update the lore in their current hand to reflect it
// it SHOULD be impossible for this function to be called
// when the player is holding anything other than a cyn_hao:horse_whistle
function setWMDynProp(player, whistleMode, colorCode) {
	// save the new whistle mode to the dynProp
	player.setDynamicProperty("cyn_hao:whistle_mode", whistleMode);

	// display the new whistle mode, with the first letter capitalized
	let displayString = "";

	// unfortunately the name of the sit mode wanted to be changed
	// to stay AFTER all this was set up, so just going to special-case it
	if(whistleMode == "sit") {
		displayString = colorCode + "Stay"
	}
	else {
		displayString = colorCode + whistleMode.charAt(0).toUpperCase() + whistleMode.slice(1);
	}


	player.onScreenDisplay.setActionBar("Whistle Mode: " + displayString + "§r");


	// set lore on the item to also display the new whistle mode, with the first letter capitalized
	updateWhistleItemName(player, displayString);
}



// function to get the current whistle mode of the player
// sets the whistle mode to Follow if it is invalid
function safelyGetWhistleMode(player) {
	// get the current whistle mode
	let whistleMode = player.getDynamicProperty("cyn_hao:whistle_mode");
	
	// check if the dyn prop does NOT exist
	if ( whistleMode == undefined ) {
        // if there is NOT already data, set the player's mode to follow
        setWMDynProp(player, "follow", "§a");
		return "follow";
    }
	// otherwise, if the dyn prop does exist, return it's value
	else {
		return whistleMode;
	}
}



// function to cycle the whistle mode of the player
function cycleWhistleMode(player) {
	// get the current whistle mode
	// we do NOT use the above getWhistleMode function,
	// as if it does not exist, we WANT it to be set to follow
	let currentMode = player.getDynamicProperty("cyn_hao:whistle_mode");
	
	// set the color of each action-word
	let colorCode = "";

    // if the DynProp DOES exist, cycle it
	switch (currentMode) {
		case "follow":
			currentMode = "sit";
			
			// Follow text is Green
			colorCode = "§b";
		break;
		case "sit":
			currentMode = "wander";

			// Sit text is Gold
			colorCode = "§6";
		break;
		case "wander":
			currentMode = "follow";

			// Wander text is Green
			colorCode = "§a";
		break;

		// if the dyn prop was uninstantiated or somehow
		// something other than sit, wander, or follow,
		// set it to follow
		default:
			currentMode = "follow";

			// Follow text is Green
			colorCode = "§a";
		break;
	}

	setWMDynProp(player, currentMode, colorCode);

	// finally, return the new whistlemode
	return currentMode;
}



// Function for setting a horse's whistle mode
function setMovementMode(player, horse) {
	// set the horse's movement mode via event
	// movement modes and their events are defined in the cyn_hao:horse behavior file
	horse.triggerEvent("cyn_hao:event_horse_movement_" + safelyGetWhistleMode(player));
}

// Function to display text after whistling a horse
// Separated since it needs to happen after the for loop for multiple horses,
// and I couldn't find an easy way to do that while keeping it in the above SMM function
function notifyMovementMode(player, horse, singleHorse) {
	// set what the actionbar should say
	let subjectMessage = "";

	// set the subject part of the message
	if (singleHorse) {
		// if the player is whistling a specific horse,
		// display it's name if it has one
		if (horse.nameTag != "") {
			subjectMessage = "" + nameTag + "";
		}
		// if it doesn't, display a generic reference
		else {
			subjectMessage = "This horse";
		}
		
	}
	// if the player is whistling multiple horses,
	// display "Nearby horses" instead
	else {
		subjectMessage = "Nearby tamed horses";
	}

	let whistleMode = safelyGetWhistleMode(player);
	let actionMessage = "";
	// set the action part of the message
	switch (whistleMode) {
		case "follow":
			actionMessage = "will §afollow§r players."
		break;

		case "sit":
			actionMessage = "will §bstay§r put."
		break;

		case "wander":
			actionMessage = "will §6wander§r around."
		break;
	}

	// display to the player what this horse will now do
	player.onScreenDisplay.setActionBar(subjectMessage + " " + actionMessage);

	// play the appropriate whistle sound
	// these sounds are defined in, well, sound_definitions
	player.runCommand("playsound cyn_hao.whistle." + whistleMode + " @a[r=10] ~ ~ ~ 1.0");
}




// The paramount function to handle horse whistling
// This is called in main's item event subscription
// This will later be updated to custom item components,
// as I don't like the idea of checking if the player is holding a whistle
// EVERY time ANY player EVER right clicks with ANY item
// doesn't seem very performant! especially since potentially
// every other add-on on the player's world could be using this same subscription
// if they have even 5 add-ons active, right clicking with ANY item *could* result
// in lag! hopefully I think it's a bigger deal than it really is
export function whistleHorse(player) {

	// save the entity that the player was looking at
	// when they used the whistle, for reference
	let whistledHorse = playerRayEntity(player);
	// check what was hit by the ray

	// first things first, check if the player is sneaking
	if (player.isSneaking) {
		// if they are, apply the current whistle mode to all TAMED cyn_horses in range
		// get all nearby cyn_horses
		let nearbyHorses = player.dimension.getEntities(
			{
				families: [ "cyn_hao:tamed_horse" ],
				location: player.location,
				maxDistance: whistleRange_group
			}
		);

		// go through the list of nearby horses,
		// but only set the movement mode of TAMED ones
		// this is redundant, but the tamed_horses tag was
		// (embarassingly) added later in development
		for (let horse of nearbyHorses) {
			if(horse.hasComponent("minecraft:is_tamed")) {
				setMovementMode(player, horse);
			}
		}
		notifyMovementMode(player, false);
		return;
	}



	// next, the player did NOT shift-click,
	// but did not click on a horse
	if (whistledHorse === undefined) {
		// change the whistle mode
		cycleWhistleMode(player);
	}



	// Otherwise, if *some* entity was hit by the ray
	// if a whistleable cyn entity was whistled
	else if (whistledHorse.getComponent("minecraft:type_family").hasTypeFamily("cyn_hao:horses")) {
		// as much as I would like to check if this player is the owner,
		// I couldn't figure out how to do that
		// therefore players can whistle ANY horse, even ones they don't own
		// maybe this will be updated in the future when API
		// experimental features like .owner or getOwner() is stable

		// check if this horse is NOT tamed
		if (whistledHorse.getComponent("minecraft:is_tamed") === undefined) {
			// if it isn't, say so and return
			player.onScreenDisplay.setActionBar("This horse has not been tamed!");
			return;
		}


		// otherwise, the horse is tamed, so set it's movement mode
		setMovementMode(player, whistledHorse);
		notifyMovementMode(player, whistledHorse, true);
		
	}
	// if something was hit, but it was not a cyn_horse
	else {
		// inform the player that the whistle doesn't work on it
		player.onScreenDisplay.setActionBar("You can't whistle that!");
	}
}