package net.minecraft.gametest.framework;

import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.level.ServerPlayer;
import java.io.OutputStream;
import java.io.BufferedReader;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.TagParser;
import java.nio.file.OpenOption;
import java.io.Reader;
import org.apache.commons.io.IOUtils;
import java.nio.file.Path;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import net.minecraft.data.structures.NbtToSnbt;
import net.minecraft.resources.ResourceLocation;
import java.nio.file.Paths;
import java.util.function.Consumer;
import net.minecraft.util.Mth;
import javax.annotation.Nullable;
import java.util.Collection;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Optional;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Style;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.block.entity.StructureBlockEntity;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.level.block.Block;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.properties.Property;
import java.util.Set;
import net.minecraft.commands.arguments.blocks.BlockInput;
import java.util.Collections;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.core.BlockPos;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.arguments.ArgumentType;
import net.minecraft.commands.Commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.CommandDispatcher;

public class TestCommand {
    public static void register(final CommandDispatcher<CommandSourceStack> commandDispatcher) {
        commandDispatcher.register((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("test").then(Commands.literal("runthis").executes(commandContext -> runNearbyTest((CommandSourceStack)commandContext.getSource())))).then(Commands.literal("runthese").executes(commandContext -> runAllNearbyTests((CommandSourceStack)commandContext.getSource())))).then(Commands.literal("run").then(Commands.argument("testName", (com.mojang.brigadier.arguments.ArgumentType<Object>)TestFunctionArgument.testFunctionArgument()).executes(commandContext -> runTest((CommandSourceStack)commandContext.getSource(), TestFunctionArgument.getTestFunction((CommandContext<CommandSourceStack>)commandContext, "testName")))))).then(((LiteralArgumentBuilder)Commands.literal("runall").executes(commandContext -> runAllTests((CommandSourceStack)commandContext.getSource()))).then(Commands.argument("testClassName", (com.mojang.brigadier.arguments.ArgumentType<Object>)TestClassNameArgument.testClassName()).executes(commandContext -> runAllTestsInClass((CommandSourceStack)commandContext.getSource(), TestClassNameArgument.getTestClassName((CommandContext<CommandSourceStack>)commandContext, "testClassName")))))).then(Commands.literal("export").then(Commands.argument("testName", (com.mojang.brigadier.arguments.ArgumentType<Object>)StringArgumentType.word()).executes(commandContext -> exportTestStructure((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "testName")))))).then(Commands.literal("import").then(Commands.argument("testName", (com.mojang.brigadier.arguments.ArgumentType<Object>)StringArgumentType.word()).executes(commandContext -> importTestStructure((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "testName")))))).then(((LiteralArgumentBuilder)Commands.literal("pos").executes(commandContext -> showPos((CommandSourceStack)commandContext.getSource(), "pos"))).then(Commands.argument("var", (com.mojang.brigadier.arguments.ArgumentType<Object>)StringArgumentType.word()).executes(commandContext -> showPos((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "var")))))).then(Commands.literal("create").then(((RequiredArgumentBuilder)Commands.argument("testName", (com.mojang.brigadier.arguments.ArgumentType<Object>)StringArgumentType.word()).executes(commandContext -> createNewStructure((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "testName"), 5, 5, 5))).then(((RequiredArgumentBuilder)Commands.argument("width", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> createNewStructure((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "testName"), IntegerArgumentType.getInteger(commandContext, "width"), IntegerArgumentType.getInteger(commandContext, "width"), IntegerArgumentType.getInteger(commandContext, "width")))).then(Commands.argument("height", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).then(Commands.argument("depth", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> createNewStructure((CommandSourceStack)commandContext.getSource(), StringArgumentType.getString(commandContext, "testName"), IntegerArgumentType.getInteger(commandContext, "width"), IntegerArgumentType.getInteger(commandContext, "height"), IntegerArgumentType.getInteger(commandContext, "depth"))))))))).then(((LiteralArgumentBuilder)Commands.literal("clearall").executes(commandContext -> clearAllTests((CommandSourceStack)commandContext.getSource(), 200))).then(Commands.argument("radius", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> clearAllTests((CommandSourceStack)commandContext.getSource(), IntegerArgumentType.getInteger(commandContext, "radius"))))));
    }
    
    private static int createNewStructure(final CommandSourceStack cq, final String string, final int integer3, final int integer4, final int integer5) {
        if (integer3 > 32 || integer4 > 32 || integer5 > 32) {
            throw new IllegalArgumentException("The structure must be less than 32 blocks big in each axis");
        }
        final ServerLevel xd6 = cq.getLevel();
        final BlockPos fk7 = new BlockPos(cq.getPosition());
        final BlockPos fk8 = new BlockPos(fk7.getX(), cq.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fk7).getY(), fk7.getZ() + 3);
        StructureUtils.createNewEmptyStructureBlock(string.toLowerCase(), fk8, new BlockPos(integer3, integer4, integer5), 2, xd6);
        for (int integer6 = 0; integer6 < integer3; ++integer6) {
            for (int integer7 = 0; integer7 < integer5; ++integer7) {
                final BlockPos fk9 = new BlockPos(fk8.getX() + integer6, fk8.getY() + 1, fk8.getZ() + integer7);
                final Block bph12 = Blocks.POLISHED_ANDESITE;
                final BlockInput ds13 = new BlockInput(bph12.defaultBlockState(), Collections.EMPTY_SET, null);
                ds13.place(xd6, fk9, 2);
            }
        }
        StructureUtils.addCommandBlockAndButtonToStartTest(fk8.offset(1, 0, -1), xd6);
        return 0;
    }
    
    private static int showPos(final CommandSourceStack cq, final String string) throws CommandSyntaxException {
        final BlockHitResult cvg3 = (BlockHitResult)cq.getPlayerOrException().pick(10.0, 1.0f, false);
        final BlockPos fk4 = cvg3.getBlockPos();
        final ServerLevel xd5 = cq.getLevel();
        Optional<BlockPos> optional6 = StructureUtils.findStructureBlockContainingPos(fk4, 15, xd5);
        if (!optional6.isPresent()) {
            optional6 = StructureUtils.findStructureBlockContainingPos(fk4, 200, xd5);
        }
        if (!optional6.isPresent()) {
            cq.sendFailure(new TextComponent("Can't find a structure block that contains the targeted pos " + fk4));
            return 0;
        }
        final StructureBlockEntity bxl7 = (StructureBlockEntity)xd5.getBlockEntity(optional6.get());
        final BlockPos fk5 = fk4.subtract(optional6.get());
        final String string2 = fk5.getX() + ", " + fk5.getY() + ", " + fk5.getZ();
        final String string3 = bxl7.getStructurePath();
        final Component lf11 = new TextComponent(string2).setStyle(new Style().setBold(true).setColor(ChatFormatting.GREEN).setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponent("Click to copy to clipboard"))).setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, "final BlockPos " + string + " = new BlockPos(" + string2 + ");")));
        cq.sendSuccess(new TextComponent("Position relative to " + string3 + ": ").append(lf11), false);
        DebugPackets.sendGameTestAddMarker(xd5, new BlockPos(fk4), string2, -2147418368, 10000);
        return 1;
    }
    
    private static int runNearbyTest(final CommandSourceStack cq) {
        final BlockPos fk2 = new BlockPos(cq.getPosition());
        final ServerLevel xd3 = cq.getLevel();
        final BlockPos fk3 = StructureUtils.findNearestStructureBlock(fk2, 15, xd3);
        if (fk3 == null) {
            say(xd3, "Couldn't find any structure block within 15 radius", ChatFormatting.RED);
            return 0;
        }
        GameTestRunner.clearMarkers(xd3);
        runTest(xd3, fk3, null);
        return 1;
    }
    
    private static int runAllNearbyTests(final CommandSourceStack cq) {
        final BlockPos fk2 = new BlockPos(cq.getPosition());
        final ServerLevel xd3 = cq.getLevel();
        final Collection<BlockPos> collection4 = StructureUtils.findStructureBlocks(fk2, 200, xd3);
        if (collection4.isEmpty()) {
            say(xd3, "Couldn't find any structure blocks within 200 block radius", ChatFormatting.RED);
            return 1;
        }
        GameTestRunner.clearMarkers(xd3);
        say(cq, "Running " + collection4.size() + " tests...");
        final MultipleTestTracker jf5 = new MultipleTestTracker();
        collection4.forEach(fk -> runTest(xd3, fk, jf5));
        return 1;
    }
    
    private static void runTest(final ServerLevel xd, final BlockPos fk, @Nullable final MultipleTestTracker jf) {
        final StructureBlockEntity bxl4 = (StructureBlockEntity)xd.getBlockEntity(fk);
        final String string5 = bxl4.getStructurePath();
        final TestFunction jk6 = GameTestRegistry.getTestFunction(string5);
        final GameTestInfo iw7 = new GameTestInfo(jk6, fk, xd);
        if (jf != null) {
            jf.add(iw7);
            iw7.addListener(new TestSummaryDisplayer(xd, jf));
        }
        runTestPreparation(jk6, xd);
        GameTestRunner.runTest(iw7, GameTestTicker.singleton);
    }
    
    private static void showTestSummaryIfAllDone(final ServerLevel xd, final MultipleTestTracker jf) {
        if (jf.isDone()) {
            say(xd, "GameTest done! " + jf.getTotalCount() + " tests were run", ChatFormatting.WHITE);
            if (jf.hasFailedRequired()) {
                say(xd, "" + jf.getFailedRequiredCount() + " required tests failed :(", ChatFormatting.RED);
            }
            else {
                say(xd, "All required tests passed :)", ChatFormatting.GREEN);
            }
            if (jf.hasFailedOptional()) {
                say(xd, "" + jf.getFailedOptionalCount() + " optional tests failed", ChatFormatting.GRAY);
            }
        }
    }
    
    private static int clearAllTests(final CommandSourceStack cq, final int integer) {
        final ServerLevel xd3 = cq.getLevel();
        GameTestRunner.clearMarkers(xd3);
        final BlockPos fk4 = new BlockPos(cq.getPosition().x, cq.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, new BlockPos(cq.getPosition())).getY(), cq.getPosition().z);
        GameTestRunner.clearAllTests(xd3, fk4, GameTestTicker.singleton, Mth.clamp(integer, 0, 1024));
        return 1;
    }
    
    private static int runTest(final CommandSourceStack cq, final TestFunction jk) {
        final ServerLevel xd3 = cq.getLevel();
        final BlockPos fk4 = new BlockPos(cq.getPosition());
        final BlockPos fk5 = new BlockPos(fk4.getX(), cq.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fk4).getY(), fk4.getZ() + 3);
        GameTestRunner.clearMarkers(xd3);
        runTestPreparation(jk, xd3);
        final GameTestInfo iw6 = new GameTestInfo(jk, fk5, xd3);
        GameTestRunner.runTest(iw6, GameTestTicker.singleton);
        return 1;
    }
    
    private static void runTestPreparation(final TestFunction jk, final ServerLevel xd) {
        final Consumer<ServerLevel> consumer3 = GameTestRegistry.getBeforeBatchFunction(jk.getBatchName());
        if (consumer3 != null) {
            consumer3.accept(xd);
        }
    }
    
    private static int runAllTests(final CommandSourceStack cq) {
        GameTestRunner.clearMarkers(cq.getLevel());
        runTests(cq, GameTestRegistry.getAllTestFunctions());
        return 1;
    }
    
    private static int runAllTestsInClass(final CommandSourceStack cq, final String string) {
        final Collection<TestFunction> collection3 = GameTestRegistry.getTestFunctionsForClassName(string);
        GameTestRunner.clearMarkers(cq.getLevel());
        runTests(cq, collection3);
        return 1;
    }
    
    private static void runTests(final CommandSourceStack cq, final Collection<TestFunction> collection) {
        final BlockPos fk3 = new BlockPos(cq.getPosition());
        final BlockPos fk4 = new BlockPos(fk3.getX(), cq.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fk3).getY(), fk3.getZ() + 3);
        final ServerLevel xd5 = cq.getLevel();
        say(cq, "Running " + collection.size() + " tests...");
        final Collection<GameTestInfo> collection2 = GameTestRunner.runTests(collection, fk4, xd5, GameTestTicker.singleton);
        final MultipleTestTracker jf7 = new MultipleTestTracker(collection2);
        jf7.setListener(new TestSummaryDisplayer(xd5, jf7));
    }
    
    private static void say(final CommandSourceStack cq, final String string) {
        cq.sendSuccess(new TextComponent(string), false);
    }
    
    private static int exportTestStructure(final CommandSourceStack cq, final String string) {
        final Path path3 = Paths.get(StructureUtils.testStructuresDir);
        final ResourceLocation sm4 = new ResourceLocation("minecraft", string);
        final Path path4 = cq.getLevel().getStructureManager().createPathToStructure(sm4, ".nbt");
        final Path path5 = NbtToSnbt.convertStructure(path4, string, path3);
        if (path5 == null) {
            say(cq, "Failed to export " + path4);
            return 1;
        }
        try {
            Files.createDirectories(path5.getParent(), new FileAttribute[0]);
        }
        catch (IOException iOException7) {
            say(cq, "Could not create folder " + path5.getParent());
            iOException7.printStackTrace();
            return 1;
        }
        say(cq, "Exported to " + path5.toAbsolutePath());
        return 0;
    }
    
    private static int importTestStructure(final CommandSourceStack cq, final String string) {
        final Path path3 = Paths.get(StructureUtils.testStructuresDir, string + ".snbt");
        final ResourceLocation sm4 = new ResourceLocation("minecraft", string);
        final Path path4 = cq.getLevel().getStructureManager().createPathToStructure(sm4, ".nbt");
        try {
            final BufferedReader bufferedReader6 = Files.newBufferedReader(path3);
            final String string2 = IOUtils.toString((Reader)bufferedReader6);
            Files.createDirectories(path4.getParent(), new FileAttribute[0]);
            final OutputStream outputStream8 = Files.newOutputStream(path4);
            NbtIo.writeCompressed(TagParser.parseTag(string2), outputStream8);
            say(cq, "Imported to " + path4.toAbsolutePath());
            return 0;
        }
        catch (IOException | CommandSyntaxException ex2) {
            final Exception ex;
            final Exception exception6 = ex;
            System.err.println("Failed to load structure " + string);
            exception6.printStackTrace();
            return 1;
        }
    }
    
    private static void say(final ServerLevel xd, final String string, final ChatFormatting g) {
        final TextComponent lf;
        xd.getPlayers(xe -> true).forEach(xe -> {
            new TextComponent(g + string);
            xe.sendMessage(lf);
        });
    }
    
    static class TestSummaryDisplayer implements GameTestListener {
        private final ServerLevel level;
        private final MultipleTestTracker tracker;
        
        public TestSummaryDisplayer(final ServerLevel xd, final MultipleTestTracker jf) {
            this.level = xd;
            this.tracker = jf;
        }
        
        @Override
        public void testStructureLoaded(final GameTestInfo iw) {
        }
        
        @Override
        public void testFailed(final GameTestInfo iw) {
            showTestSummaryIfAllDone(this.level, this.tracker);
        }
    }
}
