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

package com.hypixel.hytale.server.core.modules.blockset;

import com.hypixel.hytale.common.util.StringUtil;
import java.util.logging.Level;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import java.util.function.Consumer;
import java.util.Map;
import java.util.Objects;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.core.modules.blockset.commands.BlockSetCommand;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import javax.annotation.Nonnull;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

@Deprecated(forRemoval = true)
public class BlockSetModule extends JavaPlugin
{
    public static final PluginManifest MANIFEST;
    private static BlockSetModule INSTANCE;
    @Nonnull
    private Int2ObjectMap<IntSet> flattenedBlockSets;
    @Nonnull
    private Int2ObjectMap<IntSet> unmodifiableFlattenedBlockSets;
    private BlockSetLookupTable blockSetLookupTable;
    
    public BlockSetModule(@Nonnull final JavaPluginInit module) {
        super(module);
        this.flattenedBlockSets = new Int2ObjectOpenHashMap<IntSet>();
        this.unmodifiableFlattenedBlockSets = Int2ObjectMaps.unmodifiable((Int2ObjectMap<? extends IntSet>)this.flattenedBlockSets);
        BlockSetModule.INSTANCE = this;
    }
    
    @Override
    protected void setup() {
        this.getCommandRegistry().registerCommand(new BlockSetCommand(this));
        this.getEventRegistry().register(LoadedAssetsEvent.class, BlockType.class, this::onBlockTypesChanged);
        this.getEventRegistry().register(LoadedAssetsEvent.class, BlockSet.class, this::onBlockSetsChanged);
    }
    
    private void onBlockTypesChanged(@Nonnull final LoadedAssetsEvent<String, BlockType, BlockTypeAssetMap<String, BlockType>> event) {
        this.blockSetLookupTable = new BlockSetLookupTable(event.getAssetMap().getAssetMap());
        this.flattenedBlockSets = this.flattenBlockSets(this.blockSetLookupTable);
        this.unmodifiableFlattenedBlockSets = Int2ObjectMaps.unmodifiable((Int2ObjectMap<? extends IntSet>)this.flattenedBlockSets);
    }
    
    private void onBlockSetsChanged(final LoadedAssetsEvent<String, BlockSet, DefaultAssetMap<String, BlockSet>> event) {
        this.blockSetLookupTable = new BlockSetLookupTable(BlockType.getAssetMap().getAssetMap());
        this.flattenedBlockSets = this.flattenBlockSets(this.blockSetLookupTable);
        this.unmodifiableFlattenedBlockSets = Int2ObjectMaps.unmodifiable((Int2ObjectMap<? extends IntSet>)this.flattenedBlockSets);
    }
    
    @Nonnull
    private Int2ObjectMap<IntSet> flattenBlockSets(@Nonnull final BlockSetLookupTable lookupTable) {
        final Int2ObjectOpenHashMap<IntSet> flattenedSets = new Int2ObjectOpenHashMap<IntSet>();
        if (!lookupTable.isEmpty()) {
            BlockSet.getAssetMap().getAssetMap().forEach((s, blockSet) -> {
                final int index = BlockSet.getAssetMap().getIndex(s);
                if (index == Integer.MIN_VALUE) {
                    throw new IllegalArgumentException("Unknown key! " + s);
                }
                else {
                    final IntSet tIntSet = flattenedSets.get(index);
                    if (tIntSet == null) {
                        final IntOpenHashSet set = this.createSet(blockSet, lookupTable, flattenedSets);
                        set.trim();
                        flattenedSets.put(index, set);
                    }
                    return;
                }
            });
        }
        return Int2ObjectMaps.unmodifiable((Int2ObjectMap<? extends IntSet>)flattenedSets);
    }
    
