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

package com.hypixel.hytale.server.core.inventory.container;

import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Iterator;
import com.hypixel.hytale.server.core.inventory.transaction.ListTransaction;
import java.util.List;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackSlotTransaction;
import com.hypixel.hytale.server.core.inventory.transaction.ItemStackTransaction;
import com.hypixel.hytale.server.core.entity.ItemUtils;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.fastutil.shorts.Short2ObjectConcurrentHashMap;
import javax.annotation.Nullable;
import java.util.Objects;
import com.hypixel.hytale.server.core.inventory.transaction.ClearTransaction;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.hypixel.hytale.server.core.inventory.container.filter.FilterType;
import com.hypixel.hytale.server.core.inventory.container.filter.SlotFilter;
import com.hypixel.fastutil.ints.Int2ObjectConcurrentHashMap;
import com.hypixel.hytale.server.core.inventory.container.filter.FilterActionType;
import java.util.Map;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import java.util.concurrent.locks.ReadWriteLock;
import com.hypixel.hytale.codec.builder.BuilderCodec;

public class SimpleItemContainer extends ItemContainer
{
    public static final BuilderCodec<SimpleItemContainer> CODEC;
    protected short capacity;
    protected final ReadWriteLock lock;
    protected Short2ObjectMap<ItemStack> items;
    private final Map<FilterActionType, Int2ObjectConcurrentHashMap<SlotFilter>> slotFilters;
    private FilterType globalFilter;
    
    protected SimpleItemContainer() {
        this.lock = new ReentrantReadWriteLock();
        this.slotFilters = new ConcurrentHashMap<FilterActionType, Int2ObjectConcurrentHashMap<SlotFilter>>();
        this.globalFilter = FilterType.ALLOW_ALL;
    }
    
    public SimpleItemContainer(final short capacity) {
        this.lock = new ReentrantReadWriteLock();
        this.slotFilters = new ConcurrentHashMap<FilterActionType, Int2ObjectConcurrentHashMap<SlotFilter>>();
        this.globalFilter = FilterType.ALLOW_ALL;
        if (capacity <= 0) {
            throw new IllegalArgumentException("Capacity is less than or equal zero! " + capacity + " <= 0");
        }
        this.capacity = capacity;
        this.items = new Short2ObjectOpenHashMap<ItemStack>(capacity);
    }
    
    public SimpleItemContainer(@Nonnull final SimpleItemContainer other) {
        this.lock = new ReentrantReadWriteLock();
        this.slotFilters = new ConcurrentHashMap<FilterActionType, Int2ObjectConcurrentHashMap<SlotFilter>>();
        this.globalFilter = FilterType.ALLOW_ALL;
        this.capacity = other.capacity;
        other.lock.readLock().lock();
        try {
            this.items = new Short2ObjectOpenHashMap<ItemStack>(other.items);
        }
        finally {
            other.lock.readLock().unlock();
        }
        this.slotFilters.putAll(other.slotFilters);
        this.globalFilter = other.globalFilter;
    }
    
    @Override
    protected <V> V readAction(@Nonnull final Supplier<V> action) {
        this.lock.readLock().lock();
        try {
            return action.get();
        }
        finally {
            this.lock.readLock().unlock();
        }
    }
    
    @Override
    protected <X, V> V readAction(@Nonnull final Function<X, V> action, final X x) {
        this.lock.readLock().lock();
        try {
            return action.apply(x);
        }
        finally {
            this.lock.readLock().unlock();
        }
    }
    
    @Override
    protected <V> V writeAction(@Nonnull final Supplier<V> action) {
        this.lock.writeLock().lock();
        try {
            return action.get();
        }
        finally {
            this.lock.writeLock().unlock();
        }
    }
    
    @Override
    protected <X, V> V writeAction(@Nonnull final Function<X, V> action, final X x) {
        this.lock.writeLock().lock();
        try {
            return action.apply(x);
        }
        finally {
            this.lock.writeLock().unlock();
        }
    }
    
    @Override
    protected ItemStack internal_getSlot(final short slot) {
        return this.items.get(slot);
    }
    
    @Override
    protected ItemStack internal_setSlot(final short slot, final ItemStack itemStack) {
        if (ItemStack.isEmpty(itemStack)) {
            return this.internal_removeSlot(slot);
        }
        return this.items.put(slot, itemStack);
    }
    
