// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.builtin.crafting.window;

import java.lang.invoke.CallSite;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.invoke.MethodHandle;
import java.lang.runtime.SwitchBootstraps;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.google.gson.JsonArray;
import java.util.List;
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.google.gson.JsonElement;
import com.hypixel.hytale.builtin.crafting.CraftingPlugin;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.asset.type.item.config.BlockGroup;
import com.hypixel.hytale.server.core.universe.world.SoundUtil;
import com.hypixel.hytale.protocol.SoundCategory;
import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent;
import com.hypixel.hytale.protocol.ItemSoundEvent;
import com.hypixel.hytale.server.core.asset.type.itemsound.config.ItemSoundSet;
import com.hypixel.hytale.server.core.inventory.MaterialQuantity;
import com.hypixel.hytale.protocol.packets.window.ChangeBlockAction;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.protocol.packets.window.CraftRecipeAction;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.protocol.packets.window.SelectSlotAction;
import java.util.Objects;
import com.hypixel.hytale.builtin.crafting.component.CraftingManager;
import com.hypixel.hytale.protocol.packets.window.WindowAction;
import com.hypixel.hytale.component.Store;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.protocol.BenchRequirement;
import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe;
import it.unimi.dsi.fastutil.objects.ObjectList;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.StructuralCraftingBench;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.inventory.container.filter.FilterType;
import com.hypixel.hytale.server.core.inventory.container.filter.FilterActionType;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.hypixel.hytale.protocol.packets.window.WindowType;
import com.hypixel.hytale.builtin.crafting.state.BenchState;
import javax.annotation.Nullable;
import com.hypixel.hytale.event.EventRegistration;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer;
import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer;
import com.hypixel.hytale.server.core.entity.entities.player.windows.ItemContainerWindow;

public class StructuralCraftingWindow extends CraftingWindow implements ItemContainerWindow
{
    private static final int MAX_OPTIONS = 64;
    private final SimpleItemContainer inputContainer;
    private final SimpleItemContainer optionsContainer;
    private final CombinedItemContainer combinedItemContainer;
    private final Int2ObjectMap<String> optionSlotToRecipeMap;
    private int selectedSlot;
    @Nullable
    private EventRegistration inventoryRegistration;
    
    public StructuralCraftingWindow(final BenchState benchState) {
        super(WindowType.StructuralCrafting, benchState);
        this.optionSlotToRecipeMap = new Int2ObjectOpenHashMap<String>();
        (this.inputContainer = new SimpleItemContainer((short)1)).registerChangeEvent(e -> this.updateRecipes());
        this.inputContainer.setSlotFilter(FilterActionType.ADD, (short)0, this::isValidInput);
        (this.optionsContainer = new SimpleItemContainer((short)64)).setGlobalFilter(FilterType.DENY_ALL);
        this.combinedItemContainer = new CombinedItemContainer(new ItemContainer[] { this.inputContainer, this.optionsContainer });
        this.windowData.addProperty("selected", this.selectedSlot);
        final StructuralCraftingBench structuralBench = (StructuralCraftingBench)this.bench;
        this.windowData.addProperty("allowBlockGroupCycling", structuralBench.shouldAllowBlockGroupCycling());
        this.windowData.addProperty("alwaysShowInventoryHints", structuralBench.shouldAlwaysShowInventoryHints());
    }
    
    private boolean isValidInput(final FilterActionType filterActionType, final ItemContainer itemContainer, final short i, final ItemStack itemStack) {
        if (filterActionType != FilterActionType.ADD) {
            return true;
        }
        final ObjectList<CraftingRecipe> matchingRecipes = this.getMatchingRecipes(itemStack);
        return matchingRecipes != null && !matchingRecipes.isEmpty();
    }
    
