package net.knarcraft.blacksmith.listener;

import net.citizensnpcs.api.CitizensAPI;
import net.knarcraft.blacksmith.trait.BlacksmithTrait;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;

public class PlayerListener implements Listener {

    /**
     * Blocks armor equipment if right-clicking a blacksmith
     *
     * @param event <p>The triggered event</p>
     */
    @EventHandler(priority = EventPriority.HIGHEST)
    public void onClick(PlayerInteractEvent event) {
        if (event.getHand() == null || (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() !=
                Action.RIGHT_CLICK_BLOCK)) {
            return;
        }
        //If the player is not looking at a blacksmith, there is no need to do anything
        Entity target = getTarget(event.getPlayer(), event.getPlayer().getNearbyEntities(15, 10, 15));
        if (!CitizensAPI.getNPCRegistry().isNPC(target) ||
                !CitizensAPI.getNPCRegistry().getNPC(target).hasTrait(BlacksmithTrait.class)) {
            return;
        }

        //Block armor equip if interacting with a blacksmith
        ItemStack usedItem = event.getPlayer().getInventory().getItem(event.getHand());
        if (usedItem != null && isArmor(usedItem)) {
            event.setUseItemInHand(Event.Result.DENY);
            event.getPlayer().updateInventory();
        }
    }

    /**
     * Gets whether the given item is a type of armor
     *
     * @param item <p>The item to check if is armor or not</p>
     * @return <p>True if the given item is a type of armor</p>
     */
    public static boolean isArmor(ItemStack item) {
        return EnchantmentTarget.WEARABLE.includes(item) || item.getType() == Material.ELYTRA;
    }

    /**
     * Gets the target-entity the entity is looking at
     *
     * @param entity   <p>The entity looking at something</p>
     * @param entities <p>Entities near the player</p>
     * @param <T>      <p>The type of entity the player is looking at</p>
     * @return <p>The entity the player is looking at, or null if no such entity exists</p>
     */
    private static <T extends Entity> T getTarget(final Entity entity, final Iterable<T> entities) {
        if (entity == null) {
            return null;
        }
        T target = null;
        final double threshold = 1;
        for (final T other : entities) {
            final Vector n = other.getLocation().toVector().subtract(entity.getLocation().toVector());
            if (entity.getLocation().getDirection().normalize().crossProduct(n).lengthSquared() < threshold &&
                    n.normalize().dot(entity.getLocation().getDirection().normalize()) >= 0) {
                if (target == null ||
                        target.getLocation().distanceSquared(entity.getLocation()) >
                                other.getLocation().distanceSquared(entity.getLocation())) {
                    target = other;
                }
            }
        }
        return target;
    }

}
