// Script to manage updating horses' equipment properties
// Includes dumping chest contents when un-chesting horses,
// Updating Armor and Carpets, 
// and checking for burning in daylight for Zombie and Skeleton Horses
// Author: InnerTri





// Imports
// Important Global Variables
import {
    system,
	ItemStack
} from "@minecraft/server";





// Global Variables
// armor types
// the index equals the armor's type
const armorTypes = [
	"minecraft:leather_horse_armor",
	"minecraft:iron_horse_armor",
	"minecraft:golden_horse_armor",
	"minecraft:diamond_horse_armor"
]

// carpet colors
// the index equals the carpet's alias ID
const carpetIds = [ 
	"minecraft:white_carpet", 
	"minecraft:orange_carpet", 
	"minecraft:magenta_carpet", 
	"minecraft:light_blue_carpet", 
	"minecraft:yellow_carpet", 
	"minecraft:lime_carpet", 
	"minecraft:pink_carpet", 
	"minecraft:gray_carpet", 
	"minecraft:light_gray_carpet", 
	"minecraft:cyan_carpet", 
	"minecraft:purple_carpet", 
	"minecraft:blue_carpet", 
	"minecraft:brown_carpet", 
	"minecraft:green_carpet", 
	"minecraft:red_carpet", 
	"minecraft:black_carpet",
	"minecraft:moss_carpet",
	"minecraft:pale_moss_carpet"
]