    private static void sortRecipes(final ObjectList<CraftingRecipe> matching, final StructuralCraftingBench structuralBench) {
        matching.sort((a, b) -> {
            final boolean aHasHeaderCategory = hasHeaderCategory(structuralBench, a);
            final boolean bHasHeaderCategory = hasHeaderCategory(structuralBench, b);
            if (aHasHeaderCategory != bHasHeaderCategory) {
                return aHasHeaderCategory ? -1 : 1;
            }
            else {
                final int categoryA = getSortingPriority(structuralBench, a);
                final int categoryB = getSortingPriority(structuralBench, b);
                final int categoryCompare = Integer.compare(categoryA, categoryB);
                return (categoryCompare != 0) ? categoryCompare : a.getId().compareTo(b.getId());
            }
        });
    }
    
    private static boolean hasHeaderCategory(final StructuralCraftingBench bench, final CraftingRecipe recipe) {
        for (final BenchRequirement requirement : recipe.getBenchRequirement()) {
            if (requirement.type == bench.getType() && requirement.id.equals(bench.getId()) && requirement.categories != null) {
                for (final String category : requirement.categories) {
                    if (bench.isHeaderCategory(category)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    private static int getSortingPriority(final StructuralCraftingBench bench, final CraftingRecipe recipe) {
        int priority = Integer.MAX_VALUE;
        for (final BenchRequirement requirement : recipe.getBenchRequirement()) {
            if (requirement.type == bench.getType() && requirement.id.equals(bench.getId()) && requirement.categories != null) {
                for (final String category : requirement.categories) {
                    priority = Math.min(priority, bench.getCategoryIndex(category));
                }
                break;
            }
        }
        return priority;
    }
    
    @Override
    public void handleAction(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, @Nonnull final WindowAction action) {
        final CraftingManager craftingManager = store.getComponent(ref, CraftingManager.getComponentType());
        Objects.requireNonNull(action);
        switch (/* invokedynamic(!) */ProcyonInvokeDynamicHelper_15.invoke(action, false)) {
            case 0: {
                final SelectSlotAction selectAction = (SelectSlotAction)action;
                final int newSlot = MathUtil.clamp(selectAction.slot, 0, this.optionsContainer.getCapacity());
                if (newSlot != this.selectedSlot) {
                    this.selectedSlot = newSlot;
                    this.windowData.addProperty("selected", this.selectedSlot);
                    this.invalidate();
                }
                break;
            }
            case 1: {
                final CraftRecipeAction craftAction = (CraftRecipeAction)action;
                final ItemStack output = this.optionsContainer.getItemStack((short)this.selectedSlot);
                if (output != null) {
                    final int quantity = craftAction.quantity;
                    final String recipeId = this.optionSlotToRecipeMap.get(this.selectedSlot);
                    if (recipeId == null) {
                        return;
                    }
                    final CraftingRecipe recipe = CraftingRecipe.getAssetMap().getAsset(recipeId);
                    if (recipe == null) {
                        return;
                    }
                    final MaterialQuantity primaryOutput = recipe.getPrimaryOutput();
                    final String primaryOutputItemId = primaryOutput.getItemId();
                    if (primaryOutputItemId != null) {
                        final Item primaryOutputItem = Item.getAssetMap().getAsset(primaryOutputItemId);
                        if (primaryOutputItem != null) {
                            this.playCraftSound(ref, store, primaryOutputItem);
                        }
                    }
                    craftingManager.queueCraft(ref, store, this, 0, recipe, quantity, this.inputContainer, CraftingManager.InputRemovalType.ORDERED);
                    this.invalidate();
                }
                break;
            }
            case 2: {
                final ChangeBlockAction changeBlockAction = (ChangeBlockAction)action;
                if (((StructuralCraftingBench)this.bench).shouldAllowBlockGroupCycling()) {
                    this.changeBlockType(ref, changeBlockAction.down, store);
                    break;
                }
                break;
            }
        }
    }
    
    private void playCraftSound(final Ref<EntityStore> ref, final Store<EntityStore> store, final Item item) {
        final ItemSoundSet soundSet = ItemSoundSet.getAssetMap().getAsset(item.getItemSoundSetIndex());
        if (soundSet == null) {
            return;
        }
        final String dragSound = soundSet.getSoundEventIds().get(ItemSoundEvent.Drop);
        if (dragSound == null) {
            return;
        }
        final int dragSoundIndex = SoundEvent.getAssetMap().getIndex(dragSound);
        if (dragSoundIndex == 0) {
            return;
        }
        SoundUtil.playSoundEvent2d(ref, dragSoundIndex, SoundCategory.UI, store);
    }
    
    private void changeBlockType(@Nonnull final Ref<EntityStore> ref, final boolean down, @Nonnull final Store<EntityStore> store) {
        final ItemStack item = this.inputContainer.getItemStack((short)0);
        if (item == null) {
            return;
        }
        final BlockGroup set = BlockGroup.findItemGroup(item.getItem());
        if (set == null) {
            return;
        }
        int currentIndex = -1;
        for (int i = 0; i < set.size(); ++i) {
            if (set.get(i).equals(item.getItem().getId())) {
                currentIndex = i;
                break;
            }
        }
        if (currentIndex == -1) {
            return;
        }
        int newIndex;
        if (down) {
            newIndex = (currentIndex - 1 + set.size()) % set.size();
        }
        else {
            newIndex = (currentIndex + 1) % set.size();
        }
        final String next = set.get(newIndex);
        final Item desiredItem = Item.getAssetMap().getAsset(next);
        if (desiredItem == null) {
            return;
        }
        this.inputContainer.replaceItemStackInSlot((short)0, item, new ItemStack(next, item.getQuantity()));
        this.playCraftSound(ref, store, desiredItem);
    }
    
    @Nonnull
    @Override
    public ItemContainer getItemContainer() {
        return this.combinedItemContainer;
    }
    
    public boolean onOpen0(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store) {
        super.onOpen0(ref, store);
        final Player playerComponent = store.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final Inventory inventory = playerComponent.getInventory();
        this.inventoryRegistration = inventory.getCombinedHotbarFirst().registerChangeEvent(event -> {
            this.windowData.add("inventoryHints", CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, inventory.getCombinedHotbarFirst()));
            this.invalidate();
            return;
        });
        this.windowData.add("inventoryHints", CraftingManager.generateInventoryHints(CraftingPlugin.getBenchRecipes(this.bench), 0, inventory.getCombinedHotbarFirst()));
        return true;
    }
    
    @Override
    public void onClose0(@Nonnull final Ref<EntityStore> ref, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        super.onClose0(ref, componentAccessor);
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final List<ItemStack> itemStacks = this.inputContainer.dropAllItemStacks();
        SimpleItemContainer.addOrDropItemStacks(componentAccessor, ref, playerComponent.getInventory().getCombinedHotbarFirst(), itemStacks);
        final CraftingManager craftingManagerComponent = componentAccessor.getComponent(ref, CraftingManager.getComponentType());
        assert craftingManagerComponent != null;
        craftingManagerComponent.cancelAllCrafting(ref, componentAccessor);
        if (this.inventoryRegistration != null) {
            this.inventoryRegistration.unregister();
            this.inventoryRegistration = null;
        }
    }
    
    private void updateRecipes() {
        this.invalidate();
        this.optionsContainer.clear();
        this.optionSlotToRecipeMap.clear();
        final ItemStack inputStack = this.inputContainer.getItemStack((short)0);
        final ObjectList<CraftingRecipe> matchingRecipes = this.getMatchingRecipes(inputStack);
        if (matchingRecipes == null) {
            return;
        }
        final StructuralCraftingBench structuralBench = (StructuralCraftingBench)this.bench;
        sortRecipes(matchingRecipes, structuralBench);
        int dividerIndex;
        for (dividerIndex = 0; dividerIndex < matchingRecipes.size(); ++dividerIndex) {
            final CraftingRecipe recipe = matchingRecipes.get(dividerIndex);
            if (!hasHeaderCategory(structuralBench, recipe)) {
                break;
            }
        }
        this.windowData.addProperty("dividerIndex", dividerIndex);
        this.optionsContainer.clear();
        short index = 0;
        for (int i = 0, bound = matchingRecipes.size(); i < bound; ++i) {
            final CraftingRecipe match = matchingRecipes.get(i);
            for (final BenchRequirement requirement : match.getBenchRequirement()) {
                if (requirement.type == this.bench.getType()) {
                    if (requirement.id.equals(this.bench.getId())) {
                        final List<ItemStack> output = CraftingManager.getOutputItemStacks(match);
                        this.optionsContainer.setItemStackForSlot(index, output.getFirst(), false);
                        this.optionSlotToRecipeMap.put(index, match.getId());
                        ++index;
                    }
                }
            }
        }
        final JsonArray optionSlotRecipes = new JsonArray();
        for (int j = 0; j < this.optionsContainer.getCapacity(); ++j) {
            final String recipeId = this.optionSlotToRecipeMap.get(j);
            if (recipeId != null) {
                optionSlotRecipes.add(recipeId);
            }
        }
        this.windowData.add("optionSlotRecipes", optionSlotRecipes);
    }
    
    @Nullable
    private ObjectList<CraftingRecipe> getMatchingRecipes(@Nullable final ItemStack inputStack) {
        if (inputStack == null) {
            return null;
        }
        final List<CraftingRecipe> recipes = CraftingPlugin.getBenchRecipes(this.bench.getType(), this.bench.getId());
        if (recipes.isEmpty()) {
            return null;
        }
        final ObjectList<CraftingRecipe> matchingRecipes = new ObjectArrayList<CraftingRecipe>();
        for (int i = 0, bound = recipes.size(); i < bound; ++i) {
            final CraftingRecipe recipe = recipes.get(i);
            final List<MaterialQuantity> inputMaterials = CraftingManager.getInputMaterials(recipe);
            if (inputMaterials.size() == 1) {
                if (CraftingManager.matches(inputMaterials.getFirst(), inputStack)) {
                    matchingRecipes.add(recipe);
                }
            }
        }
        if (matchingRecipes.isEmpty()) {
            return null;
        }
        return matchingRecipes;
    }
    
    // This helper class was generated by Procyon to approximate the behavior of an
    // 'invokedynamic' instruction that it doesn't know how to interpret.
    private static final class ProcyonInvokeDynamicHelper_15
    {
        private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
        private static MethodHandle handle;
        private static volatile int fence;
        
        private static MethodHandle handle() {
            final MethodHandle handle = ProcyonInvokeDynamicHelper_15.handle;
            if (handle != null)
                return handle;
            return ProcyonInvokeDynamicHelper_15.ensureHandle();
        }
        
        private static MethodHandle ensureHandle() {
            ProcyonInvokeDynamicHelper_15.fence = 0;
            MethodHandle handle = ProcyonInvokeDynamicHelper_15.handle;
            if (handle == null) {
                MethodHandles.Lookup lookup = ProcyonInvokeDynamicHelper_15.LOOKUP;
                try {
                    handle = ((CallSite)SwitchBootstraps.typeSwitch(lookup, "typeSwitch", MethodType.methodType(int.class, Object.class, int.class), SelectSlotAction.class, CraftRecipeAction.class, ChangeBlockAction.class)).dynamicInvoker();
                }
                catch (Throwable t) {
                    throw new UndeclaredThrowableException(t);
                }
                ProcyonInvokeDynamicHelper_15.fence = 1;
                ProcyonInvokeDynamicHelper_15.handle = handle;
                ProcyonInvokeDynamicHelper_15.fence = 0;
            }
            return handle;
        }
        
        private static int invoke(Object p0, int p1) {
            try {
                return ProcyonInvokeDynamicHelper_15.handle().invokeExact(p0, p1);
            }
            catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
    }
}
