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

package com.hypixel.hytale.builtin.buildertools.commands;

import com.hypixel.hytale.server.core.command.system.arguments.system.Argument;
import java.util.Iterator;
import java.util.Collection;
import com.hypixel.hytale.server.core.util.message.MessageFormat;
import java.nio.file.FileVisitor;
import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.SimpleFileVisitor;
import com.hypixel.hytale.server.core.util.io.FileUtil;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.io.IOException;
import com.hypixel.hytale.common.util.PathUtil;
import com.hypixel.hytale.server.core.modules.singleplayer.SingleplayerModule;
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;
import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
import com.hypixel.hytale.builtin.buildertools.utils.RecursivePrefabLoader;
import com.hypixel.hytale.server.core.Message;
import java.util.Objects;
import com.hypixel.hytale.server.core.command.system.arguments.system.DefaultArg;
import java.nio.file.Path;
import java.util.List;
import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabPage;
import com.hypixel.hytale.server.core.prefab.PrefabStore;
import javax.annotation.Nullable;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin;
import com.hypixel.hytale.component.ComponentAccessor;
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.FlagArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
import com.hypixel.hytale.builtin.buildertools.prefablist.PrefabSavePage;
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 javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;

public class PrefabCommand extends AbstractCommandCollection
{
    public PrefabCommand() {
        super("prefab", "server.commands.prefab.desc");
        this.addAliases("p");
        this.setPermissionGroup(GameMode.Creative);
        this.addSubCommand(new PrefabSaveCommand());
        this.addSubCommand(new PrefabLoadCommand());
        this.addSubCommand(new PrefabDeleteCommand());
        this.addSubCommand(new PrefabListCommand());
    }
    
    private static class PrefabSaveCommand extends AbstractPlayerCommand
    {
        public PrefabSaveCommand() {
            super("save", "server.commands.prefab.save.desc");
            this.requirePermission("hytale.editor.prefab.manage");
            this.addUsageVariant(new PrefabSaveDirectCommand());
        }
        
