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(cz -> cz.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 cz, final BlockPos fu) throws CommandSyntaxException {
        final BlockEntity cdl3 = cz.getLevel().getBlockEntity(fu);
        if (!(cdl3 instanceof Container)) {
            throw ReplaceItemCommand.ERROR_NOT_A_CONTAINER.create();
        }
        return (Container)cdl3;
    }
    
    private static int blockDistribute(final CommandSourceStack cz, final BlockPos fu, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final Container amz5 = getContainer(cz, fu);
        final List<ItemStack> list2 = Lists.newArrayListWithCapacity(list.size());
        for (final ItemStack bki8 : list) {
            if (distributeToContainer(amz5, bki8.copy())) {
                amz5.setChanged();
                list2.add(bki8);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static boolean distributeToContainer(final Container amz, final ItemStack bki) {
        boolean boolean3 = false;
        for (int integer4 = 0; integer4 < amz.getContainerSize() && !bki.isEmpty(); ++integer4) {
            final ItemStack bki2 = amz.getItem(integer4);
            if (amz.canPlaceItem(integer4, bki)) {
                if (bki2.isEmpty()) {
                    amz.setItem(integer4, bki);
                    boolean3 = true;
                    break;
                }
                if (canMergeItems(bki2, bki)) {
                    final int integer5 = bki.getMaxStackSize() - bki2.getCount();
                    final int integer6 = Math.min(bki.getCount(), integer5);
                    bki.shrink(integer6);
                    bki2.grow(integer6);
                    boolean3 = true;
                }
            }
        }
        return boolean3;
    }
    
    private static int blockReplace(final CommandSourceStack cz, final BlockPos fu, final int integer3, final int integer4, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final Container amz7 = getContainer(cz, fu);
        final int integer5 = amz7.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 bki12 = (integer6 < list.size()) ? list.get(integer6) : ItemStack.EMPTY;
            if (amz7.canPlaceItem(integer7, bki12)) {
                amz7.setItem(integer7, bki12);
                list2.add(bki12);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static boolean canMergeItems(final ItemStack bki1, final ItemStack bki2) {
        return bki1.getItem() == bki2.getItem() && bki1.getDamageValue() == bki2.getDamageValue() && bki1.getCount() <= bki1.getMaxStackSize() && Objects.equals(bki1.getTag(), bki2.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 bki6 : list) {
            for (final ServerPlayer ze8 : collection) {
                if (ze8.inventory.add(bki6.copy())) {
                    list2.add(bki6);
                }
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static void setSlots(final Entity aom, final List<ItemStack> list2, final int integer3, final int integer4, final List<ItemStack> list5) {
        for (int integer5 = 0; integer5 < integer4; ++integer5) {
            final ItemStack bki7 = (integer5 < list2.size()) ? list2.get(integer5) : ItemStack.EMPTY;
            if (aom.setSlot(integer3 + integer5, bki7.copy())) {
                list5.add(bki7);
            }
        }
    }
    
    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 aom8 : collection) {
            if (aom8 instanceof ServerPlayer) {
                final ServerPlayer ze9 = (ServerPlayer)aom8;
                ze9.inventoryMenu.broadcastChanges();
                setSlots(aom8, list, integer2, integer3, list2);
                ze9.inventoryMenu.broadcastChanges();
            }
            else {
                setSlots(aom8, list, integer2, integer3, list2);
            }
        }
        a.accept(list2);
        return list2.size();
    }
    
    private static int dropInWorld(final CommandSourceStack cz, final Vec3 dem, final List<ItemStack> list, final Callback a) throws CommandSyntaxException {
        final ServerLevel zd5 = cz.getLevel();
        final ServerLevel bqb;
        final ItemEntity bbg4;
        list.forEach(bki -> {
            bbg4 = new ItemEntity(bqb, dem.x, dem.y, dem.z, bki.copy());
            bbg4.setDefaultPickUpDelay();
            bqb.addFreshEntity(bbg4);
            return;
        });
        a.accept(list);
        return list.size();
    }
    
    private static void callback(final CommandSourceStack cz, final List<ItemStack> list) {
        if (list.size() == 1) {
            final ItemStack bki3 = list.get(0);
            cz.sendSuccess(new TranslatableComponent("commands.drop.success.single", new Object[] { bki3.getCount(), bki3.getDisplayName() }), false);
        }
        else {
            cz.sendSuccess(new TranslatableComponent("commands.drop.success.multiple", new Object[] { list.size() }), false);
        }
    }
    
    private static void callback(final CommandSourceStack cz, final List<ItemStack> list, final ResourceLocation uh) {
        if (list.size() == 1) {
            final ItemStack bki4 = list.get(0);
            cz.sendSuccess(new TranslatableComponent("commands.drop.success.single_with_table", new Object[] { bki4.getCount(), bki4.getDisplayName(), uh }), false);
        }
        else {
            cz.sendSuccess(new TranslatableComponent("commands.drop.success.multiple_with_table", new Object[] { list.size(), uh }), false);
        }
    }
    
    private static ItemStack getSourceHandItem(final CommandSourceStack cz, final EquipmentSlot aor) throws CommandSyntaxException {
        final Entity aom3 = cz.getEntityOrException();
        if (aom3 instanceof LivingEntity) {
            return ((LivingEntity)aom3).getItemBySlot(aor);
        }
        throw LootCommand.ERROR_NO_HELD_ITEMS.create(aom3.getDisplayName());
    }
    
    private static int dropBlockLoot(final CommandContext<CommandSourceStack> commandContext, final BlockPos fu, final ItemStack bki, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cz5 = (CommandSourceStack)commandContext.getSource();
        final ServerLevel zd6 = cz5.getLevel();
        final BlockState cfj7 = zd6.getBlockState(fu);
        final BlockEntity cdl8 = zd6.getBlockEntity(fu);
        final LootContext.Builder a9 = new LootContext.Builder(zd6).<BlockPos>withParameter(LootContextParams.BLOCK_POS, fu).<BlockState>withParameter(LootContextParams.BLOCK_STATE, cfj7).<BlockEntity>withOptionalParameter(LootContextParams.BLOCK_ENTITY, cdl8).<Entity>withOptionalParameter(LootContextParams.THIS_ENTITY, cz5.getEntity()).<ItemStack>withParameter(LootContextParams.TOOL, bki);
        final List<ItemStack> list2 = cfj7.getDrops(a9);
        return b.accept(commandContext, list2, list -> callback(cz5, list, cfj7.getBlock().getLootTable()));
    }
    
    private static int dropKillLoot(final CommandContext<CommandSourceStack> commandContext, final Entity aom, final DropConsumer b) throws CommandSyntaxException {
        if (!(aom instanceof LivingEntity)) {
            throw LootCommand.ERROR_NO_LOOT_TABLE.create(aom.getDisplayName());
        }
        final ResourceLocation uh4 = ((LivingEntity)aom).getLootTable();
        final CommandSourceStack cz5 = (CommandSourceStack)commandContext.getSource();
        final LootContext.Builder a6 = new LootContext.Builder(cz5.getLevel());
        final Entity aom2 = cz5.getEntity();
        if (aom2 instanceof Player) {
            a6.<Player>withParameter(LootContextParams.LAST_DAMAGE_PLAYER, (Player)aom2);
        }
        a6.<DamageSource>withParameter(LootContextParams.DAMAGE_SOURCE, DamageSource.MAGIC);
        a6.<Entity>withOptionalParameter(LootContextParams.DIRECT_KILLER_ENTITY, aom2);
        a6.<Entity>withOptionalParameter(LootContextParams.KILLER_ENTITY, aom2);
        a6.<Entity>withParameter(LootContextParams.THIS_ENTITY, aom);
        a6.<BlockPos>withParameter(LootContextParams.BLOCK_POS, new BlockPos(cz5.getPosition()));
        final LootTable daw8 = cz5.getServer().getLootTables().get(uh4);
        final List<ItemStack> list2 = daw8.getRandomItems(a6.create(LootContextParamSets.ENTITY));
        return b.accept(commandContext, list2, list -> callback(cz5, list, uh4));
    }
    
    private static int dropChestLoot(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation uh, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cz4 = (CommandSourceStack)commandContext.getSource();
        final LootContext.Builder a5 = new LootContext.Builder(cz4.getLevel()).<Entity>withOptionalParameter(LootContextParams.THIS_ENTITY, cz4.getEntity()).<BlockPos>withParameter(LootContextParams.BLOCK_POS, new BlockPos(cz4.getPosition()));
        return drop(commandContext, uh, a5.create(LootContextParamSets.CHEST), b);
    }
    
    private static int dropFishingLoot(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation uh, final BlockPos fu, final ItemStack bki, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cz6 = (CommandSourceStack)commandContext.getSource();
        final LootContext dat7 = new LootContext.Builder(cz6.getLevel()).<BlockPos>withParameter(LootContextParams.BLOCK_POS, fu).<ItemStack>withParameter(LootContextParams.TOOL, bki).<Entity>withOptionalParameter(LootContextParams.THIS_ENTITY, cz6.getEntity()).create(LootContextParamSets.FISHING);
        return drop(commandContext, uh, dat7, b);
    }
    
    private static int drop(final CommandContext<CommandSourceStack> commandContext, final ResourceLocation uh, final LootContext dat, final DropConsumer b) throws CommandSyntaxException {
        final CommandSourceStack cz5 = (CommandSourceStack)commandContext.getSource();
        final LootTable daw6 = cz5.getServer().getLootTables().get(uh);
        final List<ItemStack> list2 = daw6.getRandomItems(dat);
        return b.accept(commandContext, list2, list -> callback(cz5, list));
    }
    
    static {
        SUGGEST_LOOT_TABLE = ((commandContext, suggestionsBuilder) -> {
            final LootTables dax3 = ((CommandSourceStack)commandContext.getSource()).getServer().getLootTables();
            return SharedSuggestionProvider.suggestResource(dax3.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;
    }
}
