package com.mineplex.studio.container;

import com.mineplex.studio.events.ContainerBroadcastChangedItemsEvent;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.slf4j.Logger;

public class ContainerChangeListener {
    private static final Logger LOGGER = LogUtils.getLogger();

    private ContainerChangeListener() {}

    private static boolean hasEventListener() {
        return ContainerBroadcastChangedItemsEvent.getHandlerList().getRegisteredListeners().length > 0;
    }

    private static Int2ObjectMap<ContainerBroadcastChangedItemsEvent.ChangedSlot> getModifiedItems(
            AbstractContainerMenu containerMenu) {
        Int2ObjectMap<ContainerBroadcastChangedItemsEvent.ChangedSlot> modifiedItems = new Int2ObjectArrayMap<>(5);
        for (int index = 0; index < containerMenu.slots.size(); index++) {
            ItemStack currentState = containerMenu.slots.get(index).getItem();
            ItemStack lastKnownState = containerMenu.lastSlots.get(index);

            if (!ItemStack.matches(lastKnownState, currentState)) {
                modifiedItems.put(
                        index,
                        new ContainerBroadcastChangedItemsEvent.ChangedSlot(
                                index,
                                CraftItemStack.asBukkitCopy(lastKnownState),
                                CraftItemStack.asCraftMirror(currentState)));
            }
        }

        return Int2ObjectMaps.unmodifiable(modifiedItems);
    }

    private static void handleChanges(AbstractContainerMenu containerMenu) {
        try {
            if (!hasEventListener()) {
                return;
            }

            final Int2ObjectMap<ContainerBroadcastChangedItemsEvent.ChangedSlot> changedItems =
                    getModifiedItems(containerMenu);
            if (changedItems.isEmpty()) {
                return;
            }

            final ContainerBroadcastChangedItemsEvent event = new ContainerBroadcastChangedItemsEvent(
                    !Bukkit.isPrimaryThread(), containerMenu.getBukkitView(), changedItems);
            event.callEvent();
        } catch (Exception exception) {
            LOGGER.error("Failed to handle container changes", exception);
        }
    }

    public static void onBroadcastChanges(AbstractContainerMenu containerMenu) {
        handleChanges(containerMenu);
    }

    public static void onBroadcastFullState(AbstractContainerMenu containerMenu) {
        handleChanges(containerMenu);
    }
}