        @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;
            playerComponent.getPageManager().openCustomPage(ref, store, new PrefabSavePage(playerRef));
        }
    }
    
    private static class PrefabSaveDirectCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private final RequiredArg<String> nameArg;
        @Nonnull
        private final FlagArg overwriteFlag;
        @Nonnull
        private final FlagArg entitiesFlag;
        @Nonnull
        private final FlagArg emptyFlag;
        @Nonnull
        private final FlagArg playerAnchorFlag;
        
        public PrefabSaveDirectCommand() {
            super("server.commands.prefab.save.desc");
            this.nameArg = this.withRequiredArg("name", "server.commands.prefab.save.name.desc", ArgTypes.STRING);
            this.overwriteFlag = this.withFlagArg("overwrite", "server.commands.prefab.save.overwrite.desc");
            this.entitiesFlag = this.withFlagArg("entities", "server.commands.prefab.save.entities.desc");
            this.emptyFlag = this.withFlagArg("empty", "server.commands.prefab.save.empty.desc");
            this.playerAnchorFlag = this.withFlagArg("playerAnchor", "server.commands.prefab.save.playerAnchor.desc");
        }
        
        @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 String name = this.nameArg.get(context);
            final boolean overwrite = ((Argument<Arg, Boolean>)this.overwriteFlag).get(context);
            final boolean entities = ((Argument<Arg, Boolean>)this.entitiesFlag).get(context);
            final boolean empty = ((Argument<Arg, Boolean>)this.emptyFlag).get(context);
            final Vector3i playerAnchor = this.getPlayerAnchor(ref, store, ((Argument<Arg, Boolean>)this.playerAnchorFlag).get(context));
            BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.saveFromSelection(r, name, true, overwrite, entities, empty, playerAnchor, componentAccessor));
        }
        
        @Nullable
        private Vector3i getPlayerAnchor(@Nonnull final Ref<EntityStore> ref, @Nonnull final Store<EntityStore> store, final boolean usePlayerAnchor) {
            if (!usePlayerAnchor) {
                return null;
            }
            final TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
            if (transformComponent == null) {
                return null;
            }
            final Vector3d position = transformComponent.getPosition();
            return new Vector3i(MathUtil.floor(position.getX()), MathUtil.floor(position.getY()), MathUtil.floor(position.getZ()));
        }
    }
    
    private static class PrefabLoadCommand extends AbstractPlayerCommand
    {
        public PrefabLoadCommand() {
            super("load", "server.commands.prefab.load.desc");
            this.requirePermission("hytale.editor.prefab.use");
            this.addUsageVariant(new PrefabLoadByNameCommand());
        }
        
        @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 List<PrefabStore.AssetPackPrefabPath> assetPaths = PrefabStore.get().getAllAssetPrefabPaths();
            final Path defaultRoot = assetPaths.isEmpty() ? PrefabStore.get().getServerPrefabsPath() : assetPaths.getFirst().prefabsPath();
            final BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRef);
            playerComponent.getPageManager().openCustomPage(ref, store, new PrefabPage(playerRef, defaultRoot, builderState));
        }
    }
    
    private static class PrefabLoadByNameCommand extends AbstractPlayerCommand
    {
        @Nonnull
        private final RequiredArg<String> nameArg;
        @Nonnull
        private final DefaultArg<String> storeTypeArg;
        @Nonnull
        private final DefaultArg<String> storeNameArg;
        @Nonnull
        private final FlagArg childrenFlag;
        
        public PrefabLoadByNameCommand() {
            super("server.commands.prefab.load.desc");
            this.nameArg = this.withRequiredArg("name", "server.commands.prefab.load.name.desc", ArgTypes.STRING);
            this.storeTypeArg = this.withDefaultArg("storeType", "server.commands.prefab.load.storeType.desc", ArgTypes.STRING, "asset", "server.commands.prefab.load.storeType.desc");
            this.storeNameArg = this.withDefaultArg("storeName", "server.commands.prefab.load.storeName.desc", ArgTypes.STRING, null, "");
            this.childrenFlag = this.withFlagArg("children", "server.commands.prefab.load.children.desc");
        }
        
        @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 String storeType = this.storeTypeArg.get(context);
            final String storeName = this.storeNameArg.get(context);
            String name = this.nameArg.get(context);
            if (!name.endsWith(".prefab.json")) {
                name += ".prefab.json";
            }
            Path prefabStorePath = null;
            Path resolvedPrefabPath = null;
            final String finalName = name;
            final String s = storeType;
            final Function<String, BlockSelection> prefabGetter = switch (s) {
                case "server" -> {
                    prefabStorePath = PrefabStore.get().getServerPrefabsPath();
                    final PrefabStore value = PrefabStore.get();
                    Objects.requireNonNull(value);
                    yield value::getServerPrefab;
                }
                case "asset" -> {
                    final Path foundPath = PrefabStore.get().findAssetPrefabPath(finalName);
                    if (foundPath != null) {
                        resolvedPrefabPath = foundPath;
                        prefabStorePath = foundPath.getParent();
                        yield key -> PrefabStore.get().getPrefab(foundPath);
                    }
                    prefabStorePath = PrefabStore.get().getAssetPrefabsPath();
                    final PrefabStore value2 = PrefabStore.get();
                    Objects.requireNonNull(value2);
                    yield value2::getAssetPrefab;
                }
                case "worldgen" -> {
                    final Path storePath = PrefabStore.get().getWorldGenPrefabsPath(storeName);
                    prefabStorePath = PrefabStore.get().getWorldGenPrefabsPath(storeName);
                    yield key -> PrefabStore.get().getWorldGenPrefab(storePath, key);
                }
                default -> {
                    context.sendMessage(Message.translation("server.commands.prefab.invalidStoreType").param("storeType", storeType));
                    yield null;
                }
            };
            if (prefabGetter == null) {
                return;
            }
            final Path finalPrefabStorePath = prefabStorePath;
            BiFunction<String, Random, BlockSelection> loader;
            if (((Argument<Arg, Boolean>)this.childrenFlag).get(context)) {
                loader = new RecursivePrefabLoader.BlockSelectionLoader(finalPrefabStorePath, prefabGetter);
            }
            else {
                loader = (BiFunction<String, Random, BlockSelection>)((prefabFile, rand) -> prefabGetter.apply(prefabFile));
            }
            final boolean prefabExists = (resolvedPrefabPath != null && Files.isRegularFile(resolvedPrefabPath, new LinkOption[0])) || Files.isRegularFile(prefabStorePath.resolve(name), new LinkOption[0]);
            if (prefabExists) {
                BuilderToolsPlugin.addToQueue(playerComponent, playerRef, (r, s, componentAccessor) -> s.load(finalName, loader.apply(finalName, s.getRandom()), componentAccessor));
            }
            else {
                context.sendMessage(Message.translation("server.builderTools.prefab.prefabNotFound").param("name", name));
            }
        }
    }
    
    private static class PrefabDeleteCommand extends CommandBase
    {
        @Nonnull
        private final RequiredArg<String> nameArg;
        
        public PrefabDeleteCommand() {
            super("delete", "server.commands.prefab.delete.desc", true);
            this.nameArg = this.withRequiredArg("name", "server.commands.prefab.delete.name.desc", ArgTypes.STRING);
            this.requirePermission("hytale.editor.prefab.manage");
        }
        
        @Override
        protected void executeSync(@Nonnull final CommandContext context) {
            String name = this.nameArg.get(context);
            if (!name.endsWith(".prefab.json")) {
                name += ".prefab.json";
            }
            final PrefabStore module = PrefabStore.get();
            final Path serverPrefabsPath = module.getServerPrefabsPath();
            final Path resolve = serverPrefabsPath.resolve(name);
            try {
                final Ref<EntityStore> ref = context.senderAsPlayerRef();
                boolean isOwner = false;
                if (ref != null && ref.isValid()) {
                    final Store<EntityStore> store = ref.getStore();
                    final PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
                    if (playerRefComponent != null) {
                        isOwner = SingleplayerModule.isOwner(playerRefComponent);
                    }
                }
                if (!PathUtil.isChildOf(serverPrefabsPath, resolve) && !isOwner) {
                    context.sendMessage(Message.translation("server.builderTools.attemptedToSaveOutsidePrefabsDir"));
                    return;
                }
                final Path relativize = PathUtil.relativize(serverPrefabsPath, resolve);
                if (Files.isRegularFile(resolve, new LinkOption[0])) {
                    Files.delete(resolve);
                    context.sendMessage(Message.translation("server.builderTools.prefab.deleted").param("name", relativize.toString()));
                }
                else {
                    context.sendMessage(Message.translation("server.builderTools.prefab.prefabNotFound").param("name", relativize.toString()));
                }
            }
            catch (final IOException e) {
                context.sendMessage(Message.translation("server.builderTools.prefab.errorOccured").param("reason", e.getMessage()));
            }
        }
    }
    
    private static class PrefabListCommand extends CommandBase
    {
        @Nonnull
        private final DefaultArg<String> storeTypeArg;
        @Nonnull
        private final FlagArg textFlag;
        static final /* synthetic */ boolean $assertionsDisabled;
        
        public PrefabListCommand() {
            super("list", "server.commands.prefab.list.desc");
            this.storeTypeArg = this.withDefaultArg("storeType", "server.commands.prefab.list.storeType.desc", ArgTypes.STRING, "asset", "server.commands.prefab.list.storeType.desc");
            this.textFlag = this.withFlagArg("text", "server.commands.prefab.list.text.desc");
        }
        
        @Override
        protected void executeSync(@Nonnull final CommandContext context) {
            final String s;
            final String storeType = s = this.storeTypeArg.get(context);
            final Path prefabStorePath = switch (s) {
                case "server" -> PrefabStore.get().getServerPrefabsPath();
                case "asset" -> {
                    final List<PrefabStore.AssetPackPrefabPath> assetPaths = PrefabStore.get().getAllAssetPrefabPaths();
                    yield assetPaths.isEmpty() ? PrefabStore.get().getAssetPrefabsPath() : assetPaths.getFirst().prefabsPath();
                }
                case "worldgen" -> PrefabStore.get().getWorldGenPrefabsPath();
                default -> throw new IllegalStateException("Unexpected value: " + storeType);
            };
            final Ref<EntityStore> ref = context.senderAsPlayerRef();
            if (ref != null && ref.isValid() && !((Argument<Arg, Boolean>)this.textFlag).get(context)) {
                final Store<EntityStore> store = ref.getStore();
                final World world = store.getExternalData().getWorld();
                final Path finalPrefabStorePath = prefabStorePath;
                world.execute(() -> {
                    final Player playerComponent = store.getComponent(ref, Player.getComponentType());
                    if (!PrefabListCommand.$assertionsDisabled && playerComponent == null) {
                        throw new AssertionError();
                    }
                    else {
                        final PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
                        if (!PrefabListCommand.$assertionsDisabled && playerRefComponent == null) {
                            throw new AssertionError();
                        }
                        else {
                            final BuilderToolsPlugin.BuilderState builderState = BuilderToolsPlugin.getState(playerComponent, playerRefComponent);
                            playerComponent.getPageManager().openCustomPage(ref, store, new PrefabPage(playerRefComponent, finalPrefabStorePath, builderState));
                        }
                    }
                });
                return;
            }
            try {
                final List<Message> prefabFiles = new ObjectArrayList<Message>();
                if ("asset".equals(storeType)) {
                    for (final PrefabStore.AssetPackPrefabPath packPath : PrefabStore.get().getAllAssetPrefabPaths()) {
                        final Path path = packPath.prefabsPath();
                        final String packPrefix = packPath.isBasePack() ? "" : ("[" + packPath.getPackName() + "] ");
                        if (Files.isDirectory(path, new LinkOption[0])) {
                            Files.walkFileTree(path, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>(this) {
                                @Nonnull
                                @Override
                                public FileVisitResult visitFile(@Nonnull final Path file, @Nonnull final BasicFileAttributes attrs) {
                                    final String fileName = file.getFileName().toString();
                                    if (fileName.endsWith(".prefab.json")) {
                                        prefabFiles.add(Message.raw(packPrefix + PathUtil.relativize(path, file).toString()));
                                    }
                                    return FileVisitResult.CONTINUE;
                                }
                            });
                        }
                    }
                }
                else {
                    final Path path2 = prefabStorePath;
                    if (Files.isDirectory(path2, new LinkOption[0])) {
                        Files.walkFileTree(path2, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>(this) {
                            @Nonnull
                            @Override
                            public FileVisitResult visitFile(@Nonnull final Path file, @Nonnull final BasicFileAttributes attrs) {
                                final String fileName = file.getFileName().toString();
                                if (fileName.endsWith(".prefab.json")) {
                                    prefabFiles.add(Message.raw(PathUtil.relativize(path2, file).toString()));
                                }
                                return FileVisitResult.CONTINUE;
                            }
                        });
                    }
                }
                context.sendMessage(MessageFormat.list(Message.translation("server.commands.prefab.list.header"), prefabFiles));
            }
            catch (final IOException e) {
                context.sendMessage(Message.translation("server.builderTools.prefab.errorListingPrefabs").param("reason", e.getMessage()));
            }
        }
    }
}