// Event Subscriptions
system.afterEvents.scriptEventReceive.subscribe(
    event => {
        // first, check if the message is for us
        if (event.id === "cyn:hao") {
            // if it is, save entity for reference and check which "command" is being run
            let sourceEntity = event.sourceEntity;
            switch (event.message) {

				// Cyn Horse needs it's chest inventory dropped
				case "removeChest":
					// go through the horse's inventory and drop items at it's location
					dumpInventory(sourceEntity)
				return;



				// Cyn Horse was equipped with a carpet
				case "updateCarpet":
					// set the horse's property according to the carpet within
					// ideally we would get the item's alias ID directly here, 
					// since that is carpet 0 = white all the way to carpet 15 = black,
					// but I couldn't find a way to do that

					// so instead we defined the carpet's names in an array,
					// and then their index in the array is their alias!
					// then we can just use the index that the name of the carpet this horse has
					// +1 since render controller needs a "no carpet" texture, as the property value!
					// proud of that (maybe-not-so) clever solution!

					// and, even better, since .indexOf returns -1 if the element was not in the array,
					// if there is ever a problem, the property will be set to 0,
					// and white carpet will be used instead of everything going up in flames
					// convenient! granted, should that situation ever arise
					// that's obviously still a problem!
					// still warn the error though, for ease of debugging in the future
					
					// breakdown of the long line:
					// // the horse that sent the event command
					// sourceEntity
					// // their inventory component
					// .getComponent("minecraft:inventory")
					// // the actual inventory belonging to that component
					// .container
					// // the "carpet" equipment slot
					// .getSlot(2)
					// // the item in said slot
					// .getItem()
					// // the name of said item
					// .typeId
					let carpetColor = carpetIds.indexOf(sourceEntity.getComponent("minecraft:inventory").container.getSlot(2).getItem().typeId);

					if (carpetColor == -1) {
						console.warn("A problem occurred whilst setting the carpet color.\nThe cyn_hao:carpet_color property will be set to 0.");
					}

					

					// again, reminder:
					// carpetColor + 1 since render controller has a "no carpet" texture at property 0
					sourceEntity.setProperty("cyn_hao:carpet_color", carpetColor + 1);
				return;



				// Armor is done identically to carpet
				// why is it below carpet here, when everywhere else it's above?
				// because I thought I had it working, so moved on to carpet
				// but turns out I did not have it working, but carpet works,
				// so the functionality could be copied
				// but I didn't want to update the above comments,
				// and the part about the carpet's aliases makes sense, since it's carpet/x,
				// whereas horse armor does NOT have aliases like that 
				// it is horsearmorleather, horsearmoriron, etc. instead of
				// horsearmor/0, horsearmor/1, etc.
				case "updateArmor":
					let armorItem = sourceEntity.getComponent("minecraft:inventory").container.getSlot(1).getItem();
					
					// verify the item in the armor slot is valid
					// if you remove armor,
					// wait a very short period of time,
					// and then re-place the armor,
					// the armor doesn't actually get put in the slot, as the game
					// second click is fast enough to count as a double-click,
					// so the game instead runs it's "stack items" code,
					// which includes NOT actually putting the item in the slot

					// however, for some reason, the game still triggers the
					// on_equip event, even though it's not actually equipping anything,
					// so the item in the slot is undefined when we run all this code
					// because there's nothing there
					// so catch that so that it doesn't throw an error
					let armorType = -1;
					if (armorItem != undefined) {
						armorType = armorTypes.indexOf(armorItem.typeId);
					}
					else {
						console.warn("A problem occurred whilst setting the armor type.\nThe cyn_hao:armor_type property will be set to 0.");
					}

					// again, reminder:
					// armorType + 1 since render controller has a "no armor" texture at property 0
					sourceEntity.setProperty("cyn_hao:armor_type", armorType + 1);
				return;



                // Burning in Daylight check for Zombie and Skeleton Horses
                // We can ALWAYS remove burning when carpet / armor is ADDED,
                // but when one is removed, we must check the other first
                // I just realized I probably could / should have done this in an AnimController...
                case "checkBurning":
                    // get the horse's inventory
                    let invContainer = sourceEntity.getComponent("minecraft:inventory").container;

                    // check if BOTH Armor and Carpet slots are empty (saddle doesn't block burning)
                    // 0 - Saddle
                    // 1 - Armor
                    // 2 - Carpet
                    // 3+ - Chested Inventory
                    if ((invContainer.getItem(1) === undefined) && (invContainer.getItem(2) === undefined)) {
                        // if both are empty, make it burn in daylight or take damage from rain
						// sourceEntity.runCommand so that, if the event does not exist, simply nothing happens
						// otherwise, using triggerEvent throws an error
                        sourceEntity.runCommand("event entity @s cyn_hao:event_burns_in_daylight");
						sourceEntity.runCommand("event entity @s cyn_hao:event_not_immune_to_rain");
                    }
                    // otherwise, ensure it won't burn in daylight or take damage from rain
                    else {
                        sourceEntity.runCommand("event entity @s cyn_hao:event_not_burns_in_daylight");
						sourceEntity.runCommand("event entity @s cyn_hao:event_immune_to_rain");
                    }
                return;
            }
        }
    }
);





function dumpInventory(entity) {
	// get this entity's inventory component
	let container = entity.getComponent("minecraft:inventory").container;
	
	// verify that it is valid
	if (!container.isValid) {
		console.warn("Attempted to empty an invalid container.");
		return;
	}
	
	// if for some reason this horse does NOT have 4 slots
	// 4 slots needed since the first 3 are the saddle, armor, and carpet slots
	if (container.size < 4) {
		console.warn("Attempted to empty a continer with less than 3 slots.");
		return;
	}

	// otherwise, if all is good, carry on
	let currentSlot;
	let slotItem;
	let spawnLoc = entity.location
	spawnLoc.y = spawnLoc.y + 1;

	// go through the container's slots
	// container.size + 3 since the first 3 are saddle, armor, and carpet slots
	for(let i = 3; i < container.size; i++) {
		// get the current slot's itemStack
		currentSlot = container.getSlot(i)
		slotItem = currentSlot.getItem();

		// if there is an item at the current slot
		if (slotItem != undefined) {
			// spawn a copy of the itemStack
			entity.dimension.spawnItem(slotItem, spawnLoc);
			
			// clear the existing item
			currentSlot.setItem();
		}
	}

	// drop a chest as well with the other items
	entity.dimension.spawnItem(new ItemStack("minecraft:chest", 1), spawnLoc);
}