    @Override
    protected ItemStack internal_removeSlot(final short slot) {
        return this.items.remove(slot);
    }
    
    @Override
    protected boolean cantAddToSlot(final short slot, final ItemStack itemStack, final ItemStack slotItemStack) {
        return !this.globalFilter.allowInput() || this.testFilter(FilterActionType.ADD, slot, itemStack);
    }
    
    @Override
    protected boolean cantRemoveFromSlot(final short slot) {
        return !this.globalFilter.allowOutput() || this.testFilter(FilterActionType.REMOVE, slot, null);
    }
    
    @Override
    protected boolean cantDropFromSlot(final short slot) {
        return this.testFilter(FilterActionType.DROP, slot, null);
    }
    
    @Override
    protected boolean cantMoveToSlot(final ItemContainer fromContainer, final short slotFrom) {
        return false;
    }
    
    private boolean testFilter(final FilterActionType actionType, final short slot, final ItemStack itemStack) {
        final Int2ObjectConcurrentHashMap<SlotFilter> map = this.slotFilters.get(actionType);
        if (map == null) {
            return false;
        }
        final SlotFilter filter = map.get(slot);
        return filter != null && !filter.test(actionType, this, slot, itemStack);
    }
    
    @Override
    public short getCapacity() {
        return this.capacity;
    }
    
    @Nonnull
    @Override
    protected ClearTransaction internal_clear() {
        final ItemStack[] itemStacks = new ItemStack[this.getCapacity()];
        for (short i = 0; i < itemStacks.length; ++i) {
            itemStacks[i] = this.items.get(i);
        }
        this.items.clear();
        return new ClearTransaction(true, (short)0, itemStacks);
    }
    
    @Nonnull
    @Override
    public SimpleItemContainer clone() {
        return new SimpleItemContainer(this);
    }
    
    @Override
    public boolean isEmpty() {
        this.lock.readLock().lock();
        try {
            if (this.items.isEmpty()) {
                return true;
            }
        }
        finally {
            this.lock.readLock().unlock();
        }
        return super.isEmpty();
    }
    
    @Override
    public void setGlobalFilter(@Nonnull final FilterType globalFilter) {
        this.globalFilter = Objects.requireNonNull(globalFilter);
    }
    
    @Override
    public void setSlotFilter(final FilterActionType actionType, final short slot, @Nullable final SlotFilter filter) {
        ItemContainer.validateSlotIndex(slot, this.getCapacity());
        if (filter != null) {
            this.slotFilters.computeIfAbsent(actionType, k -> new Int2ObjectConcurrentHashMap()).put(slot, filter);
        }
        else {
            this.slotFilters.computeIfPresent(actionType, (k, map) -> {
                map.remove(slot);
                return map.isEmpty() ? null : map;
            });
        }
    }
    
