package leigh.minecraft.link.bukkit;

import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.mq.Fanout;
import leigh.mecha.util.StringAccumulatorV2;
import leigh.minecraft.link.MinecraftLinkPlugin;
import leigh.minecraft.link.event.ChatMessage;
import leigh.minecraft.link.item.XPBottle;
import net.milkbowl.vault.economy.Economy;
import org.apache.commons.lang.StringUtils;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.RegisteredServiceProvider;

import static org.bukkit.Bukkit.getServer;

/**
 * This class implements a {@link Listener} which instruments activity within a Minecraft server.
 * <p>
 * Since Bukkit does not support filtering of events in listeners (all listeners receive all events),
 * to reduce server load, this class implements all event listeners for Rezrov and then distributes
 * the events to other classes as required. Noted, that it is theoretically possible that some or all
 * Minecraft implementations may cache the reflection results to provide a faster path optimization,
 * we do not know that is the case universally and choose not to rely on it.
 *
 * @author Alex Leigh
 */
public class BukkitEventHandler implements Listener {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(BukkitEventHandler.class);
    private final Fanout<ChatMessage> chatFanout = new Fanout<>();
    private Economy econ;
    private final XPBottle xpBottle;

    public BukkitEventHandler(MinecraftLinkPlugin linkPlugin, XPBottle xpBottle) {
        this.xpBottle = xpBottle;
        RegisteredServiceProvider<Economy> economyProvider =
                getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
        if (economyProvider != null) {
            econ = economyProvider.getProvider();
            for (String bank : econ.getBanks()) {
                linkPlugin.getLogger().info("Bank: " + bank);
            }
        }
    }

    public Fanout<ChatMessage> getChatFanout() {
        return chatFanout;
    }

    @EventHandler
    public void onPlayerItemConsume(PlayerItemConsumeEvent event) {
        logger.info("onPlayerInteract. [cancelled: {}]", event.isCancelled());
        xpBottle.playerConsumeEvent(event);
    }

    @EventHandler
    public void onPlayerChat(PlayerChatEvent event) {
        Player p = event.getPlayer();
        ChatMessage msg = new ChatMessage(p.getUniqueId(), p.getPlayerListName(), event.getMessage());
        new Thread(() -> chatFanout.write(msg)).start();
    }

    @EventHandler
    public void onPlayerDeath(PlayerDeathEvent event) {
        logger.info("Player has died. [p: {}]", event);
        Player player = event.getEntity();

        StringAccumulatorV2 sa = new StringAccumulatorV2(",");

        for (ItemStack item : player.getInventory()) {
            if(item!=null) {
                ItemMeta meta = item.getItemMeta();

                if (meta instanceof Damageable) {
                    short maxDurability = item.getType().getMaxDurability();
                    // 0 is undamaged.
                    int currentDamage = ((Damageable) meta).getDamage();
                    // How many points of damage are left before the item breaks
                    int remaining = maxDurability - currentDamage;
                    // How many points of damage we therefore want to do
                    int todo = (int) (remaining * .55);

                    logger.info("Item: [max: {}] [current: {}] [remaining: {}] [todo: {}]",
                            maxDurability, currentDamage, remaining, todo);

                    // But we are not total assholes
                    if(currentDamage+todo>=maxDurability) todo = maxDurability-1;

                    ((Damageable) meta).setDamage(currentDamage + todo);
                    item.setItemMeta(meta);
                    sa.pushUnique(item.getItemMeta().getDisplayName());
                }
            }
        }

        String things = sa.asString();
        if(!StringUtils.isEmpty(things)) {
            // TODO: Grammar
            player.sendMessage(sa.asString() + " receives 55% damage.");
        }
    }
}