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

package com.hypixel.hytale.builtin.crafting;

import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.packets.interface_.UpdateKnownRecipes;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import com.hypixel.hytale.server.core.entity.entities.player.data.PlayerConfigData;
import java.util.HashSet;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.common.util.ArrayUtil;
import com.hypixel.hytale.protocol.BenchRequirement;
import java.util.function.Function;
import java.util.Iterator;
import java.util.Collection;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.server.core.universe.world.meta.BlockStateRegistry;
import com.hypixel.hytale.component.ComponentRegistryProxy;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.builtin.crafting.commands.RecipeCommand;
import com.hypixel.hytale.server.core.command.system.CommandManager;
import com.hypixel.hytale.builtin.crafting.interaction.LearnRecipeInteraction;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent;
import com.hypixel.hytale.server.core.asset.type.item.config.CraftingRecipe;
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
import com.hypixel.hytale.builtin.crafting.window.FieldCraftingWindow;
import com.hypixel.hytale.protocol.packets.window.WindowType;
import com.hypixel.hytale.server.core.entity.entities.player.windows.Window;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.builtin.crafting.state.ProcessingBenchState;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.Bench;
import com.hypixel.hytale.protocol.BenchType;
import com.hypixel.hytale.builtin.crafting.interaction.OpenProcessingBenchInteraction;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.builtin.crafting.system.PlayerCraftingSystems;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import java.util.List;
import com.hypixel.hytale.builtin.crafting.interaction.OpenBenchPageInteraction;
import com.hypixel.hytale.assetstore.AssetRegistry;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.protocol.ItemResourceType;
import com.hypixel.hytale.server.core.inventory.MaterialQuantity;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.bench.BenchUpgradeRequirement;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.builtin.crafting.state.BenchState;
import javax.annotation.Nullable;
import java.util.Set;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.builtin.crafting.component.CraftingManager;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentType;
import java.util.Map;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class CraftingPlugin extends JavaPlugin
{
    private static CraftingPlugin instance;
    private static final Map<String, BenchRecipeRegistry> registries;
    private static final Map<String, String[]> itemGeneratedRecipes;
    private ComponentType<EntityStore, CraftingManager> craftingManagerComponentType;
    
    public CraftingPlugin(@Nonnull final JavaPluginInit init) {
        super(init);
        CraftingPlugin.instance = this;
    }
    
    @Nullable
    public static Set<String> getAvailableRecipesForCategory(final String benchId, final String benchCategoryId) {
        final BenchRecipeRegistry benchRecipeRegistry = CraftingPlugin.registries.get(benchId);
        if (benchRecipeRegistry == null) {
            return null;
        }
        return benchRecipeRegistry.getRecipesForCategory(benchCategoryId);
    }
    
    public static boolean isValidCraftingMaterialForBench(final BenchState benchState, final ItemStack itemStack) {
        final BenchRecipeRegistry benchRecipeRegistry = CraftingPlugin.registries.get(benchState.getBench().getId());
        return benchRecipeRegistry != null && benchRecipeRegistry.isValidCraftingMaterial(itemStack);
    }
    
    public static boolean isValidUpgradeMaterialForBench(final BenchState benchState, final ItemStack itemStack) {
        final BenchUpgradeRequirement nextLevelUpgradeMaterials = benchState.getNextLevelUpgradeMaterials();
        if (nextLevelUpgradeMaterials == null) {
            return false;
        }
        for (final MaterialQuantity upgradeMaterial : nextLevelUpgradeMaterials.getInput()) {
            if (itemStack.getItemId().equals(upgradeMaterial.getItemId())) {
                return true;
            }
            final ItemResourceType[] resourceTypeId = itemStack.getItem().getResourceTypes();
            if (resourceTypeId != null) {
                for (final ItemResourceType resTypeId : resourceTypeId) {
                    if (resTypeId.id.equals(upgradeMaterial.getResourceTypeId())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    @Override
    protected void setup() {
        AssetRegistry.getAssetStore((Class<OpenBenchPageInteraction>)Interaction.class).loadAssets("Hytale:Hytale", List.of(OpenBenchPageInteraction.SIMPLE_CRAFTING, OpenBenchPageInteraction.DIAGRAM_CRAFTING, OpenBenchPageInteraction.STRUCTURAL_CRAFTING));
        AssetRegistry.getAssetStore(RootInteraction.class).loadAssets("Hytale:Hytale", List.of(OpenBenchPageInteraction.SIMPLE_CRAFTING_ROOT, OpenBenchPageInteraction.DIAGRAM_CRAFTING_ROOT, OpenBenchPageInteraction.STRUCTURAL_CRAFTING_ROOT));
        final ComponentRegistryProxy<EntityStore> entityStoreRegistry = this.getEntityStoreRegistry();
        this.craftingManagerComponentType = entityStoreRegistry.registerComponent(CraftingManager.class, CraftingManager::new);
        entityStoreRegistry.registerSystem(new PlayerCraftingSystems.CraftingTickingSystem(this.craftingManagerComponentType));
        entityStoreRegistry.registerSystem(new PlayerCraftingSystems.CraftingHolderSystem(this.craftingManagerComponentType));
        entityStoreRegistry.registerSystem(new PlayerCraftingSystems.CraftingRefSystem(this.craftingManagerComponentType));
        this.getCodecRegistry(Interaction.CODEC).register("OpenBenchPage", OpenBenchPageInteraction.class, OpenBenchPageInteraction.CODEC).register("OpenProcessingBench", OpenProcessingBenchInteraction.class, OpenProcessingBenchInteraction.CODEC);
        Bench.registerRootInteraction(BenchType.Crafting, OpenBenchPageInteraction.SIMPLE_CRAFTING_ROOT);
        Bench.registerRootInteraction(BenchType.DiagramCrafting, OpenBenchPageInteraction.DIAGRAM_CRAFTING_ROOT);
        Bench.registerRootInteraction(BenchType.StructuralCrafting, OpenBenchPageInteraction.STRUCTURAL_CRAFTING_ROOT);
        final BlockStateRegistry blockStateRegistry = this.getBlockStateRegistry();
        blockStateRegistry.registerBlockState(ProcessingBenchState.class, "processingBench", ProcessingBenchState.CODEC);
        blockStateRegistry.registerBlockState(BenchState.class, "crafting", BenchState.CODEC);
        Window.CLIENT_REQUESTABLE_WINDOW_TYPES.put(WindowType.PocketCrafting, FieldCraftingWindow::new);
        this.getEventRegistry().register(LoadedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeLoad);
        this.getEventRegistry().register(RemovedAssetsEvent.class, CraftingRecipe.class, CraftingPlugin::onRecipeRemove);
        this.getEventRegistry().register(LoadedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetLoad);
        this.getEventRegistry().register(RemovedAssetsEvent.class, Item.class, CraftingPlugin::onItemAssetRemove);
        Interaction.CODEC.register("LearnRecipe", LearnRecipeInteraction.class, LearnRecipeInteraction.CODEC);
        CommandManager.get().registerSystemCommand(new RecipeCommand());
        entityStoreRegistry.registerSystem(new PlayerAddedSystem());
    }
    
    private static void onItemAssetLoad(final LoadedAssetsEvent<String, Item, DefaultAssetMap<String, Item>> event) {
        final List<CraftingRecipe> recipesToLoad = new ObjectArrayList<CraftingRecipe>();
        for (final Item item : event.getLoadedAssets().values()) {
            if (!item.hasRecipesToGenerate()) {
                continue;
            }
            final List<CraftingRecipe> generatedRecipes = new ObjectArrayList<CraftingRecipe>();
            item.collectRecipesToGenerate(generatedRecipes);
            final List<String> generatedIds = new ObjectArrayList<String>();
            for (final CraftingRecipe generatedRecipe : generatedRecipes) {
                final String id = generatedRecipe.getId();
                generatedIds.add(id);
            }
            CraftingPlugin.itemGeneratedRecipes.put(item.getId(), generatedIds.toArray(String[]::new));
            recipesToLoad.addAll(generatedRecipes);
        }
        if (!recipesToLoad.isEmpty()) {
            CraftingRecipe.getAssetStore().loadAssets("Hytale:Hytale", recipesToLoad);
        }
    }
    
    private static void onItemAssetRemove(@Nonnull final RemovedAssetsEvent<String, Item, DefaultAssetMap<String, Item>> event) {
        for (final String id : event.getRemovedAssets()) {
            final String[] generatedRecipes = CraftingPlugin.itemGeneratedRecipes.get(id);
            if (generatedRecipes == null) {
                continue;
            }
            CraftingRecipe.getAssetStore().removeAssets(List.of(generatedRecipes));
        }
    }
    
    private static void onRecipeLoad(final LoadedAssetsEvent<String, CraftingRecipe, DefaultAssetMap<String, CraftingRecipe>> event) {
        for (final CraftingRecipe recipe : event.getLoadedAssets().values()) {
            for (final BenchRecipeRegistry registry : CraftingPlugin.registries.values()) {
                registry.removeRecipe(recipe.getId());
            }
            if (recipe.getBenchRequirement() == null) {
                continue;
            }
            for (final BenchRequirement benchRequirement : recipe.getBenchRequirement()) {
                final BenchRecipeRegistry benchRecipeRegistry = CraftingPlugin.registries.computeIfAbsent(benchRequirement.id, BenchRecipeRegistry::new);
                benchRecipeRegistry.addRecipe(benchRequirement, recipe);
            }
        }
        computeBenchRecipeRegistries();
    }
    
    private static void onRecipeRemove(final RemovedAssetsEvent<String, CraftingRecipe, DefaultAssetMap<String, CraftingRecipe>> event) {
        for (final String removedRecipeId : event.getRemovedAssets()) {
            for (final BenchRecipeRegistry registry : CraftingPlugin.registries.values()) {
                registry.removeRecipe(removedRecipeId);
            }
        }
        computeBenchRecipeRegistries();
    }
    
    private static void computeBenchRecipeRegistries() {
        for (final BenchRecipeRegistry registry : CraftingPlugin.registries.values()) {
            registry.recompute();
        }
    }
    
    @Nonnull
    public static List<CraftingRecipe> getBenchRecipes(@Nonnull final Bench bench) {
        return getBenchRecipes(bench.getType(), bench.getId());
    }
    
    @Nonnull
    public static List<CraftingRecipe> getBenchRecipes(final BenchType benchType, final String name) {
        return getBenchRecipes(benchType, name, null);
    }
    
    @Nonnull
    public static List<CraftingRecipe> getBenchRecipes(final BenchType benchType, final String benchId, @Nullable final String category) {
        final BenchRecipeRegistry registry = CraftingPlugin.registries.get(benchId);
        if (registry == null) {
            return List.of();
        }
        final List<CraftingRecipe> list = new ObjectArrayList<CraftingRecipe>();
        for (final CraftingRecipe recipe : registry.getAllRecipes()) {
            final BenchRequirement[] benchRequirement = recipe.getBenchRequirement();
            if (benchRequirement != null) {
                for (final BenchRequirement requirement : benchRequirement) {
                    if (requirement.type == benchType && requirement.id.equals(benchId) && (category == null || hasCategory(recipe, category))) {
                        list.add(recipe);
                        break;
                    }
                }
            }
        }
        return list;
    }
    
    private static boolean hasCategory(@Nonnull final CraftingRecipe recipe, final String category) {
        for (final BenchRequirement benchRequirement : recipe.getBenchRequirement()) {
            if (benchRequirement.categories != null && ArrayUtil.contains(benchRequirement.categories, category)) {
                return true;
            }
        }
        return false;
    }
    
    public static boolean learnRecipe(@Nonnull final Ref<EntityStore> ref, @Nonnull final String recipeId, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
        final Set<String> knownRecipes = new HashSet<String>(playerConfigData.getKnownRecipes());
        if (knownRecipes.add(recipeId)) {
            playerConfigData.setKnownRecipes(knownRecipes);
            sendKnownRecipes(ref, componentAccessor);
            return true;
        }
        return false;
    }
    
    public static boolean forgetRecipe(@Nonnull final Ref<EntityStore> ref, @Nonnull final String itemId, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
        final Set<String> knownRecipes = new ObjectOpenHashSet<String>(playerConfigData.getKnownRecipes());
        if (knownRecipes.remove(itemId)) {
            playerConfigData.setKnownRecipes(knownRecipes);
            sendKnownRecipes(ref, componentAccessor);
            return true;
        }
        return false;
    }
    
    public static void sendKnownRecipes(@Nonnull final Ref<EntityStore> ref, @Nonnull final ComponentAccessor<EntityStore> componentAccessor) {
        final PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType());
        assert playerRefComponent != null;
        final Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final PlayerConfigData playerConfigData = playerComponent.getPlayerConfigData();
        final DefaultAssetMap<String, Item> itemAssetMap = Item.getAssetMap();
        final Map<String, com.hypixel.hytale.protocol.CraftingRecipe> knownRecipes = new Object2ObjectOpenHashMap<String, com.hypixel.hytale.protocol.CraftingRecipe>();
        for (final String id : playerConfigData.getKnownRecipes()) {
            final Item item = itemAssetMap.getAsset(id);
            if (item == null) {
                continue;
            }
            for (final BenchRecipeRegistry registry : CraftingPlugin.registries.values()) {
                final Iterable<String> incomingRecipes = registry.getIncomingRecipesForItem(item.getId());
                for (final String recipeId : incomingRecipes) {
                    final CraftingRecipe recipe = CraftingRecipe.getAssetMap().getAsset(recipeId);
                    if (recipe == null) {
                        continue;
                    }
                    knownRecipes.put(id, recipe.toPacket(id));
                }
            }
        }
        playerRefComponent.getPacketHandler().writeNoCache(new UpdateKnownRecipes(knownRecipes));
    }
    
    public ComponentType<EntityStore, CraftingManager> getCraftingManagerComponentType() {
        return this.craftingManagerComponentType;
    }
    
    public static CraftingPlugin get() {
        return CraftingPlugin.instance;
    }
    
    static {
        registries = new Object2ObjectOpenHashMap<String, BenchRecipeRegistry>();
        itemGeneratedRecipes = new Object2ObjectOpenHashMap<String, String[]>();
    }
    
    public static class PlayerAddedSystem extends RefSystem<EntityStore>
    {
        private static final Query<EntityStore> QUERY;
        
        @Override
        public Query<EntityStore> getQuery() {
            return PlayerAddedSystem.QUERY;
        }
        
        @Override
        public void onEntityAdded(@Nonnull final Ref<EntityStore> ref, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
            CraftingPlugin.sendKnownRecipes(ref, commandBuffer);
        }
        
        @Override
        public void onEntityRemove(@Nonnull final Ref<EntityStore> ref, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store, @Nonnull final CommandBuffer<EntityStore> commandBuffer) {
        }
        
        static {
            QUERY = Archetype.of(Player.getComponentType(), PlayerRef.getComponentType());
        }
    }
}