    @Nullable
    @Override
    public ItemStack getItemStack(final short slot) {
        ItemContainer.validateSlotIndex(slot, this.getCapacity());
        this.lock.readLock().lock();
        try {
            return this.internal_getSlot(slot);
        }
        finally {
            this.lock.readLock().unlock();
        }
    }
    
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof SimpleItemContainer)) {
            return false;
        }
        final SimpleItemContainer that = (SimpleItemContainer)o;
        if (this.capacity != that.capacity) {
            return false;
        }
        this.lock.readLock().lock();
        try {
            return this.items.equals(that.items);
        }
        finally {
            this.lock.readLock().unlock();
        }
    }
    
    @Override
    public int hashCode() {
        this.lock.readLock().lock();
        int result;
        try {
            result = this.items.hashCode();
        }
        finally {
            this.lock.readLock().unlock();
        }
        result = 31 * result + this.capacity;
        return result;
    }
    
    public static ItemContainer getNewContainer(final short capacity) {
        return ItemContainer.getNewContainer(capacity, (Short2ObjectConcurrentHashMap.ShortFunction<ItemContainer>)SimpleItemContainer::new);
    }
    
    public static boolean addOrDropItemStack(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final ItemContainer itemContainer, @Nonnull final ItemStack itemStack) {
        final ItemStackTransaction transaction = itemContainer.addItemStack(itemStack);
        final ItemStack remainder = transaction.getRemainder();
        if (!ItemStack.isEmpty(remainder)) {
            ItemUtils.dropItem(ref, remainder, store);
            return true;
        }
        return false;
    }
    
    public static boolean addOrDropItemStack(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final ItemContainer itemContainer, final short slot, @Nonnull final ItemStack itemStack) {
        final ItemStackSlotTransaction transaction = itemContainer.addItemStackToSlot(slot, itemStack);
        final ItemStack remainder = transaction.getRemainder();
        return !ItemStack.isEmpty(remainder) && addOrDropItemStack(store, ref, itemContainer, itemStack);
    }
    
    public static boolean addOrDropItemStacks(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final ItemContainer itemContainer, final List<ItemStack> itemStacks) {
        final ListTransaction<ItemStackTransaction> transaction = itemContainer.addItemStacks(itemStacks);
        boolean droppedItem = false;
        for (final ItemStackTransaction stackTransaction : transaction.getList()) {
            final ItemStack remainder = stackTransaction.getRemainder();
            if (!ItemStack.isEmpty(remainder)) {
                ItemUtils.dropItem(ref, remainder, store);
                droppedItem = true;
            }
        }
        return droppedItem;
    }
    
    public static boolean tryAddOrderedOrDropItemStacks(@Nonnull final ComponentAccessor<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final ItemContainer itemContainer, final List<ItemStack> itemStacks) {
        final ListTransaction<ItemStackSlotTransaction> transaction = itemContainer.addItemStacksOrdered(itemStacks);
        List<ItemStack> remainderItemStacks = null;
        for (final ItemStackSlotTransaction stackTransaction : transaction.getList()) {
            final ItemStack remainder = stackTransaction.getRemainder();
            if (!ItemStack.isEmpty(remainder)) {
                if (remainderItemStacks == null) {
                    remainderItemStacks = new ObjectArrayList<ItemStack>();
                }
                remainderItemStacks.add(remainder);
            }
        }
        return addOrDropItemStacks(store, ref, itemContainer, remainderItemStacks);
    }
    
    static {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     2: invokedynamic   BootstrapMethod #5, get:()Ljava/util/function/Supplier;
        //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    13: dup            
        //    14: ldc_w           "Capacity"
        //    17: getstatic       com/hypixel/hytale/codec/Codec.SHORT:Lcom/hypixel/hytale/codec/codecs/simple/ShortCodec;
        //    20: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    23: invokedynamic   BootstrapMethod #6, accept:()Ljava/util/function/BiConsumer;
        //    28: invokedynamic   BootstrapMethod #7, apply:()Ljava/util/function/Function;
        //    33: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    36: iconst_0       
        //    37: invokestatic    java/lang/Short.valueOf:(S)Ljava/lang/Short;
        //    40: invokestatic    com/hypixel/hytale/codec/validation/Validators.greaterThanOrEqual:(Ljava/lang/Comparable;)Lcom/hypixel/hytale/codec/validation/Validator;
        //    43: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.addValidator:(Lcom/hypixel/hytale/codec/validation/Validator;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    46: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    49: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    52: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    55: dup            
        //    56: ldc_w           "Items"
        //    59: new             Lcom/hypixel/hytale/codec/codecs/map/Short2ObjectMapCodec;
        //    62: dup            
        //    63: getstatic       com/hypixel/hytale/server/core/inventory/ItemStack.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    66: invokedynamic   BootstrapMethod #8, get:()Ljava/util/function/Supplier;
        //    71: iconst_0       
        //    72: invokespecial   com/hypixel/hytale/codec/codecs/map/Short2ObjectMapCodec.<init>:(Lcom/hypixel/hytale/codec/Codec;Ljava/util/function/Supplier;Z)V
        //    75: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    78: invokedynamic   BootstrapMethod #9, accept:()Ljava/util/function/BiConsumer;
        //    83: invokedynamic   BootstrapMethod #10, apply:()Ljava/util/function/Function;
        //    88: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    91: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    94: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    97: invokedynamic   BootstrapMethod #11, accept:()Ljava/util/function/Consumer;
        //   102: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.afterDecode:(Ljava/util/function/Consumer;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   105: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   108: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   111: putstatic       com/hypixel/hytale/server/core/inventory/container/SimpleItemContainer.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   114: return         
        // 
        // The error that occurred was:
        // 
        // java.lang.UnsupportedOperationException: The requested operation is not supported.
        //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
        //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1083)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypesForVariables(TypeAnalysis.java:593)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:405)
        //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:109)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
        //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
}