    @Nonnull
    private IntOpenHashSet createSet(@Nonnull final BlockSet blockSet, @Nonnull final BlockSetLookupTable lookupTable, @Nonnull final Int2ObjectMap<IntSet> flattenedSets) {
        final IntOpenHashSet result = new IntOpenHashSet();
        final String parent = blockSet.getParent();
        if (parent != null && !parent.isEmpty()) {
            final int parentIndex = BlockSet.getAssetMap().getIndex(parent);
            if (parentIndex == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + parent);
            }
            result.addAll(flattenedSets.computeIfAbsent(parentIndex, s -> {
                final IntOpenHashSet set = this.createSet(parent, lookupTable, flattenedSets);
                set.trim();
                return set;
            }));
        }
        if (blockSet.isIncludeAll()) {
            lookupTable.addAll(result);
        }
        final String[] includeBlockTypes = blockSet.getIncludeBlockTypes();
        final Object2ObjectMap<String, IntSet> blockNameIdMap = lookupTable.getBlockNameIdMap();
        final String typeString = "block name";
        final IntOpenHashSet obj = result;
        Objects.requireNonNull(obj);
        this.consume(includeBlockTypes, blockNameIdMap, typeString, (Consumer<IntSet>)obj::addAll);
        final String[] includeBlockGroups = blockSet.getIncludeBlockGroups();
        final Object2ObjectMap<String, IntSet> groupNameIdMap = lookupTable.getGroupNameIdMap();
        final String typeString2 = "group name";
        final IntOpenHashSet obj2 = result;
        Objects.requireNonNull(obj2);
        this.consume(includeBlockGroups, groupNameIdMap, typeString2, (Consumer<IntSet>)obj2::addAll);
        final String[] includeHitboxTypes = blockSet.getIncludeHitboxTypes();
        final Object2ObjectMap<String, IntSet> hitboxNameIdMap = lookupTable.getHitboxNameIdMap();
        final String typeString3 = "hitbox name";
        final IntOpenHashSet obj3 = result;
        Objects.requireNonNull(obj3);
        this.consume(includeHitboxTypes, hitboxNameIdMap, typeString3, (Consumer<IntSet>)obj3::addAll);
        final String[] excludeBlockTypes = blockSet.getExcludeBlockTypes();
        final Object2ObjectMap<String, IntSet> blockNameIdMap2 = lookupTable.getBlockNameIdMap();
        final String typeString4 = "block name";
        final IntOpenHashSet obj4 = result;
        Objects.requireNonNull(obj4);
        this.consume(excludeBlockTypes, blockNameIdMap2, typeString4, (Consumer<IntSet>)obj4::removeAll);
        final String[] excludeBlockGroups = blockSet.getExcludeBlockGroups();
        final Object2ObjectMap<String, IntSet> groupNameIdMap2 = lookupTable.getGroupNameIdMap();
        final String typeString5 = "group name";
        final IntOpenHashSet obj5 = result;
        Objects.requireNonNull(obj5);
        this.consume(excludeBlockGroups, groupNameIdMap2, typeString5, (Consumer<IntSet>)obj5::removeAll);
        final String[] excludeHitboxTypes = blockSet.getExcludeHitboxTypes();
        final Object2ObjectMap<String, IntSet> hitboxNameIdMap2 = lookupTable.getHitboxNameIdMap();
        final String typeString6 = "hitbox name";
        final IntOpenHashSet obj6 = result;
        Objects.requireNonNull(obj6);
        this.consume(excludeHitboxTypes, hitboxNameIdMap2, typeString6, (Consumer<IntSet>)obj6::removeAll);
        final String[][] includeCategories = blockSet.getIncludeCategories();
        final IntOpenHashSet obj7 = result;
        Objects.requireNonNull(obj7);
        this.consume(includeCategories, lookupTable, (Consumer<IntSet>)obj7::addAll);
        final String[][] excludeCategories = blockSet.getExcludeCategories();
        final IntOpenHashSet obj8 = result;
        Objects.requireNonNull(obj8);
        this.consume(excludeCategories, lookupTable, (Consumer<IntSet>)obj8::removeAll);
        return result;
    }
    
    private void consume(@Nullable final String[] values, @Nonnull final Map<String, IntSet> map, final String typeString, @Nonnull final Consumer<IntSet> addAll) {
        if (values != null) {
            for (final String s : values) {
                this.consumeEntry(s, addAll, map, typeString);
            }
        }
    }
    
    private void consume(@Nullable final String[][] values, @Nonnull final BlockSetLookupTable lookupTable, @Nonnull final Consumer<IntSet> addAll) {
        if (values != null) {
            for (final String[] s : values) {
                this.consumeCategory(s, addAll, lookupTable);
            }
        }
    }
    
    @Nonnull
    private IntOpenHashSet createSet(final String name, @Nonnull final BlockSetLookupTable lookupTable, @Nonnull final Int2ObjectMap<IntSet> flattenedSets) {
        final Map<String, BlockSet> blockSets = BlockSet.getAssetMap().getAssetMap();
        final BlockSet blockSet = blockSets.get(name);
        if (blockSet == null) {
            this.getLogger().at(Level.WARNING).log("Creating block sets: Failed to find block set '%s'", name);
            return new IntOpenHashSet();
        }
        return this.createSet(blockSet, lookupTable, flattenedSets);
    }
    
    private void consumeCategory(@Nullable final String[] categories, @Nonnull final Consumer<IntSet> predicate, @Nonnull final BlockSetLookupTable lookupTable) {
        if (categories == null || categories.length == 0) {
            return;
        }
        final Map<String, IntSet> categoryIdMap = lookupTable.getCategoryIdMap();
        IntSet catSet = categoryIdMap.get(categories[0]);
        if (catSet == null) {
            this.getLogger().at(Level.WARNING).log("Creating block sets: '%s' does not match any block category", categories[0]);
            return;
        }
        if (categories.length == 1) {
            predicate.accept(catSet);
            return;
        }
        final IntSet andSet = new IntOpenHashSet(catSet);
        for (int i = 1; i < categories.length; ++i) {
            catSet = categoryIdMap.get(categories[i]);
            if (catSet == null) {
                this.getLogger().at(Level.WARNING).log("Creating block sets: '%s' does not match any block category", categories[i]);
                return;
            }
            andSet.removeAll(catSet);
            if (andSet.isEmpty()) {
                return;
            }
        }
        predicate.accept(andSet);
    }
    
    private void consumeEntry(@Nonnull final String name, @Nonnull final Consumer<IntSet> predicate, @Nonnull final Map<String, IntSet> nameIdMap, final String typeString) {
        if (StringUtil.isGlobPattern(name)) {
            final boolean[] found = { false };
            nameIdMap.forEach((s, tIntSet) -> {
                if (StringUtil.isGlobMatching(name, s)) {
                    predicate.accept(tIntSet);
                    found[0] = true;
                }
                return;
            });
            if (!found[0]) {
                this.getLogger().at(Level.FINE).log("Creating block sets: '%s' does not match any %s", name, typeString);
            }
        }
        else {
            final IntSet ids = nameIdMap.get(name);
            if (ids == null) {
                this.getLogger().at(Level.WARNING).log("Creating block sets: Failed to find %s '%s'", typeString, name);
            }
            else {
                predicate.accept(ids);
            }
        }
    }
    
    @Nonnull
    public Int2ObjectMap<IntSet> getBlockSets() {
        return this.unmodifiableFlattenedBlockSets;
    }
    
    public boolean blockInSet(final int set, final int blockId) {
        final IntSet s = this.flattenedBlockSets.get(set);
        return s != null && s.contains(blockId);
    }
    
    public boolean blockInSet(final int set, @Nullable final BlockType blockType) {
        return blockType != null && this.blockInSet(set, blockType.getId());
    }
    
    public boolean blockInSet(final int set, @Nullable final String blockTypeKey) {
        if (blockTypeKey == null) {
            return false;
        }
        final IntSet s = this.flattenedBlockSets.get(set);
        if (s == null) {
            return false;
        }
        final int index = BlockType.getAssetMap().getIndex(blockTypeKey);
        if (index == Integer.MIN_VALUE) {
            throw new IllegalArgumentException("Unknown key! " + blockTypeKey);
        }
        return s.contains(index);
    }
    
    public static BlockSetModule getInstance() {
        return BlockSetModule.INSTANCE;
    }
    
    static {
        MANIFEST = PluginManifest.corePlugin(BlockSetModule.class).build();
    }
}
