// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.universe.world.commands.block;

import com.hypixel.hytale.server.core.command.system.arguments.system.Argument;
import com.hypixel.hytale.server.core.command.system.arguments.types.EnumArgumentType;
import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import com.hypixel.hytale.server.core.prefab.selection.SelectionProvider;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.core.util.FillerBlockUtil;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.shape.Box;
import com.hypixel.hytale.server.core.asset.type.blockhitbox.BlockBoundingBoxes;
import com.hypixel.hytale.server.core.command.system.exceptions.GeneralCommandException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import java.util.regex.Pattern;
import com.hypixel.hytale.server.core.prefab.selection.SelectionManager;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.VariantRotation;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFlipType;
import com.hypixel.hytale.server.core.command.system.arguments.types.SingleArgumentType;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;

public class BlockSelectCommand extends AbstractPlayerCommand
{
    @Nonnull
    private static final SingleArgumentType<BlockFlipType> BLOCK_FLIP_TYPE;
    @Nonnull
    private static final SingleArgumentType<VariantRotation> VARIANT_ROTATION;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_BLOCK_SELECT_DONE;
    @Nonnull
    private static final Message MESSAGE_COMMANDS_BLOCK_SELECT_NO_SELECTION_PROVIDER;
    @Nonnull
    private final OptionalArg<String> regexArg;
    @Nonnull
    private final FlagArg allFlag;
    @Nonnull
    private final OptionalArg<String> sortArg;
    @Nonnull
    private final OptionalArg<BlockFlipType> flipTypeArg;
    @Nonnull
    private final OptionalArg<VariantRotation> variantRotationArg;
    @Nonnull
    private final DefaultArg<Integer> paddingArg;
    @Nonnull
    private final OptionalArg<String> groundArg;
    
    public BlockSelectCommand() {
        super("blockselect", "server.commands.block.select.desc");
        this.regexArg = this.withOptionalArg("regex", "server.commands.block.select.regex.desc", ArgTypes.STRING);
        this.allFlag = this.withFlagArg("all", "server.commands.block.select.all.desc");
        this.sortArg = this.withOptionalArg("sort", "server.commands.block.select.sort.desc", ArgTypes.STRING);
        this.flipTypeArg = this.withOptionalArg("fliptype", "server.commands.block.select.fliptype.desc", BlockSelectCommand.BLOCK_FLIP_TYPE);
        this.variantRotationArg = this.withOptionalArg("variantrotation", "server.commands.block.select.variantrotation.desc", BlockSelectCommand.VARIANT_ROTATION);
        this.paddingArg = this.withDefaultArg("padding", "server.commands.block.select.padding.desc", ArgTypes.INTEGER, 1, "1");
        this.groundArg = this.withOptionalArg("ground", "server.commands.block.select.ground.desc", ArgTypes.STRING);
    }
    
