package net.minecraft.world.level;

import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.Commands;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import java.util.function.BiConsumer;
import com.mojang.brigadier.arguments.ArgumentType;
import java.util.function.Supplier;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import com.google.common.collect.Maps;
import java.util.Comparator;
import org.apache.logging.log4j.LogManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
import java.util.Iterator;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.server.level.ServerPlayer;
import javax.annotation.Nullable;
import net.minecraft.server.MinecraftServer;
import java.util.function.Consumer;
import net.minecraft.nbt.CompoundTag;
import java.util.stream.Collector;
import java.util.function.Function;
import com.google.common.collect.ImmutableMap;
import com.mojang.serialization.DynamicLike;
import java.util.Map;
import org.apache.logging.log4j.Logger;

public class GameRules {
    private static final Logger LOGGER;
    private static final Map<Key<?>, Type<?>> GAME_RULE_TYPES;
    public static final Key<BooleanValue> RULE_DOFIRETICK;
    public static final Key<BooleanValue> RULE_MOBGRIEFING;
    public static final Key<BooleanValue> RULE_KEEPINVENTORY;
    public static final Key<BooleanValue> RULE_DOMOBSPAWNING;
    public static final Key<BooleanValue> RULE_DOMOBLOOT;
    public static final Key<BooleanValue> RULE_DOBLOCKDROPS;
    public static final Key<BooleanValue> RULE_DOENTITYDROPS;
    public static final Key<BooleanValue> RULE_COMMANDBLOCKOUTPUT;
    public static final Key<BooleanValue> RULE_NATURAL_REGENERATION;
    public static final Key<BooleanValue> RULE_DAYLIGHT;
    public static final Key<BooleanValue> RULE_LOGADMINCOMMANDS;
    public static final Key<BooleanValue> RULE_SHOWDEATHMESSAGES;
    public static final Key<IntegerValue> RULE_RANDOMTICKING;
    public static final Key<BooleanValue> RULE_SENDCOMMANDFEEDBACK;
    public static final Key<BooleanValue> RULE_REDUCEDDEBUGINFO;
    public static final Key<BooleanValue> RULE_SPECTATORSGENERATECHUNKS;
    public static final Key<IntegerValue> RULE_SPAWN_RADIUS;
    public static final Key<BooleanValue> RULE_DISABLE_ELYTRA_MOVEMENT_CHECK;
    public static final Key<IntegerValue> RULE_MAX_ENTITY_CRAMMING;
    public static final Key<BooleanValue> RULE_WEATHER_CYCLE;
    public static final Key<BooleanValue> RULE_LIMITED_CRAFTING;
    public static final Key<IntegerValue> RULE_MAX_COMMAND_CHAIN_LENGTH;
    public static final Key<BooleanValue> RULE_ANNOUNCE_ADVANCEMENTS;
    public static final Key<BooleanValue> RULE_DISABLE_RAIDS;
    public static final Key<BooleanValue> RULE_DOINSOMNIA;
    public static final Key<BooleanValue> RULE_DO_IMMEDIATE_RESPAWN;
    public static final Key<BooleanValue> RULE_DROWNING_DAMAGE;
    public static final Key<BooleanValue> RULE_FALL_DAMAGE;
    public static final Key<BooleanValue> RULE_FIRE_DAMAGE;
    public static final Key<BooleanValue> RULE_DO_PATROL_SPAWNING;
    public static final Key<BooleanValue> RULE_DO_TRADER_SPAWNING;
    public static final Key<BooleanValue> RULE_FORGIVE_DEAD_PLAYERS;
    public static final Key<BooleanValue> RULE_UNIVERSAL_ANGER;
    private final Map<Key<?>, Value<?>> rules;
    
    private static <T extends Value<T>> Key<T> register(final String string, final Category b, final Type<T> f) {
        final Key<T> e4 = new Key<T>(string, b);
        final Type<?> f2 = GameRules.GAME_RULE_TYPES.put(e4, f);
        if (f2 != null) {
            throw new IllegalStateException("Duplicate game rule registration for " + string);
        }
        return e4;
    }
    
