package net.minecraft.server.commands;

import javax.annotation.Nullable;
import com.mojang.brigadier.Message;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Iterator;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import java.util.Deque;
import java.util.List;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import java.util.Collection;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.Clearable;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.LevelReader;
import com.google.common.collect.Lists;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.core.BlockPos;
import net.minecraft.commands.arguments.blocks.BlockPredicateArgument;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.arguments.ArgumentType;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.Commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.world.level.block.state.pattern.BlockInWorld;
import java.util.function.Predicate;
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;

public class CloneCommands {
    private static final SimpleCommandExceptionType ERROR_OVERLAP;
    private static final Dynamic2CommandExceptionType ERROR_AREA_TOO_LARGE;
    private static final SimpleCommandExceptionType ERROR_FAILED;
    public static final Predicate<BlockInWorld> FILTER_AIR;
    
    public static void register(final CommandDispatcher<CommandSourceStack> commandDispatcher) {
        commandDispatcher.register((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("clone").requires(cz -> cz.hasPermission(2))).then(Commands.argument("begin", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).then(Commands.argument("end", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("destination", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPosArgument.blockPos()).executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), cfn -> true, Mode.NORMAL))).then(((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("replace").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), cfn -> true, Mode.NORMAL))).then(Commands.literal("force").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), cfn -> true, Mode.FORCE)))).then(Commands.literal("move").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), cfn -> true, Mode.MOVE)))).then(Commands.literal("normal").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), cfn -> true, Mode.NORMAL))))).then(((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("masked").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), CloneCommands.FILTER_AIR, Mode.NORMAL))).then(Commands.literal("force").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), CloneCommands.FILTER_AIR, Mode.FORCE)))).then(Commands.literal("move").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), CloneCommands.FILTER_AIR, Mode.MOVE)))).then(Commands.literal("normal").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), CloneCommands.FILTER_AIR, Mode.NORMAL))))).then(Commands.literal("filtered").then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("filter", (com.mojang.brigadier.arguments.ArgumentType<Object>)BlockPredicateArgument.blockPredicate()).executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), BlockPredicateArgument.getBlockPredicate((CommandContext<CommandSourceStack>)commandContext, "filter"), Mode.NORMAL))).then(Commands.literal("force").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), BlockPredicateArgument.getBlockPredicate((CommandContext<CommandSourceStack>)commandContext, "filter"), Mode.FORCE)))).then(Commands.literal("move").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), BlockPredicateArgument.getBlockPredicate((CommandContext<CommandSourceStack>)commandContext, "filter"), Mode.MOVE)))).then(Commands.literal("normal").executes(commandContext -> clone((CommandSourceStack)commandContext.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "begin"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "end"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)commandContext, "destination"), BlockPredicateArgument.getBlockPredicate((CommandContext<CommandSourceStack>)commandContext, "filter"), Mode.NORMAL)))))))));
    }
    
    private static int clone(final CommandSourceStack cz, final BlockPos fu2, final BlockPos fu3, final BlockPos fu4, final Predicate<BlockInWorld> predicate, final Mode b) throws CommandSyntaxException {
        final BoundingBox ctd7 = new BoundingBox(fu2, fu3);
        final BlockPos fu5 = fu4.offset(ctd7.getLength());
        final BoundingBox ctd8 = new BoundingBox(fu4, fu5);
        if (!b.canOverlap() && ctd8.intersects(ctd7)) {
            throw CloneCommands.ERROR_OVERLAP.create();
        }
        final int integer10 = ctd7.getXSpan() * ctd7.getYSpan() * ctd7.getZSpan();
        if (integer10 > 32768) {
            throw CloneCommands.ERROR_AREA_TOO_LARGE.create(32768, integer10);
        }
        final ServerLevel zd11 = cz.getLevel();
        if (!zd11.hasChunksAt(fu2, fu3) || !zd11.hasChunksAt(fu4, fu5)) {
            throw BlockPosArgument.ERROR_NOT_LOADED.create();
        }
        final List<CloneBlockInfo> list12 = Lists.newArrayList();
        final List<CloneBlockInfo> list13 = Lists.newArrayList();
        final List<CloneBlockInfo> list14 = Lists.newArrayList();
        final Deque<BlockPos> deque15 = Lists.newLinkedList();
        final BlockPos fu6 = new BlockPos(ctd8.x0 - ctd7.x0, ctd8.y0 - ctd7.y0, ctd8.z0 - ctd7.z0);
        for (int integer11 = ctd7.z0; integer11 <= ctd7.z1; ++integer11) {
            for (int integer12 = ctd7.y0; integer12 <= ctd7.y1; ++integer12) {
                for (int integer13 = ctd7.x0; integer13 <= ctd7.x1; ++integer13) {
                    final BlockPos fu7 = new BlockPos(integer13, integer12, integer11);
                    final BlockPos fu8 = fu7.offset(fu6);
                    final BlockInWorld cfn22 = new BlockInWorld(zd11, fu7, false);
                    final BlockState cfj23 = cfn22.getState();
                    if (predicate.test(cfn22)) {
                        final BlockEntity cdl24 = zd11.getBlockEntity(fu7);
                        if (cdl24 != null) {
                            final CompoundTag le25 = cdl24.save(new CompoundTag());
                            list13.add(new CloneBlockInfo(fu8, cfj23, le25));
                            deque15.addLast(fu7);
                        }
                        else if (cfj23.isSolidRender(zd11, fu7) || cfj23.isCollisionShapeFullBlock(zd11, fu7)) {
                            list12.add(new CloneBlockInfo(fu8, cfj23, null));
                            deque15.addLast(fu7);
                        }
                        else {
                            list14.add(new CloneBlockInfo(fu8, cfj23, null));
                            deque15.addFirst(fu7);
                        }
                    }
                }
            }
        }
        if (b == Mode.MOVE) {
            for (final BlockPos fu9 : deque15) {
                final BlockEntity cdl25 = zd11.getBlockEntity(fu9);
                Clearable.tryClear(cdl25);
                zd11.setBlock(fu9, Blocks.BARRIER.defaultBlockState(), 2);
            }
            for (final BlockPos fu9 : deque15) {
                zd11.setBlock(fu9, Blocks.AIR.defaultBlockState(), 3);
            }
        }
        final List<CloneBlockInfo> list15 = Lists.newArrayList();
        list15.addAll(list12);
        list15.addAll(list13);
        list15.addAll(list14);
        final List<CloneBlockInfo> list16 = Lists.<CloneBlockInfo>reverse(list15);
        for (final CloneBlockInfo a20 : list16) {
            final BlockEntity cdl26 = zd11.getBlockEntity(a20.pos);
            Clearable.tryClear(cdl26);
            zd11.setBlock(a20.pos, Blocks.BARRIER.defaultBlockState(), 2);
        }
        int integer13 = 0;
        for (final CloneBlockInfo a21 : list15) {
            if (zd11.setBlock(a21.pos, a21.state, 2)) {
                ++integer13;
            }
        }
        for (final CloneBlockInfo a21 : list13) {
            final BlockEntity cdl27 = zd11.getBlockEntity(a21.pos);
            if (a21.tag != null && cdl27 != null) {
                a21.tag.putInt("x", a21.pos.getX());
                a21.tag.putInt("y", a21.pos.getY());
                a21.tag.putInt("z", a21.pos.getZ());
                cdl27.load(a21.state, a21.tag);
                cdl27.setChanged();
            }
            zd11.setBlock(a21.pos, a21.state, 2);
        }
        for (final CloneBlockInfo a21 : list16) {
            zd11.blockUpdated(a21.pos, a21.state.getBlock());
        }
        zd11.getBlockTicks().copy(ctd7, fu6);
        if (integer13 == 0) {
            throw CloneCommands.ERROR_FAILED.create();
        }
        cz.sendSuccess(new TranslatableComponent("commands.clone.success", new Object[] { integer13 }), true);
        return integer13;
    }
    
    static {
        ERROR_OVERLAP = new SimpleCommandExceptionType((Message)new TranslatableComponent("commands.clone.overlap"));
        ERROR_AREA_TOO_LARGE = new Dynamic2CommandExceptionType((object1, object2) -> new TranslatableComponent("commands.clone.toobig", new Object[] { object1, object2 }));
        ERROR_FAILED = new SimpleCommandExceptionType((Message)new TranslatableComponent("commands.clone.failed"));
        FILTER_AIR = (cfn -> !cfn.getState().isAir());
    }
    
    enum Mode {
        FORCE(true), 
        MOVE(true), 
        NORMAL(false);
        
        private final boolean canOverlap;
        
        private Mode(final boolean boolean3) {
            this.canOverlap = boolean3;
        }
        
        public boolean canOverlap() {
            return this.canOverlap;
        }
    }
    
    static class CloneBlockInfo {
        public final BlockPos pos;
        public final BlockState state;
        @Nullable
        public final CompoundTag tag;
        
        public CloneBlockInfo(final BlockPos fu, final BlockState cfj, @Nullable final CompoundTag le) {
            this.pos = fu;
            this.state = cfj;
            this.tag = le;
        }
    }
}