    @Override
    protected void execute(@Nonnull final CommandContext context, @Nonnull final Store<EntityStore> store, @Nonnull final Ref<EntityStore> ref, @Nonnull final PlayerRef playerRef, @Nonnull final World world) {
        final Player playerComponent = store.getComponent(ref, Player.getComponentType());
        assert playerComponent != null;
        final SelectionProvider selectionProvider = SelectionManager.getSelectionProvider();
        if (selectionProvider == null) {
            context.sendMessage(BlockSelectCommand.MESSAGE_COMMANDS_BLOCK_SELECT_NO_SELECTION_PROVIDER);
            return;
        }
        final Pattern pattern = this.regexArg.provided(context) ? Pattern.compile(this.regexArg.get(context)) : null;
        Stream<Map.Entry<String, BlockType>> stream = BlockType.getAssetMap().getAssetMap().entrySet().parallelStream().filter(e -> !e.getValue().isUnknown()).filter(e -> pattern == null || pattern.matcher(e.getKey()).matches());
        if (!((Argument<Arg, Boolean>)this.allFlag).get(context)) {
            stream = stream.filter(e -> !e.getValue().isState());
        }
        if (this.flipTypeArg.provided(context)) {
            final BlockFlipType flipType = this.flipTypeArg.get(context);
            stream = stream.filter(e -> e.getValue().getFlipType() == flipType);
        }
        if (this.variantRotationArg.provided(context)) {
            final VariantRotation variantRotation = this.variantRotationArg.get(context);
            stream = stream.filter(e -> e.getValue().getVariantRotation() == variantRotation);
        }
        if (this.sortArg.provided(context)) {
            final String sort = this.sortArg.get(context);
            if (sort.isEmpty()) {
                stream = stream.sorted((Comparator<? super Map.Entry<String, BlockType>>)Map.Entry.comparingByKey());
            }
            else {
                for (final String s : sort.split(",")) {
                    final String sortType = s;
                    stream = switch (s) {
                        case "key" -> stream.sorted((Comparator<? super Map.Entry<String, BlockType>>)Map.Entry.comparingByKey());
                        case "name" -> stream.sorted((Comparator<? super Map.Entry<String, BlockType>>)Map.Entry.comparingByKey());
                        case "reverse" -> stream.sorted(Collections.reverseOrder());
                        default -> throw new GeneralCommandException(Message.translation("server.commands.block.select.invalidSortType").param("sortType", sortType));
                    };
                }
            }
        }
        final List<Map.Entry<String, BlockBoundingBoxes>> blocks = stream.map(e -> Map.entry((Object)e.getKey(), BlockBoundingBoxes.getAssetMap().getAsset(e.getValue().getHitboxTypeIndex()))).toList();
        context.sendMessage(Message.translation("server.commands.block.select.select").param("count", blocks.size()));
        final Box largestBox = new Box();
        for (final Map.Entry<String, BlockBoundingBoxes> block : blocks) {
            largestBox.union(block.getValue().get(0).getBoundingBox());
        }
        final int paddingSize = this.paddingArg.get(context);
        final int sqrt = MathUtil.ceil(Math.sqrt(blocks.size())) + 1;
        final int strideX = MathUtil.ceil(largestBox.width()) + paddingSize;
        final int strideZ = MathUtil.ceil(largestBox.depth()) + paddingSize;
        final int halfStrideX = strideX / 2;
        final int halfStrideZ = strideZ / 2;
        final double height = largestBox.height();
        String groundBlock;
        if (this.groundArg.provided(context)) {
            groundBlock = this.groundArg.get(context);
        }
        else {
            final String rockStone = "Rock_Stone";
            if (BlockType.getAssetMap().getAsset("Rock_Stone") != null) {
                groundBlock = "Rock_Stone";
            }
            else {
                groundBlock = "Unknown";
            }
        }
        selectionProvider.computeSelectionCopy(ref, playerComponent, selection -> {
            final BlockTypeAssetMap<String, BlockType> blockTypeAssetMap = BlockType.getAssetMap();
            final int groundId = blockTypeAssetMap.getIndex(groundBlock);
            int x;
            int z = 0;
            for (x = -paddingSize; x < sqrt * strideX; ++x) {
                for (z = -paddingSize; z < sqrt * strideZ; ++z) {
                    selection.addBlockAtWorldPos(x, 0, z, groundId, 0, 0, 0);
                    for (int y = 1; y < height; ++y) {
                        selection.addBlockAtWorldPos(x, y, z, 0, 0, 0, 0);
                    }
                }
            }
            for (int j = 0; j < blocks.size(); ++j) {
                final Map.Entry<String, BlockBoundingBoxes> entry = blocks.get(j);
                final BlockBoundingBoxes.RotatedVariantBoxes rotatedBoxes = entry.getValue().get(0);
                final Box boundingBox = rotatedBoxes.getBoundingBox();
                final int x2 = j % sqrt * strideX + halfStrideX + MathUtil.floor(boundingBox.middleX());
                final int z2 = j / sqrt * strideZ + halfStrideZ + MathUtil.floor(boundingBox.middleZ());
                final int blockId = blockTypeAssetMap.getIndex(entry.getKey());
                selection.addBlockAtWorldPos(x2, 1, z2, blockId, 0, 0, 0);
                if (entry.getValue().protrudesUnitBox()) {
                    FillerBlockUtil.forEachFillerBlock(rotatedBoxes, (x1, y1, z1) -> {
                        if (x1 == 0 && y1 == 0 && z1 == 0) {
                            return;
                        }
                        else {
                            final int filler = FillerBlockUtil.pack(x1, y1, z1);
                            selection.addBlockAtWorldPos(x + x1, 1 + y1, z + z1, blockId, 0, filler, 0);
                            return;
                        }
                    });
                }
            }
            context.sendMessage(BlockSelectCommand.MESSAGE_COMMANDS_BLOCK_SELECT_DONE);
        }, store);
    }
    
    static {
        BLOCK_FLIP_TYPE = new EnumArgumentType<BlockFlipType>("server.commands.parsing.argtype.blockfliptype.name", BlockFlipType.class);
        VARIANT_ROTATION = new EnumArgumentType<VariantRotation>("server.commands.parsing.argtype.variantrotation.name", VariantRotation.class);
        MESSAGE_COMMANDS_BLOCK_SELECT_DONE = Message.translation("server.commands.block.select.done");
        MESSAGE_COMMANDS_BLOCK_SELECT_NO_SELECTION_PROVIDER = Message.translation("server.commands.block.select.noSelectionProvider");
    }
}