    public GameRules(final DynamicLike<?> dynamicLike) {
        this();
        this.loadFromTag(dynamicLike);
    }
    
    public GameRules() {
        this.rules = GameRules.GAME_RULE_TYPES.entrySet().stream().collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, entry -> entry.getValue().createRule()));
    }
    
    private GameRules(final Map<Key<?>, Value<?>> map) {
        this.rules = map;
    }
    
    public <T extends Value<T>> T getRule(final Key<T> e) {
        return (T)this.rules.get(e);
    }
    
    public CompoundTag createTag() {
        final CompoundTag le2 = new CompoundTag();
        this.rules.forEach((e, g) -> le2.putString(e.id, g.serialize()));
        return le2;
    }
    
    private void loadFromTag(final DynamicLike<?> dynamicLike) {
        this.rules.forEach((e, g) -> dynamicLike.get(e.id).asString().result().ifPresent(g::deserialize));
    }
    
    public GameRules copy() {
        return new GameRules(this.rules.entrySet().stream().collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, entry -> entry.getValue().copy())));
    }
    
    public static void visitGameRuleTypes(final GameRuleTypeVisitor c) {
        GameRules.GAME_RULE_TYPES.forEach((e, f) -> GameRules.<Value>callVisitorCap(c, e, f));
    }
    
    private static <T extends Value<T>> void callVisitorCap(final GameRuleTypeVisitor c, final Key<?> e, final Type<?> f) {
        final Key<T> e2 = (Key<T>)e;
        final Type<T> f2 = (Type<T>)f;
        c.<T>visit(e2, f2);
        f2.callVisitor(c, e2);
    }
    
    public void assignFrom(final GameRules bpx, @Nullable final MinecraftServer minecraftServer) {
        bpx.rules.keySet().forEach(e -> this.<Value>assignCap(e, bpx, minecraftServer));
    }
    
    private <T extends Value<T>> void assignCap(final Key<T> e, final GameRules bpx, @Nullable final MinecraftServer minecraftServer) {
        final T g5 = bpx.<T>getRule(e);
        this.<T>getRule(e).setFrom(g5, minecraftServer);
    }
    
    public boolean getBoolean(final Key<BooleanValue> e) {
        return this.<BooleanValue>getRule(e).get();
    }
    
    public int getInt(final Key<IntegerValue> e) {
        return this.<IntegerValue>getRule(e).get();
    }
    
    static {
        LOGGER = LogManager.getLogger();
        GAME_RULE_TYPES = Maps.newTreeMap(Comparator.<Object, Comparable>comparing(e -> e.id));
        RULE_DOFIRETICK = GameRules.<BooleanValue>register("doFireTick", Category.UPDATES, create(true));
        RULE_MOBGRIEFING = GameRules.<BooleanValue>register("mobGriefing", Category.MOBS, create(true));
        RULE_KEEPINVENTORY = GameRules.<BooleanValue>register("keepInventory", Category.PLAYER, create(false));
        RULE_DOMOBSPAWNING = GameRules.<BooleanValue>register("doMobSpawning", Category.SPAWNING, create(true));
        RULE_DOMOBLOOT = GameRules.<BooleanValue>register("doMobLoot", Category.DROPS, create(true));
        RULE_DOBLOCKDROPS = GameRules.<BooleanValue>register("doTileDrops", Category.DROPS, create(true));
        RULE_DOENTITYDROPS = GameRules.<BooleanValue>register("doEntityDrops", Category.DROPS, create(true));
        RULE_COMMANDBLOCKOUTPUT = GameRules.<BooleanValue>register("commandBlockOutput", Category.CHAT, create(true));
        RULE_NATURAL_REGENERATION = GameRules.<BooleanValue>register("naturalRegeneration", Category.PLAYER, create(true));
        RULE_DAYLIGHT = GameRules.<BooleanValue>register("doDaylightCycle", Category.UPDATES, create(true));
        RULE_LOGADMINCOMMANDS = GameRules.<BooleanValue>register("logAdminCommands", Category.CHAT, create(true));
        RULE_SHOWDEATHMESSAGES = GameRules.<BooleanValue>register("showDeathMessages", Category.CHAT, create(true));
        RULE_RANDOMTICKING = GameRules.<IntegerValue>register("randomTickSpeed", Category.UPDATES, create(3));
        RULE_SENDCOMMANDFEEDBACK = GameRules.<BooleanValue>register("sendCommandFeedback", Category.CHAT, create(true));
        final int byte3;
        final Iterator<ServerPlayer> iterator;
        ServerPlayer ze5;
        RULE_REDUCEDDEBUGINFO = GameRules.<BooleanValue>register("reducedDebugInfo", Category.MISC, create(false, (minecraftServer, a) -> {
            byte3 = (a.get() ? 22 : 23);
            minecraftServer.getPlayerList().getPlayers().iterator();
            while (iterator.hasNext()) {
                ze5 = iterator.next();
                ze5.connection.send(new ClientboundEntityEventPacket(ze5, (byte)byte3));
            }
            return;
        }));
        RULE_SPECTATORSGENERATECHUNKS = GameRules.<BooleanValue>register("spectatorsGenerateChunks", Category.PLAYER, create(true));
        RULE_SPAWN_RADIUS = GameRules.<IntegerValue>register("spawnRadius", Category.PLAYER, create(10));
        RULE_DISABLE_ELYTRA_MOVEMENT_CHECK = GameRules.<BooleanValue>register("disableElytraMovementCheck", Category.PLAYER, create(false));
        RULE_MAX_ENTITY_CRAMMING = GameRules.<IntegerValue>register("maxEntityCramming", Category.MOBS, create(24));
        RULE_WEATHER_CYCLE = GameRules.<BooleanValue>register("doWeatherCycle", Category.UPDATES, create(true));
        RULE_LIMITED_CRAFTING = GameRules.<BooleanValue>register("doLimitedCrafting", Category.PLAYER, create(false));
        RULE_MAX_COMMAND_CHAIN_LENGTH = GameRules.<IntegerValue>register("maxCommandChainLength", Category.MISC, create(65536));
        RULE_ANNOUNCE_ADVANCEMENTS = GameRules.<BooleanValue>register("announceAdvancements", Category.CHAT, create(true));
        RULE_DISABLE_RAIDS = GameRules.<BooleanValue>register("disableRaids", Category.MOBS, create(false));
        RULE_DOINSOMNIA = GameRules.<BooleanValue>register("doInsomnia", Category.SPAWNING, create(true));
        final Iterator<ServerPlayer> iterator2;
        ServerPlayer ze6;
        ServerGamePacketListenerImpl connection;
        final ClientboundGameEventPacket ni;
        RULE_DO_IMMEDIATE_RESPAWN = GameRules.<BooleanValue>register("doImmediateRespawn", Category.PLAYER, create(false, (minecraftServer, a) -> {
            minecraftServer.getPlayerList().getPlayers().iterator();
            while (iterator2.hasNext()) {
                ze6 = iterator2.next();
                connection = ze6.connection;
                new ClientboundGameEventPacket(ClientboundGameEventPacket.IMMEDIATE_RESPAWN, a.get() ? 1.0f : 0.0f);
                connection.send(ni);
            }
            return;
        }));
        RULE_DROWNING_DAMAGE = GameRules.<BooleanValue>register("drowningDamage", Category.PLAYER, create(true));
        RULE_FALL_DAMAGE = GameRules.<BooleanValue>register("fallDamage", Category.PLAYER, create(true));
        RULE_FIRE_DAMAGE = GameRules.<BooleanValue>register("fireDamage", Category.PLAYER, create(true));
        RULE_DO_PATROL_SPAWNING = GameRules.<BooleanValue>register("doPatrolSpawning", Category.SPAWNING, create(true));
        RULE_DO_TRADER_SPAWNING = GameRules.<BooleanValue>register("doTraderSpawning", Category.SPAWNING, create(true));
        RULE_FORGIVE_DEAD_PLAYERS = GameRules.<BooleanValue>register("forgiveDeadPlayers", Category.MOBS, create(true));
        RULE_UNIVERSAL_ANGER = GameRules.<BooleanValue>register("universalAnger", Category.MOBS, create(false));
    }
    
    public enum Category {
        PLAYER("gamerule.category.player"), 
        MOBS("gamerule.category.mobs"), 
        SPAWNING("gamerule.category.spawning"), 
        DROPS("gamerule.category.drops"), 
        UPDATES("gamerule.category.updates"), 
        CHAT("gamerule.category.chat"), 
        MISC("gamerule.category.misc");
        
        private final String descriptionId;
        
        private Category(final String string3) {
            this.descriptionId = string3;
        }
        
        public String getDescriptionId() {
            return this.descriptionId;
        }
    }
    
    public interface GameRuleTypeVisitor {
        default <T extends Value<T>> void visit(final Key<T> e, final Type<T> f) {
        }
        
        default void visitBoolean(final Key<BooleanValue> e, final Type<BooleanValue> f) {
        }
        
        default void visitInteger(final Key<IntegerValue> e, final Type<IntegerValue> f) {
        }
    }
    
    public static final class Key<T extends Value<T>> {
        private final String id;
        private final Category category;
        
        public Key(final String string, final Category b) {
            this.id = string;
            this.category = b;
        }
        
        @Override
        public String toString() {
            return this.id;
        }
        
        @Override
        public boolean equals(final Object object) {
            return this == object || (object instanceof Key && ((Key)object).id.equals(this.id));
        }
        
        @Override
        public int hashCode() {
            return this.id.hashCode();
        }
        
        public String getId() {
            return this.id;
        }
        
        public String getDescriptionId() {
            return "gamerule." + this.id;
        }
        
        public Category getCategory() {
            return this.category;
        }
    }
    
    public static class Type<T extends Value<T>> {
        private final Supplier<ArgumentType<?>> argument;
        private final Function<Type<T>, T> constructor;
        private final BiConsumer<MinecraftServer, T> callback;
        private final VisitorCaller<T> visitorCaller;
        
        private Type(final Supplier<ArgumentType<?>> supplier, final Function<Type<T>, T> function, final BiConsumer<MinecraftServer, T> biConsumer, final VisitorCaller<T> h) {
            this.argument = supplier;
            this.constructor = function;
            this.callback = biConsumer;
            this.visitorCaller = h;
        }
        
        public RequiredArgumentBuilder<CommandSourceStack, ?> createArgument(final String string) {
            return Commands.argument(string, this.argument.get());
        }
        
        public T createRule() {
            return this.constructor.apply(this);
        }
        
        public void callVisitor(final GameRuleTypeVisitor c, final Key<T> e) {
            this.visitorCaller.call(c, e, this);
        }
    }
    
    public abstract static class Value<T extends Value<T>> {
        protected final Type<T> type;
        
        public Value(final Type<T> f) {
            this.type = f;
        }
        
        protected abstract void updateFromArgument(final CommandContext<CommandSourceStack> commandContext, final String string);
        
        public void setFromArgument(final CommandContext<CommandSourceStack> commandContext, final String string) {
            this.updateFromArgument(commandContext, string);
            this.onChanged(((CommandSourceStack)commandContext.getSource()).getServer());
        }
        
        protected void onChanged(@Nullable final MinecraftServer minecraftServer) {
            if (minecraftServer != null) {
                ((Type<Value>)this.type).callback.accept(minecraftServer, this.getSelf());
            }
        }
        
        protected abstract void deserialize(final String string);
        
        public abstract String serialize();
        
        @Override
        public String toString() {
            return this.serialize();
        }
        
        public abstract int getCommandResult();
        
        protected abstract T getSelf();
        
        protected abstract T copy();
        
        public abstract void setFrom(final T g, @Nullable final MinecraftServer minecraftServer);
    }
    
    public static class IntegerValue extends Value<IntegerValue> {
        private int value;
        
        private static Type<IntegerValue> create(final int integer, final BiConsumer<MinecraftServer, IntegerValue> biConsumer) {
            return new Type<IntegerValue>((Supplier)IntegerArgumentType::integer, f -> new IntegerValue(f, integer), (BiConsumer)biConsumer, GameRuleTypeVisitor::visitInteger);
        }
        
        private static Type<IntegerValue> create(final int integer) {
            return create(integer, (minecraftServer, d) -> {});
        }
        
        public IntegerValue(final Type<IntegerValue> f, final int integer) {
            super(f);
            this.value = integer;
        }
        
        @Override
        protected void updateFromArgument(final CommandContext<CommandSourceStack> commandContext, final String string) {
            this.value = IntegerArgumentType.getInteger((CommandContext)commandContext, string);
        }
        
        public int get() {
            return this.value;
        }
        
        @Override
        public String serialize() {
            return Integer.toString(this.value);
        }
        
        @Override
        protected void deserialize(final String string) {
            this.value = safeParse(string);
        }
        
        public boolean tryDeserialize(final String string) {
            try {
                this.value = Integer.parseInt(string);
                return true;
            }
            catch (NumberFormatException ex) {
                return false;
            }
        }
        
        private static int safeParse(final String string) {
            if (!string.isEmpty()) {
                try {
                    return Integer.parseInt(string);
                }
                catch (NumberFormatException numberFormatException2) {
                    GameRules.LOGGER.warn("Failed to parse integer {}", string);
                }
            }
            return 0;
        }
        
        @Override
        public int getCommandResult() {
            return this.value;
        }
        
        @Override
        protected IntegerValue getSelf() {
            return this;
        }
        
        @Override
        protected IntegerValue copy() {
            return new IntegerValue((Type<IntegerValue>)this.type, this.value);
        }
        
        @Override
        public void setFrom(final IntegerValue d, @Nullable final MinecraftServer minecraftServer) {
            this.value = d.value;
            this.onChanged(minecraftServer);
        }
    }
    
    public static class BooleanValue extends Value<BooleanValue> {
        private boolean value;
        
        private static Type<BooleanValue> create(final boolean boolean1, final BiConsumer<MinecraftServer, BooleanValue> biConsumer) {
            return new Type<BooleanValue>((Supplier)BoolArgumentType::bool, f -> new BooleanValue(f, boolean1), (BiConsumer)biConsumer, GameRuleTypeVisitor::visitBoolean);
        }
        
        private static Type<BooleanValue> create(final boolean boolean1) {
            return create(boolean1, (minecraftServer, a) -> {});
        }
        
        public BooleanValue(final Type<BooleanValue> f, final boolean boolean2) {
            super(f);
            this.value = boolean2;
        }
        
        @Override
        protected void updateFromArgument(final CommandContext<CommandSourceStack> commandContext, final String string) {
            this.value = BoolArgumentType.getBool((CommandContext)commandContext, string);
        }
        
        public boolean get() {
            return this.value;
        }
        
        public void set(final boolean boolean1, @Nullable final MinecraftServer minecraftServer) {
            this.value = boolean1;
            this.onChanged(minecraftServer);
        }
        
        @Override
        public String serialize() {
            return Boolean.toString(this.value);
        }
        
        @Override
        protected void deserialize(final String string) {
            this.value = Boolean.parseBoolean(string);
        }
        
        @Override
        public int getCommandResult() {
            return this.value ? 1 : 0;
        }
        
        @Override
        protected BooleanValue getSelf() {
            return this;
        }
        
        @Override
        protected BooleanValue copy() {
            return new BooleanValue((Type<BooleanValue>)this.type, this.value);
        }
        
        @Override
        public void setFrom(final BooleanValue a, @Nullable final MinecraftServer minecraftServer) {
            this.value = a.value;
            this.onChanged(minecraftServer);
        }
    }
    
    interface VisitorCaller<T extends Value<T>> {
        void call(final GameRuleTypeVisitor c, final Key<T> e, final Type<T> f);
    }
}
