package net.minecraft.gametest.framework;

import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.Util;
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.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Consumer;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.AABB;
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.block.Rotation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.core.BlockPos;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
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)((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(((LiteralArgumentBuilder)Commands.literal("runfailed").executes(commandContext -> runLastFailedTests((CommandSourceStack)commandContext.getSource(), false, 0, 8))).then(((RequiredArgumentBuilder)Commands.argument("onlyRequiredTests", (com.mojang.brigadier.arguments.ArgumentType<Object>)BoolArgumentType.bool()).executes(commandContext -> runLastFailedTests((CommandSourceStack)commandContext.getSource(), BoolArgumentType.getBool(commandContext, "onlyRequiredTests"), 0, 8))).then(((RequiredArgumentBuilder)Commands.argument("rotationSteps", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runLastFailedTests((CommandSourceStack)commandContext.getSource(), BoolArgumentType.getBool(commandContext, "onlyRequiredTests"), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), 8))).then(Commands.argument("testsPerRow", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runLastFailedTests((CommandSourceStack)commandContext.getSource(), BoolArgumentType.getBool(commandContext, "onlyRequiredTests"), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), IntegerArgumentType.getInteger(commandContext, "testsPerRow")))))))).then(Commands.literal("run").then(((RequiredArgumentBuilder)Commands.argument("testName", (com.mojang.brigadier.arguments.ArgumentType<Object>)TestFunctionArgument.testFunctionArgument()).executes(commandContext -> runTest((CommandSourceStack)commandContext.getSource(), TestFunctionArgument.getTestFunction((CommandContext<CommandSourceStack>)commandContext, "testName"), 0))).then(Commands.argument("rotationSteps", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runTest((CommandSourceStack)commandContext.getSource(), TestFunctionArgument.getTestFunction((CommandContext<CommandSourceStack>)commandContext, "testName"), IntegerArgumentType.getInteger(commandContext, "rotationSteps"))))))).then(((LiteralArgumentBuilder)((LiteralArgumentBuilder)Commands.literal("runall").executes(commandContext -> runAllTests((CommandSourceStack)commandContext.getSource(), 0, 8))).then(((RequiredArgumentBuilder)Commands.argument("testClassName", (com.mojang.brigadier.arguments.ArgumentType<Object>)TestClassNameArgument.testClassName()).executes(commandContext -> runAllTestsInClass((CommandSourceStack)commandContext.getSource(), TestClassNameArgument.getTestClassName((CommandContext<CommandSourceStack>)commandContext, "testClassName"), 0, 8))).then(((RequiredArgumentBuilder)Commands.argument("rotationSteps", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runAllTestsInClass((CommandSourceStack)commandContext.getSource(), TestClassNameArgument.getTestClassName((CommandContext<CommandSourceStack>)commandContext, "testClassName"), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), 8))).then(Commands.argument("testsPerRow", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runAllTestsInClass((CommandSourceStack)commandContext.getSource(), TestClassNameArgument.getTestClassName((CommandContext<CommandSourceStack>)commandContext, "testClassName"), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), IntegerArgumentType.getInteger(commandContext, "testsPerRow"))))))).then(((RequiredArgumentBuilder)Commands.argument("rotationSteps", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runAllTests((CommandSourceStack)commandContext.getSource(), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), 8))).then(Commands.argument("testsPerRow", (com.mojang.brigadier.arguments.ArgumentType<Object>)IntegerArgumentType.integer()).executes(commandContext -> runAllTests((CommandSourceStack)commandContext.getSource(), IntegerArgumentType.getInteger(commandContext, "rotationSteps"), IntegerArgumentType.getInteger(commandContext, "testsPerRow"))))))).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("exportthis").executes(commandContext -> exportNearestTestStructure((CommandSourceStack)commandContext.getSource())))).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 cz, final String string, final int integer3, final int integer4, final int integer5) {
        if (integer3 > 48 || integer4 > 48 || integer5 > 48) {
            throw new IllegalArgumentException("The structure must be less than 48 blocks big in each axis");
        }
        final ServerLevel zd6 = cz.getLevel();
        final BlockPos fu7 = new BlockPos(cz.getPosition());
        final BlockPos fu8 = new BlockPos(fu7.getX(), cz.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fu7).getY(), fu7.getZ() + 3);
        StructureUtils.createNewEmptyStructureBlock(string.toLowerCase(), fu8, new BlockPos(integer3, integer4, integer5), Rotation.NONE, zd6);
        for (int integer6 = 0; integer6 < integer3; ++integer6) {
            for (int integer7 = 0; integer7 < integer5; ++integer7) {
                final BlockPos fu9 = new BlockPos(fu8.getX() + integer6, fu8.getY() + 1, fu8.getZ() + integer7);
                final Block bvr12 = Blocks.POLISHED_ANDESITE;
                final BlockInput ec13 = new BlockInput(bvr12.defaultBlockState(), Collections.EMPTY_SET, null);
                ec13.place(zd6, fu9, 2);
            }
        }
        StructureUtils.addCommandBlockAndButtonToStartTest(fu8, new BlockPos(1, 0, -1), Rotation.NONE, zd6);
        return 0;
    }
    
    private static int showPos(final CommandSourceStack cz, final String string) throws CommandSyntaxException {
        final BlockHitResult deh3 = (BlockHitResult)cz.getPlayerOrException().pick(10.0, 1.0f, false);
        final BlockPos fu4 = deh3.getBlockPos();
        final ServerLevel zd5 = cz.getLevel();
        Optional<BlockPos> optional6 = StructureUtils.findStructureBlockContainingPos(fu4, 15, zd5);
        if (!optional6.isPresent()) {
            optional6 = StructureUtils.findStructureBlockContainingPos(fu4, 200, zd5);
        }
        if (!optional6.isPresent()) {
            cz.sendFailure(new TextComponent("Can't find a structure block that contains the targeted pos " + fu4));
            return 0;
        }
        final StructureBlockEntity cel7 = (StructureBlockEntity)zd5.getBlockEntity(optional6.get());
        final BlockPos fu5 = fu4.subtract(optional6.get());
        final String string2 = fu5.getX() + ", " + fu5.getY() + ", " + fu5.getZ();
        final String string3 = cel7.getStructurePath();
        final Component mr11 = new TextComponent(string2).setStyle(Style.EMPTY.withBold(true).withColor(ChatFormatting.GREEN).withHoverEvent(new HoverEvent((HoverEvent.Action<T>)HoverEvent.Action.SHOW_TEXT, (T)new TextComponent("Click to copy to clipboard"))).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, "final BlockPos " + string + " = new BlockPos(" + string2 + ");")));
        cz.sendSuccess(new TextComponent("Position relative to " + string3 + ": ").append(mr11), false);
        DebugPackets.sendGameTestAddMarker(zd5, new BlockPos(fu4), string2, -2147418368, 10000);
        return 1;
    }
    
    private static int runNearbyTest(final CommandSourceStack cz) {
        final BlockPos fu2 = new BlockPos(cz.getPosition());
        final ServerLevel zd3 = cz.getLevel();
        final BlockPos fu3 = StructureUtils.findNearestStructureBlock(fu2, 15, zd3);
        if (fu3 == null) {
            say(zd3, "Couldn't find any structure block within 15 radius", ChatFormatting.RED);
            return 0;
        }
        GameTestRunner.clearMarkers(zd3);
        runTest(zd3, fu3, null);
        return 1;
    }
    
    private static int runAllNearbyTests(final CommandSourceStack cz) {
        final BlockPos fu2 = new BlockPos(cz.getPosition());
        final ServerLevel zd3 = cz.getLevel();
        final Collection<BlockPos> collection4 = StructureUtils.findStructureBlocks(fu2, 200, zd3);
        if (collection4.isEmpty()) {
            say(zd3, "Couldn't find any structure blocks within 200 block radius", ChatFormatting.RED);
            return 1;
        }
        GameTestRunner.clearMarkers(zd3);
        say(cz, "Running " + collection4.size() + " tests...");
        final MultipleTestTracker kq5 = new MultipleTestTracker();
        collection4.forEach(fu -> runTest(zd3, fu, kq5));
        return 1;
    }
    
    private static void runTest(final ServerLevel zd, final BlockPos fu, @Nullable final MultipleTestTracker kq) {
        final StructureBlockEntity cel4 = (StructureBlockEntity)zd.getBlockEntity(fu);
        final String string5 = cel4.getStructurePath();
        final TestFunction kv6 = GameTestRegistry.getTestFunction(string5);
        final GameTestInfo kg7 = new GameTestInfo(kv6, cel4.getRotation(), zd);
        if (kq != null) {
            kq.addTestToTrack(kg7);
            kg7.addListener(new TestSummaryDisplayer(zd, kq));
        }
        runTestPreparation(kv6, zd);
        final AABB deg8 = StructureUtils.getStructureBounds(cel4);
        final BlockPos fu2 = new BlockPos(deg8.minX, deg8.minY, deg8.minZ);
        GameTestRunner.runTest(kg7, fu2, GameTestTicker.singleton);
    }
    
    private static void showTestSummaryIfAllDone(final ServerLevel zd, final MultipleTestTracker kq) {
        if (kq.isDone()) {
            say(zd, "GameTest done! " + kq.getTotalCount() + " tests were run", ChatFormatting.WHITE);
            if (kq.hasFailedRequired()) {
                say(zd, "" + kq.getFailedRequiredCount() + " required tests failed :(", ChatFormatting.RED);
            }
            else {
                say(zd, "All required tests passed :)", ChatFormatting.GREEN);
            }
            if (kq.hasFailedOptional()) {
                say(zd, "" + kq.getFailedOptionalCount() + " optional tests failed", ChatFormatting.GRAY);
            }
        }
    }
    
    private static int clearAllTests(final CommandSourceStack cz, final int integer) {
        final ServerLevel zd3 = cz.getLevel();
        GameTestRunner.clearMarkers(zd3);
        final BlockPos fu4 = new BlockPos(cz.getPosition().x, cz.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, new BlockPos(cz.getPosition())).getY(), cz.getPosition().z);
        GameTestRunner.clearAllTests(zd3, fu4, GameTestTicker.singleton, Mth.clamp(integer, 0, 1024));
        return 1;
    }
    
    private static int runTest(final CommandSourceStack cz, final TestFunction kv, final int integer) {
        final ServerLevel zd4 = cz.getLevel();
        final BlockPos fu5 = new BlockPos(cz.getPosition());
        final int integer2 = cz.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fu5).getY();
        final BlockPos fu6 = new BlockPos(fu5.getX(), integer2, fu5.getZ() + 3);
        GameTestRunner.clearMarkers(zd4);
        runTestPreparation(kv, zd4);
        final Rotation cap8 = StructureUtils.getRotationForRotationSteps(integer);
        final GameTestInfo kg9 = new GameTestInfo(kv, cap8, zd4);
        GameTestRunner.runTest(kg9, fu6, GameTestTicker.singleton);
        return 1;
    }
    
    private static void runTestPreparation(final TestFunction kv, final ServerLevel zd) {
        final Consumer<ServerLevel> consumer3 = GameTestRegistry.getBeforeBatchFunction(kv.getBatchName());
        if (consumer3 != null) {
            consumer3.accept(zd);
        }
    }
    
    private static int runAllTests(final CommandSourceStack cz, final int integer2, final int integer3) {
        GameTestRunner.clearMarkers(cz.getLevel());
        final Collection<TestFunction> collection4 = GameTestRegistry.getAllTestFunctions();
        say(cz, "Running all " + collection4.size() + " tests...");
        GameTestRegistry.forgetFailedTests();
        runTests(cz, collection4, integer2, integer3);
        return 1;
    }
    
    private static int runAllTestsInClass(final CommandSourceStack cz, final String string, final int integer3, final int integer4) {
        final Collection<TestFunction> collection5 = GameTestRegistry.getTestFunctionsForClassName(string);
        GameTestRunner.clearMarkers(cz.getLevel());
        say(cz, "Running " + collection5.size() + " tests from " + string + "...");
        GameTestRegistry.forgetFailedTests();
        runTests(cz, collection5, integer3, integer4);
        return 1;
    }
    
    private static int runLastFailedTests(final CommandSourceStack cz, final boolean boolean2, final int integer3, final int integer4) {
        Collection<TestFunction> collection5;
        if (boolean2) {
            collection5 = GameTestRegistry.getLastFailedTests().stream().filter(TestFunction::isRequired).collect(Collectors.toList());
        }
        else {
            collection5 = GameTestRegistry.getLastFailedTests();
        }
        if (collection5.isEmpty()) {
            say(cz, "No failed tests to rerun");
            return 0;
        }
        GameTestRunner.clearMarkers(cz.getLevel());
        say(cz, "Rerunning " + collection5.size() + " failed tests (" + (boolean2 ? "only required tests" : "including optional tests") + ")");
        runTests(cz, collection5, integer3, integer4);
        return 1;
    }
    
    private static void runTests(final CommandSourceStack cz, final Collection<TestFunction> collection, final int integer3, final int integer4) {
        final BlockPos fu5 = new BlockPos(cz.getPosition());
        final BlockPos fu6 = new BlockPos(fu5.getX(), cz.getLevel().getHeightmapPos(Heightmap.Types.WORLD_SURFACE, fu5).getY(), fu5.getZ() + 3);
        final ServerLevel zd7 = cz.getLevel();
        final Rotation cap8 = StructureUtils.getRotationForRotationSteps(integer3);
        final Collection<GameTestInfo> collection2 = GameTestRunner.runTests(collection, fu6, cap8, zd7, GameTestTicker.singleton, integer4);
        final MultipleTestTracker kq10 = new MultipleTestTracker(collection2);
        kq10.addListener(new TestSummaryDisplayer(zd7, kq10));
        kq10.addFailureListener(kg -> GameTestRegistry.rememberFailedTest(kg.getTestFunction()));
    }
    
    private static void say(final CommandSourceStack cz, final String string) {
        cz.sendSuccess(new TextComponent(string), false);
    }
    
    private static int exportNearestTestStructure(final CommandSourceStack cz) {
        final BlockPos fu2 = new BlockPos(cz.getPosition());
        final ServerLevel zd3 = cz.getLevel();
        final BlockPos fu3 = StructureUtils.findNearestStructureBlock(fu2, 15, zd3);
        if (fu3 == null) {
            say(zd3, "Couldn't find any structure block within 15 radius", ChatFormatting.RED);
            return 0;
        }
        final StructureBlockEntity cel5 = (StructureBlockEntity)zd3.getBlockEntity(fu3);
        final String string6 = cel5.getStructurePath();
        return exportTestStructure(cz, string6);
    }
    
    private static int exportTestStructure(final CommandSourceStack cz, final String string) {
        final Path path3 = Paths.get(StructureUtils.testStructuresDir);
        final ResourceLocation uh4 = new ResourceLocation("minecraft", string);
        final Path path4 = cz.getLevel().getStructureManager().createPathToStructure(uh4, ".nbt");
        final Path path5 = NbtToSnbt.convertStructure(path4, string, path3);
        if (path5 == null) {
            say(cz, "Failed to export " + path4);
            return 1;
        }
        try {
            Files.createDirectories(path5.getParent(), new FileAttribute[0]);
        }
        catch (IOException iOException7) {
            say(cz, "Could not create folder " + path5.getParent());
            iOException7.printStackTrace();
            return 1;
        }
        say(cz, "Exported " + string + " to " + path5.toAbsolutePath());
        return 0;
    }
    
    private static int importTestStructure(final CommandSourceStack cz, final String string) {
        final Path path3 = Paths.get(StructureUtils.testStructuresDir, string + ".snbt");
        final ResourceLocation uh4 = new ResourceLocation("minecraft", string);
        final Path path4 = cz.getLevel().getStructureManager().createPathToStructure(uh4, ".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(cz, "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 zd, final String string, final ChatFormatting i) {
        final TextComponent mr;
        zd.getPlayers(ze -> true).forEach(ze -> {
            new TextComponent(i + string);
            ze.sendMessage(mr, Util.NIL_UUID);
        });
    }
    
    static class TestSummaryDisplayer implements GameTestListener {
        private final ServerLevel level;
        private final MultipleTestTracker tracker;
        
        public TestSummaryDisplayer(final ServerLevel zd, final MultipleTestTracker kq) {
            this.level = zd;
            this.tracker = kq;
        }
        
        @Override
        public void testStructureLoaded(final GameTestInfo kg) {
        }
        
        @Override
        public void testFailed(final GameTestInfo kg) {
            showTestSummaryIfAllDone(this.level, this.tracker);
        }
    }
}
