package net.minecraft.world.level.storage.loot;

import com.google.gson.stream.JsonReader;
import java.io.IOException;
import com.google.gson.stream.JsonWriter;
import com.google.gson.TypeAdapter;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
import com.google.common.collect.Maps;
import javax.annotation.Nullable;
import net.minecraft.world.item.ItemStack;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
import java.util.Map;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import java.util.Set;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
import net.minecraft.server.level.ServerLevel;
import java.util.Random;

public class LootContext {
    private final Random random;
    private final float luck;
    private final ServerLevel level;
    private final Function<ResourceLocation, LootTable> lootTables;
    private final Set<LootTable> visitedTables;
    private final Function<ResourceLocation, LootItemCondition> conditions;
    private final Set<LootItemCondition> visitedConditions;
    private final Map<LootContextParam<?>, Object> params;
    private final Map<ResourceLocation, DynamicDrop> dynamicDrops;
    
    private LootContext(final Random random, final float float2, final ServerLevel xd, final Function<ResourceLocation, LootTable> function4, final Function<ResourceLocation, LootItemCondition> function5, final Map<LootContextParam<?>, Object> map6, final Map<ResourceLocation, DynamicDrop> map7) {
        this.visitedTables = Sets.newLinkedHashSet();
        this.visitedConditions = Sets.newLinkedHashSet();
        this.random = random;
        this.luck = float2;
        this.level = xd;
        this.lootTables = function4;
        this.conditions = function5;
        this.params = ImmutableMap.copyOf(map6);
        this.dynamicDrops = ImmutableMap.copyOf(map7);
    }
    
    public boolean hasParam(final LootContextParam<?> ctx) {
        return this.params.containsKey(ctx);
    }
    
    public void addDynamicDrops(final ResourceLocation sm, final Consumer<ItemStack> consumer) {
        final DynamicDrop b4 = this.dynamicDrops.get(sm);
        if (b4 != null) {
            b4.add(this, consumer);
        }
    }
    
    @Nullable
    public <T> T getParamOrNull(final LootContextParam<T> ctx) {
        return (T)this.params.get(ctx);
    }
    
    public boolean addVisitedTable(final LootTable csa) {
        return this.visitedTables.add(csa);
    }
    
    public void removeVisitedTable(final LootTable csa) {
        this.visitedTables.remove(csa);
    }
    
    public boolean addVisitedCondition(final LootItemCondition cum) {
        return this.visitedConditions.add(cum);
    }
    
    public void removeVisitedCondition(final LootItemCondition cum) {
        this.visitedConditions.remove(cum);
    }
    
    public LootTable getLootTable(final ResourceLocation sm) {
        return this.lootTables.apply(sm);
    }
    
    public LootItemCondition getCondition(final ResourceLocation sm) {
        return this.conditions.apply(sm);
    }
    
    public Random getRandom() {
        return this.random;
    }
    
    public float getLuck() {
        return this.luck;
    }
    
    public ServerLevel getLevel() {
        return this.level;
    }
    
    public static class Builder {
        private final ServerLevel level;
        private final Map<LootContextParam<?>, Object> params;
        private final Map<ResourceLocation, DynamicDrop> dynamicDrops;
        private Random random;
        private float luck;
        
        public Builder(final ServerLevel xd) {
            this.params = Maps.newIdentityHashMap();
            this.dynamicDrops = Maps.newHashMap();
            this.level = xd;
        }
        
        public Builder withRandom(final Random random) {
            this.random = random;
            return this;
        }
        
        public Builder withOptionalRandomSeed(final long long1) {
            if (long1 != 0L) {
                this.random = new Random(long1);
            }
            return this;
        }
        
        public Builder withOptionalRandomSeed(final long long1, final Random random) {
            if (long1 == 0L) {
                this.random = random;
            }
            else {
                this.random = new Random(long1);
            }
            return this;
        }
        
        public Builder withLuck(final float float1) {
            this.luck = float1;
            return this;
        }
        
        public <T> Builder withParameter(final LootContextParam<T> ctx, final T object) {
            this.params.put(ctx, object);
            return this;
        }
        
        public <T> Builder withOptionalParameter(final LootContextParam<T> ctx, @Nullable final T object) {
            if (object == null) {
                this.params.remove(ctx);
            }
            else {
                this.params.put(ctx, object);
            }
            return this;
        }
        
        public Builder withDynamicDrop(final ResourceLocation sm, final DynamicDrop b) {
            final DynamicDrop b2 = this.dynamicDrops.put(sm, b);
            if (b2 != null) {
                throw new IllegalStateException("Duplicated dynamic drop '" + this.dynamicDrops + "'");
            }
            return this;
        }
        
        public ServerLevel getLevel() {
            return this.level;
        }
        
        public <T> T getParameter(final LootContextParam<T> ctx) {
            final T object3 = (T)this.params.get(ctx);
            if (object3 == null) {
                throw new IllegalArgumentException("No parameter " + ctx);
            }
            return object3;
        }
        
        @Nullable
        public <T> T getOptionalParameter(final LootContextParam<T> ctx) {
            return (T)this.params.get(ctx);
        }
        
        public LootContext create(final LootContextParamSet cty) {
            final Set<LootContextParam<?>> set3 = Sets.<LootContextParam<?>>difference(this.params.keySet(), cty.getAllowed());
            if (!set3.isEmpty()) {
                throw new IllegalArgumentException("Parameters not allowed in this parameter set: " + set3);
            }
            final Set<LootContextParam<?>> set4 = Sets.<LootContextParam<?>>difference(cty.getRequired(), this.params.keySet());
            if (!set4.isEmpty()) {
                throw new IllegalArgumentException("Missing required parameters: " + set4);
            }
            Random random5 = this.random;
            if (random5 == null) {
                random5 = new Random();
            }
            final MinecraftServer minecraftServer6 = this.level.getServer();
            return new LootContext(random5, this.luck, this.level, minecraftServer6.getLootTables()::get, minecraftServer6.getPredicateManager()::get, this.params, this.dynamicDrops, null);
        }
    }
    
    public enum EntityTarget {
        THIS("this", LootContextParams.THIS_ENTITY), 
        KILLER("killer", LootContextParams.KILLER_ENTITY), 
        DIRECT_KILLER("direct_killer", LootContextParams.DIRECT_KILLER_ENTITY), 
        KILLER_PLAYER("killer_player", LootContextParams.LAST_DAMAGE_PLAYER);
        
        private final String name;
        private final LootContextParam<? extends Entity> param;
        
        private EntityTarget(final String string3, final LootContextParam<? extends Entity> ctx) {
            this.name = string3;
            this.param = ctx;
        }
        
        public LootContextParam<? extends Entity> getParam() {
            return this.param;
        }
        
        public static EntityTarget getByName(final String string) {
            for (final EntityTarget c5 : values()) {
                if (c5.name.equals(string)) {
                    return c5;
                }
            }
            throw new IllegalArgumentException("Invalid entity target " + string);
        }
        
        public static class Serializer extends TypeAdapter<EntityTarget> {
            @Override
            public void write(final JsonWriter jsonWriter, final EntityTarget c) throws IOException {
                jsonWriter.value(c.name);
            }
            
            @Override
            public EntityTarget read(final JsonReader jsonReader) throws IOException {
                return EntityTarget.getByName(jsonReader.nextString());
            }
        }
    }
    
    @FunctionalInterface
    public interface DynamicDrop {
        void add(final LootContext crx, final Consumer<ItemStack> consumer);
    }
}
