package net.minecraft.server.commands;

import net.minecraft.world.level.storage.loot.LootTables;
import net.minecraft.commands.SharedSuggestionProvider;
import java.util.concurrent.CompletableFuture;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.Message;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.entity.Entity;
import net.minecraft.server.level.ServerPlayer;
import java.util.Collection;
import java.util.Objects;
import java.util.Iterator;
import com.google.common.collect.Lists;
import net.minecraft.world.item.ItemStack;
import java.util.List;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.Container;
import net.minecraft.core.BlockPos;
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import net.minecraft.commands.arguments.SlotArgument;
import com.mojang.brigadier.builder.ArgumentBuilder;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.arguments.item.ItemArgument;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.arguments.ArgumentType;
import net.minecraft.commands.arguments.ResourceLocationArgument;
import net.minecraft.commands.Commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.suggestion.SuggestionProvider;

public class LootCommand {
    public static final SuggestionProvider<CommandSourceStack> SUGGEST_LOOT_TABLE;
    private static final DynamicCommandExceptionType ERROR_NO_HELD_ITEMS;
    private static final DynamicCommandExceptionType ERROR_NO_LOOT_TABLE;
    
    public static void register(final CommandDispatcher<CommandSourceStack> commandDispatcher) {
        commandDispatcher.register((LiteralArgumentBuilder)LootCommand.<LiteralArgumentBuilder>addTargets((LiteralArgumentBuilder)Commands.literal("loot").requires(cq -> cq.hasPermission(2)), (argumentBuilder, b) -> argumentBuilder.then(Commands.literal("fish").then(Commands.argument("loot_table", (com.mojang.brigadier.arguments.ArgumentType<Object>)ResourceLocationArgument.id()).suggests((SuggestionProvider)LootCommand.SUGGEST_LOOT_TABLE).then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("pos", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).executes(commandContext -> dropFishingLoot((CommandContext<CommandSourceStack>)commandContext, ResourceLocationArgument.getId((CommandContext<CommandSourceStack>)commandContext, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), ItemStack.EMPTY, b))).then(Commands.argument("tool", (com.mojang.brigadier.arguments.ArgumentType<Object>)ItemArgument.item()).executes(commandContext -> dropFishingLoot((CommandContext<CommandSourceStack>)commandContext, ResourceLocationArgument.getId((CommandContext<CommandSourceStack>)commandContext, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), ItemArgument.getItem((com.mojang.brigadier.context.CommandContext<Object>)commandContext, "tool").createItemStack(1, false), b)))).then(Commands.literal("mainhand").executes(commandContext -> dropFishingLoot((CommandContext<CommandSourceStack>)commandContext, ResourceLocationArgument.getId((CommandContext<CommandSourceStack>)commandContext, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), getSourceHandItem((CommandSourceStack)commandContext.getSource(), EquipmentSlot.MAINHAND), b)))).then(Commands.literal("offhand").executes(commandContext -> dropFishingLoot((CommandContext<CommandSourceStack>)commandContext, ResourceLocationArgument.getId((CommandContext<CommandSourceStack>)commandContext, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), getSourceHandItem((CommandSourceStack)commandContext.getSource(), EquipmentSlot.OFFHAND), b)))))).then(Commands.literal("loot").then(Commands.argument("loot_table", (com.mojang.brigadier.arguments.ArgumentType<Object>)ResourceLocationArgument.id()).suggests((SuggestionProvider)LootCommand.SUGGEST_LOOT_TABLE).executes(commandContext -> dropChestLoot((CommandContext<CommandSourceStack>)commandContext, ResourceLocationArgument.getId((CommandContext<CommandSourceStack>)commandContext, "loot_table"), b)))).then(Commands.literal("kill").then(Commands.argument("target", (com.mojang.brigadier.arguments.ArgumentType<Object>)EntityArgument.entity()).executes(commandContext -> dropKillLoot((CommandContext<CommandSourceStack>)commandContext, EntityArgument.getEntity((CommandContext<CommandSourceStack>)commandContext, "target"), b)))).then(Commands.literal("mine").then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("pos", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).executes(commandContext -> dropBlockLoot((CommandContext<CommandSourceStack>)commandContext, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), ItemStack.EMPTY, b))).then(Commands.argument("tool", (com.mojang.brigadier.arguments.ArgumentType<Object>)ItemArgument.item()).executes(commandContext -> dropBlockLoot((CommandContext<CommandSourceStack>)commandContext, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), ItemArgument.getItem((com.mojang.brigadier.context.CommandContext<Object>)commandContext, "tool").createItemStack(1, false), b)))).then(Commands.literal("mainhand").executes(commandContext -> dropBlockLoot((CommandContext<CommandSourceStack>)commandContext, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), getSourceHandItem((CommandSourceStack)commandContext.getSource(), EquipmentSlot.MAINHAND), b)))).then(Commands.literal("offhand").executes(commandContext -> dropBlockLoot((CommandContext<CommandSourceStack>)commandContext, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "pos"), getSourceHandItem((CommandSourceStack)commandContext.getSource(), EquipmentSlot.OFFHAND), b)))))));
    }
    
    private static <T extends ArgumentBuilder<CommandSourceStack, T>> T addTargets(final T argumentBuilder, final TailProvider c) {
        return (T)argumentBuilder.then(((LiteralArgumentBuilder)Commands.literal("replace").then(Commands.literal("entity").then(Commands.argument("entities", (com.mojang.brigadier.arguments.ArgumentType<Object>)EntityArgument.entities()).then(c.construct(Commands.argument("slot", (com.mojang.brigadier.arguments.ArgumentType<Object>)SlotArgument.slot()), (commandContext, list, a) -> entityReplace(EntityArgument.getEntities(commandContext, "entities"), SlotArgument.getSlot(commandContext, "slot"), list.size(), list, a)).then((ArgumentBuilder)c.construct(Commands.argument("count", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer(0)), (commandContext, list, a) -> entityReplace(EntityArgument.getEntities(commandContext, "entities"), SlotArgument.getSlot(commandContext, "slot"), IntegerArgumentType.getInteger((CommandContext)commandContext, "count"), list, a))))))).then(Commands.literal("block").then(Commands.argument("targetPos", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).then(c.construct(Commands.argument("slot", (com.mojang.brigadier.arguments.ArgumentType<Object>)SlotArgument.slot()), (commandContext, list, a) -> blockReplace((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos(commandContext, "targetPos"), SlotArgument.getSlot(commandContext, "slot"), list.size(), list, a)).then((ArgumentBuilder)c.construct(Commands.argument("count", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer(0)), (commandContext, list, a) -> blockReplace((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos(commandContext, "targetPos"), IntegerArgumentType.getInteger((CommandContext)commandContext, "slot"), IntegerArgumentType.getInteger((CommandContext)commandContext, "count"), list, a))))))).then(Commands.literal("insert").then((ArgumentBuilder)c.construct(Commands.argument("targetPos", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()), (commandContext, list, a) -> blockDistribute((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos(commandContext, "targetPos"), list, a)))).then(Commands.literal("give").then((ArgumentBuilder)c.construct(Commands.argument("players", (com.mojang.brigadier.arguments.ArgumentType<Object>)EntityArgument.players()), (commandContext, list, a) -> playerGive(EntityArgument.getPlayers(commandContext, "players"), list, a)))).then(Commands.literal("spawn").then((ArgumentBuilder)c.construct(Commands.argument("targetPos", (com.mojang.brigadier.arguments.ArgumentType<Object>)Vec3Argument.vec3()), (commandContext, list, a) -> dropInWorld((CommandSourceStack)commandContext.getSource(), Vec3Argument.getVec3(commandContext, "targetPos"), list, a))));
    }
    
    private static Container getContainer(final CommandSourceStack cq, final BlockPos fk) throws CommandSyntaxException {
        final BlockEntity bwl3 = cq.getLevel().getBlockEntity(fk);
        if (!(bwl3 instanceof Container)) {
            throw ReplaceItemCommand.ERROR_NOT_A_CONTAINER.create();
        }
        return (Container)bwl3;
    }
    
    private static int blockDistribute(final CommandSourceStack cq, final BlockPos fk, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final Container aje5 = getContainer(cq, fk);
        final List<ItemStack> list2 = Lists.newArrayListWithCapacity(list.size());
        for (final ItemStack ben8 : list) {
            if (distributeToContainer(aje5, ben8.copy())) {
                aje5.setChanged();
                list2.add(ben8);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static boolean distributeToContainer(final Container aje, final ItemStack ben) {
        boolean boolean3 = false;
        for (int integer4 = 0; integer4 < aje.getContainerSize() && !ben.isEmpty(); ++integer4) {
            final ItemStack ben2 = aje.getItem(integer4);
            if (aje.canPlaceItem(integer4, ben)) {
                if (ben2.isEmpty()) {
                    aje.setItem(integer4, ben);
                    boolean3 = true;
                    break;
                }
                if (canMergeItems(ben2, ben)) {
                    final int integer5 = ben.getMaxStackSize() - ben2.getCount();
                    final int integer6 = Math.min(ben.getCount(), integer5);
                    ben.shrink(integer6);
                    ben2.grow(integer6);
                    boolean3 = true;
                }
            }
        }
        return boolean3;
    }
    
    private static int blockReplace(final CommandSourceStack cq, final BlockPos fk, final int integer3, final int integer4, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final Container aje7 = getContainer(cq, fk);
        final int integer5 = aje7.getContainerSize();
        if (integer3 < 0 || integer3 >= integer5) {
            throw ReplaceItemCommand.ERROR_INAPPLICABLE_SLOT.create(integer3);
        }
        final List<ItemStack> list2 = Lists.newArrayListWithCapacity(list.size());
        for (int integer6 = 0; integer6 < integer4; ++integer6) {
            final int integer7 = integer3 + integer6;
            final ItemStack ben12 = (integer6 < list.size()) ? list.get(integer6) : ItemStack.EMPTY;
            if (aje7.canPlaceItem(integer7, ben12)) {
                aje7.setItem(integer7, ben12);
                list2.add(ben12);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static boolean canMergeItems(final ItemStack ben1, final ItemStack ben2) {
        return ben1.getItem() == ben2.getItem() && ben1.getDamageValue() == ben2.getDamageValue() && ben1.getCount() <= ben1.getMaxStackSize() && Objects.equals(ben1.getTag(), ben2.getTag());
    }
    
    private static int playerGive(final Collection<ServerPlayer> collection, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final List<ItemStack> list2 = Lists.newArrayListWithCapacity(list.size());
        for (final ItemStack ben6 : list) {
            for (final ServerPlayer xe8 : collection) {
                if (xe8.inventory.add(ben6.copy())) {
                    list2.add(ben6);
                }
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static void setSlots(final Entity akq, final List<ItemStack> list2, final int integer3, final int integer4, final List<ItemStack> list5) {
        for (int integer5 = 0; integer5 < integer4; ++integer5) {
            final ItemStack ben7 = (integer5 < list2.size()) ? list2.get(integer5) : ItemStack.EMPTY;
            if (akq.setSlot(integer3 + integer5, ben7.copy())) {
                list5.add(ben7);
            }
        }
    }
    
    private static int entityReplace(final Collection<? extends Entity> collection, final int integer2, final int integer3, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final List<ItemStack> list2 = Lists.newArrayListWithCapacity(list.size());
        for (final Entity akq8 : collection) {
            if (akq8 instanceof ServerPlayer) {
                final ServerPlayer xe9 = (ServerPlayer)akq8;
                xe9.inventoryMenu.broadcastChanges();
                setSlots(akq8, list, integer2, integer3, list2);
                xe9.inventoryMenu.broadcastChanges();
            }
            else {
                setSlots(akq8, list, integer2, integer3, list2);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static int dropInWorld(final CommandSourceStack cq, final Vec3 cvl, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final ServerLevel xd5 = cq.getLevel();
        final ServerLevel bjw;
        final ItemEntity awb4;
        list.forEach(ben -> {
            awb4 = new ItemEntity(bjw, cvl.x, cvl.y, cvl.z, ben.copy());
            awb4.setDefaultPickUpDelay();
            bjw.addFreshEntity(awb4);
            return;
        });
        a.accept(list);
        return list.size();
    }
    
    private static void callback(final CommandSourceStack cq, final List<ItemStack> list) {
        if (list.size() == 1) {
            final ItemStack ben3 = list.get(0);
            cq.sendSuccess(new TranslatableComponent("commands.drop.success.single", new Object[] { ben3.getCount(), ben3.getDisplayName() }), false);
        }
        else {
            cq.sendSuccess(new TranslatableComponent("commands.drop.success.multiple", new Object[] { list.size() }), false);
        }
    }
    
    private static void callback(final CommandSourceStack cq, final List<ItemStack> list, final ResourceLocation sm) {
        if (list.size() == 1) {
            final ItemStack ben4 = list.get(0);
            cq.sendSuccess(new TranslatableComponent("commands.drop.success.single_with_table", new Object[] { ben4.getCount(), ben4.getDisplayName(), sm }), false);
        }
        else {
            cq.sendSuccess(new TranslatableComponent("commands.drop.success.multiple_with_table", new Object[] { list.size(), sm }), false);
        }
    }
    
    private static ItemStack getSourceHandItem(final CommandSourceStack cq, final EquipmentSlot akv) throws CommandSyntaxException {
        final Entity akq3 = cq.getEntityOrException();
        if (akq3 instanceof LivingEntity) {
            return ((LivingEntity)akq3).getItemBySlot(akv);
        }
        throw LootCommand.ERROR_NO_HELD_ITEMS.create(akq3.getDisplayName());
    }
    
    private static int dropBlockLoot(final CommandContext<CommandSourceStack> commandContext, final BlockPos fk, final ItemStack ben, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cq5 = (CommandSourceStack)commandContext.getSource();
        final ServerLevel xd6 = cq5.getLevel();
        final BlockState byj7 = xd6.getBlockState(fk);
        final BlockEntity bwl8 = xd6.getBlockEntity(fk);
        final LootContext.Builder a9 = new LootContext.Builder(xd6).<BlockPos>withParameter(LootContextParams.BLOCK_POS, fk).<BlockState>withParameter(LootContextParams.BLOCK_STATE, byj7).<BlockEntity>withOptionalParameter(LootContextParams.BLOCK_ENTITY, bwl8).<Entity>withOptionalParameter(LootContextParams.THIS_ENTITY, cq5.getEntity()).<ItemStack>withParameter(LootContextParams.TOOL, ben);
        final List<ItemStack> list2 = byj7.getDrops(a9);
        return b.accept(commandContext, list2, list -> callback(cq5, list, byj7.getBlock().getLootTable()));
    }
    
    private static int dropKillLoot(final CommandContext<CommandSourceStack> commandContext, final Entity akq, final DropConsumer b) throws CommandSyntaxException {
        if (!(akq instanceof LivingEntity)) {
            throw LootCommand.ERROR_NO_LOOT_TABLE.create(akq.getDisplayName());
        }
        final ResourceLocation sm4 = ((LivingEntity)akq).getLootTable();
        final CommandSourceStack cq5 = (CommandSourceStack)commandContext.getSource();
        final LootContext.Builder a6 = new LootContext.Builder(cq5.getLevel());
        final Entity akq2 = cq5.getEntity();
        if (akq2 instanceof Player) {
            a6.<Player>withParameter(LootContextParams.LAST_DAMAGE_PLAYER, (Player)akq2);
        }
        a6.<DamageSource>withParameter(LootContextParams.DAMAGE_SOURCE, DamageSource.MAGIC);
        a6.<Entity>withOptionalParameter(LootContextParams.DIRECT_KILLER_ENTITY, akq2);
        a6.<Entity>withOptionalParameter(LootContextParams.KILLER_ENTITY, akq2);
        a6.<Entity>withParameter(LootContextParams.THIS_ENTITY, akq);
        a6.<BlockPos>withParameter(LootContextParams.BLOCK_POS, new BlockPos(cq5.getPosition()));
        final LootTable csa8 = cq5.getServer().getLootTables().get(sm4);
        final List<ItemStack> list2 = csa8.getRandomItems(a6.create(LootContextParamSets.ENTITY));
        return b.accept(commandContext, list2, list -> callback(cq5, list, sm4));
    }
    
    private static int dropChestLoot(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation sm, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cq4 = (CommandSourceStack)commandContext.getSource();
        final LootContext.Builder a5 = new LootContext.Builder(cq4.getLevel()).<Entity>withOptionalParameter(LootContextParams.THIS_ENTITY, cq4.getEntity()).<BlockPos>withParameter(LootContextParams.BLOCK_POS, new BlockPos(cq4.getPosition()));
        return drop(commandContext, sm, a5.create(LootContextParamSets.CHEST), b);
    }
    
    private static int dropFishingLoot(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation sm, final BlockPos fk, final ItemStack ben, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cq6 = (CommandSourceStack)commandContext.getSource();
        final LootContext crx7 = new LootContext.Builder(cq6.getLevel()).<BlockPos>withParameter(LootContextParams.BLOCK_POS, fk).<ItemStack>withParameter(LootContextParams.TOOL, ben).create(LootContextParamSets.FISHING);
        return drop(commandContext, sm, crx7, b);
    }
    
    private static int drop(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation sm, final LootContext crx, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cq5 = (CommandSourceStack)commandContext.getSource();
        final LootTable csa6 = cq5.getServer().getLootTables().get(sm);
        final List<ItemStack> list2 = csa6.getRandomItems(crx);
        return b.accept(commandContext, list2, list -> callback(cq5, list));
    }
    
    static {
        SUGGEST_LOOT_TABLE = ((commandContext, suggestionsBuilder) -> {
            final LootTables csb3 = ((CommandSourceStack)commandContext.getSource()).getServer().getLootTables();
            return SharedSuggestionProvider.suggestResource(csb3.getIds(), suggestionsBuilder);
        });
        final TranslatableComponent translatableComponent;
        ERROR_NO_HELD_ITEMS = new DynamicCommandExceptionType(object -> {
            new TranslatableComponent("commands.drop.no_held_items", new Object[] { object });
            return translatableComponent;
        });
        final TranslatableComponent translatableComponent2;
        ERROR_NO_LOOT_TABLE = new DynamicCommandExceptionType(object -> {
            new TranslatableComponent("commands.drop.no_loot_table", new Object[] { object });
            return translatableComponent2;
        });
    }
    
    @FunctionalInterface
    interface TailProvider {
        ArgumentBuilder<CommandSourceStack, ?> construct(final ArgumentBuilder<CommandSourceStack, ?> argumentBuilder, final DropConsumer b);
    }
    
    @FunctionalInterface
    interface DropConsumer {
        int accept(final CommandContext<CommandSourceStack> commandContext, final List<ItemStack> list, final Callback a) throws CommandSyntaxException;
    }
    
    @FunctionalInterface
    interface Callback {
        void accept(final List<ItemStack> list) throws CommandSyntaxException;
    }
}
