package net.minecraft.client.gui.screens.worldselection;

import org.apache.logging.log4j.LogManager;
import java.util.Optional;
import org.apache.commons.lang3.mutable.MutableObject;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.LevelResource;
import java.util.stream.Stream;
import java.util.Comparator;
import java.nio.file.FileVisitOption;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.client.gui.screens.ConfirmScreen;
import java.util.concurrent.Executor;
import net.minecraft.server.ServerResources;
import net.minecraft.Util;
import net.minecraft.commands.Commands;
import java.util.stream.Collector;
import java.util.List;
import com.google.common.collect.ImmutableList;
import java.io.File;
import net.minecraft.client.gui.screens.DataPackSelectScreen;
import java.util.Collection;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.repository.FolderRepositorySource;
import net.minecraft.server.packs.repository.PackSource;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.repository.RepositorySource;
import net.minecraft.server.packs.repository.Pack;
import java.io.IOException;
import net.minecraft.client.gui.components.toasts.SystemToast;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.network.chat.FormattedText;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.world.level.GameType;
import net.minecraft.client.gui.screens.GenericDirtMessageScreen;
import net.minecraft.FileUtil;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.client.gui.Font;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.server.MinecraftServer;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.GameRules;
import net.minecraft.network.chat.Component;
import net.minecraft.client.gui.components.Button;
import java.nio.file.Path;
import net.minecraft.world.level.DataPackConfig;
import net.minecraft.world.Difficulty;
import javax.annotation.Nullable;
import net.minecraft.client.gui.components.EditBox;
import org.apache.logging.log4j.Logger;
import net.minecraft.client.gui.screens.Screen;

public class CreateWorldScreen extends Screen {
    private static final Logger LOGGER;
    private final Screen lastScreen;
    private EditBox nameEdit;
    private String resultFolder;
    private SelectedGameMode gameMode;
    @Nullable
    private SelectedGameMode oldGameMode;
    private Difficulty selectedDifficulty;
    private Difficulty effectiveDifficulty;
    private boolean commands;
    private boolean commandsChanged;
    public boolean hardCore;
    protected DataPackConfig dataPacks;
    @Nullable
    private Path tempDataPackDir;
    private boolean displayOptions;
    private Button createButton;
    private Button modeButton;
    private Button difficultyButton;
    private Button moreOptionsButton;
    private Button gameRulesButton;
    private Button dataPacksButton;
    private Button commandsButton;
    private Component gameModeHelp1;
    private Component gameModeHelp2;
    private String initName;
    private GameRules gameRules;
    public final WorldGenSettingsComponent worldGenSettingsComponent;
    
    public CreateWorldScreen(@Nullable final Screen dqs, final LevelSettings bqe, final WorldGenSettings cix, @Nullable final Path path, final RegistryAccess.RegistryHolder a) {
        this(dqs, new WorldGenSettingsComponent(a, cix));
        this.initName = bqe.levelName();
        this.commands = bqe.allowCommands();
        this.commandsChanged = true;
        this.selectedDifficulty = bqe.difficulty();
        this.effectiveDifficulty = this.selectedDifficulty;
        this.gameRules.assignFrom(bqe.gameRules(), null);
        this.dataPacks = bqe.getDataPackConfig();
        if (bqe.hardcore()) {
            this.gameMode = SelectedGameMode.HARDCORE;
        }
        else if (bqe.gameType().isSurvival()) {
            this.gameMode = SelectedGameMode.SURVIVAL;
        }
        else if (bqe.gameType().isCreative()) {
            this.gameMode = SelectedGameMode.CREATIVE;
        }
        this.tempDataPackDir = path;
    }
    
    public CreateWorldScreen(@Nullable final Screen dqs) {
        this(dqs, new WorldGenSettingsComponent());
    }
    
    private CreateWorldScreen(@Nullable final Screen dqs, final WorldGenSettingsComponent dud) {
        super(new TranslatableComponent("selectWorld.create"));
        this.gameMode = SelectedGameMode.SURVIVAL;
        this.selectedDifficulty = Difficulty.NORMAL;
        this.effectiveDifficulty = Difficulty.NORMAL;
        this.dataPacks = DataPackConfig.DEFAULT;
        this.gameRules = new GameRules();
        this.lastScreen = dqs;
        this.initName = I18n.get("selectWorld.newWorld");
        this.worldGenSettingsComponent = dud;
    }
    
    @Override
    public void tick() {
        this.nameEdit.tick();
        this.worldGenSettingsComponent.tick();
    }
    
    @Override
    protected void init() {
        this.minecraft.keyboardHandler.setSendRepeatsToGui(true);
        (this.nameEdit = new EditBox(this.font, this.width / 2 - 100, 60, 200, 20, new TranslatableComponent("selectWorld.enterName")) {
            @Override
            protected MutableComponent createNarrationMessage() {
                return super.createNarrationMessage().append(". ").append(new TranslatableComponent("selectWorld.resultFolder")).append(" ").append(CreateWorldScreen.this.resultFolder);
            }
        }).setValue(this.initName);
        this.nameEdit.setResponder(string -> {
            this.initName = string;
            this.createButton.active = !this.nameEdit.getValue().isEmpty();
            this.updateResultFolder();
            return;
        });
        this.children.add(this.nameEdit);
        final int integer2 = this.width / 2 - 155;
        final int integer3 = this.width / 2 + 5;
        this.modeButton = this.addButton(new Button(integer2, 100, 150, 20, new TranslatableComponent("selectWorld.gameMode"), dni -> {
            switch (this.gameMode) {
                case SURVIVAL: {
                    this.setGameMode(SelectedGameMode.HARDCORE);
                    break;
                }
                case HARDCORE: {
                    this.setGameMode(SelectedGameMode.CREATIVE);
                    break;
                }
                case CREATIVE: {
                    this.setGameMode(SelectedGameMode.SURVIVAL);
                    break;
                }
            }
            dni.queueNarration(250);
            return;
        }) {
            @Override
            public Component getMessage() {
                return super.getMessage().copy().append(": ").append(new TranslatableComponent("selectWorld.gameMode." + CreateWorldScreen.this.gameMode.name));
            }
            
            @Override
            protected MutableComponent createNarrationMessage() {
                return super.createNarrationMessage().append(". ").append(CreateWorldScreen.this.gameModeHelp1).append(" ").append(CreateWorldScreen.this.gameModeHelp2);
            }
        });
        this.difficultyButton = this.addButton(new Button(integer3, 100, 150, 20, new TranslatableComponent("options.difficulty"), dni -> {
            this.selectedDifficulty = this.selectedDifficulty.nextById();
            this.effectiveDifficulty = this.selectedDifficulty;
            dni.queueNarration(250);
            return;
        }) {
            @Override
            public Component getMessage() {
                return new TranslatableComponent("options.difficulty").append(": ").append(CreateWorldScreen.this.effectiveDifficulty.getDisplayName());
            }
        });
        this.commandsButton = this.addButton(new Button(integer2, 151, 150, 20, new TranslatableComponent("selectWorld.allowCommands"), dni -> {
            this.commandsChanged = true;
            this.commands = !this.commands;
            dni.queueNarration(250);
            return;
        }) {
            @Override
            public Component getMessage() {
                return super.getMessage().copy().append(" ").append(CommonComponents.optionStatus(CreateWorldScreen.this.commands && !CreateWorldScreen.this.hardCore));
            }
            
            @Override
            protected MutableComponent createNarrationMessage() {
                return super.createNarrationMessage().append(". ").append(new TranslatableComponent("selectWorld.allowCommands.info"));
            }
        });
        this.dataPacksButton = this.<Button>addButton(new Button(integer3, 151, 150, 20, new TranslatableComponent("selectWorld.dataPacks"), dni -> this.openDataPackSelectionScreen()));
        this.gameRulesButton = this.<Button>addButton(new Button(integer2, 185, 150, 20, new TranslatableComponent("selectWorld.gameRules"), dni -> this.minecraft.setScreen(new EditGameRulesScreen(this.gameRules.copy(), optional -> {
            this.minecraft.setScreen(this);
            optional.ifPresent(bpx -> this.gameRules = bpx);
        }))));
        this.worldGenSettingsComponent.init(this, this.minecraft, this.font);
        this.moreOptionsButton = this.<Button>addButton(new Button(integer3, 185, 150, 20, new TranslatableComponent("selectWorld.moreWorldOptions"), dni -> this.toggleDisplayOptions()));
        this.createButton = this.<Button>addButton(new Button(integer2, this.height - 28, 150, 20, new TranslatableComponent("selectWorld.create"), dni -> this.onCreate()));
        this.createButton.active = !this.initName.isEmpty();
        this.<Button>addButton(new Button(integer3, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, dni -> {
            this.removeTempDataPackDir();
            this.minecraft.setScreen(this.lastScreen);
            return;
        }));
        this.updateDisplayOptions();
        this.setInitialFocus(this.nameEdit);
        this.setGameMode(this.gameMode);
        this.updateResultFolder();
    }
    
    private void updateGameModeHelp() {
        this.gameModeHelp1 = new TranslatableComponent("selectWorld.gameMode." + this.gameMode.name + ".line1");
        this.gameModeHelp2 = new TranslatableComponent("selectWorld.gameMode." + this.gameMode.name + ".line2");
    }
    
    private void updateResultFolder() {
        this.resultFolder = this.nameEdit.getValue().trim();
        if (this.resultFolder.isEmpty()) {
            this.resultFolder = "World";
        }
        try {
            this.resultFolder = FileUtil.findAvailableName(this.minecraft.getLevelSource().getBaseDir(), this.resultFolder, "");
        }
        catch (Exception exception4) {
            this.resultFolder = "World";
            try {
                this.resultFolder = FileUtil.findAvailableName(this.minecraft.getLevelSource().getBaseDir(), this.resultFolder, "");
            }
            catch (Exception exception3) {
                throw new RuntimeException("Could not create save folder", exception3);
            }
        }
    }
    
    @Override
    public void removed() {
        this.minecraft.keyboardHandler.setSendRepeatsToGui(false);
    }
    
    private void onCreate() {
        this.minecraft.forceSetScreen(new GenericDirtMessageScreen(new TranslatableComponent("createWorld.preparing")));
        if (!this.copyTempDataPackDirToNewWorld()) {
            return;
        }
        final WorldGenSettings cix2 = this.worldGenSettingsComponent.makeSettings(this.hardCore);
        LevelSettings bqe3;
        if (cix2.isDebug()) {
            final GameRules bpx4 = new GameRules();
            bpx4.<GameRules.BooleanValue>getRule(GameRules.RULE_DAYLIGHT).set(false, null);
            bqe3 = new LevelSettings(this.nameEdit.getValue().trim(), GameType.SPECTATOR, false, Difficulty.PEACEFUL, true, bpx4, DataPackConfig.DEFAULT);
        }
        else {
            bqe3 = new LevelSettings(this.nameEdit.getValue().trim(), this.gameMode.gameType, this.hardCore, this.effectiveDifficulty, this.commands && !this.hardCore, this.gameRules, this.dataPacks);
        }
        this.minecraft.createLevel(this.resultFolder, bqe3, this.worldGenSettingsComponent.registryHolder(), cix2);
    }
    
    private void toggleDisplayOptions() {
        this.setDisplayOptions(!this.displayOptions);
    }
    
    private void setGameMode(final SelectedGameMode b) {
        if (!this.commandsChanged) {
            this.commands = (b == SelectedGameMode.CREATIVE);
        }
        if (b == SelectedGameMode.HARDCORE) {
            this.hardCore = true;
            this.commandsButton.active = false;
            this.worldGenSettingsComponent.bonusItemsButton.active = false;
            this.effectiveDifficulty = Difficulty.HARD;
            this.difficultyButton.active = false;
        }
        else {
            this.hardCore = false;
            this.commandsButton.active = true;
            this.worldGenSettingsComponent.bonusItemsButton.active = true;
            this.effectiveDifficulty = this.selectedDifficulty;
            this.difficultyButton.active = true;
        }
        this.gameMode = b;
        this.updateGameModeHelp();
    }
    
    public void updateDisplayOptions() {
        this.setDisplayOptions(this.displayOptions);
    }
    
    private void setDisplayOptions(final boolean boolean1) {
        this.displayOptions = boolean1;
        this.modeButton.visible = !this.displayOptions;
        this.difficultyButton.visible = !this.displayOptions;
        if (this.worldGenSettingsComponent.isDebug()) {
            this.dataPacksButton.visible = false;
            this.modeButton.active = false;
            if (this.oldGameMode == null) {
                this.oldGameMode = this.gameMode;
            }
            this.setGameMode(SelectedGameMode.DEBUG);
            this.commandsButton.visible = false;
        }
        else {
            this.modeButton.active = true;
            if (this.oldGameMode != null) {
                this.setGameMode(this.oldGameMode);
            }
            this.commandsButton.visible = !this.displayOptions;
            this.dataPacksButton.visible = !this.displayOptions;
        }
        this.worldGenSettingsComponent.setDisplayOptions(this.displayOptions);
        this.nameEdit.setVisible(!this.displayOptions);
        if (this.displayOptions) {
            this.moreOptionsButton.setMessage(CommonComponents.GUI_DONE);
        }
        else {
            this.moreOptionsButton.setMessage(new TranslatableComponent("selectWorld.moreWorldOptions"));
        }
        this.gameRulesButton.visible = !this.displayOptions;
    }
    
    @Override
    public boolean keyPressed(final int integer1, final int integer2, final int integer3) {
        if (super.keyPressed(integer1, integer2, integer3)) {
            return true;
        }
        if (integer1 == 257 || integer1 == 335) {
            this.onCreate();
            return true;
        }
        return false;
    }
    
    @Override
    public void onClose() {
        if (this.displayOptions) {
            this.setDisplayOptions(false);
        }
        else {
            this.minecraft.setScreen(this.lastScreen);
        }
        this.removeTempDataPackDir();
    }
    
    @Override
    public void render(final PoseStack dhl, final int integer2, final int integer3, final float float4) {
        this.renderBackground(dhl);
        this.drawCenteredString(dhl, this.font, this.title, this.width / 2, 20, -1);
        if (this.displayOptions) {
            this.drawString(dhl, this.font, I18n.get("selectWorld.enterSeed"), this.width / 2 - 100, 47, -6250336);
            this.drawString(dhl, this.font, I18n.get("selectWorld.seedInfo"), this.width / 2 - 100, 85, -6250336);
            this.worldGenSettingsComponent.render(dhl, integer2, integer3, float4);
        }
        else {
            this.drawString(dhl, this.font, I18n.get("selectWorld.enterName"), this.width / 2 - 100, 47, -6250336);
            this.drawString(dhl, this.font, I18n.get("selectWorld.resultFolder") + " " + this.resultFolder, this.width / 2 - 100, 85, -6250336);
            this.nameEdit.render(dhl, integer2, integer3, float4);
            this.drawString(dhl, this.font, this.gameModeHelp1, this.width / 2 - 150, 122, -6250336);
            this.drawString(dhl, this.font, this.gameModeHelp2, this.width / 2 - 150, 134, -6250336);
            if (this.commandsButton.visible) {
                this.drawString(dhl, this.font, I18n.get("selectWorld.allowCommands.info"), this.width / 2 - 150, 172, -6250336);
            }
        }
        super.render(dhl, integer2, integer3, float4);
    }
    
    @Override
    protected <T extends GuiEventListener> T addWidget(final T dof) {
        return super.<T>addWidget(dof);
    }
    
    @Override
    protected <T extends AbstractWidget> T addButton(final T dng) {
        return super.<T>addButton(dng);
    }
    
    @Nullable
    protected Path getTempDataPackDir() {
        if (this.tempDataPackDir == null) {
            try {
                this.tempDataPackDir = Files.createTempDirectory("mcworld-", new FileAttribute[0]);
            }
            catch (IOException iOException2) {
                CreateWorldScreen.LOGGER.warn("Failed to create temporary dir", (Throwable)iOException2);
                SystemToast.onPackCopyFailure(this.minecraft, this.resultFolder);
                this.minecraft.setScreen(this.lastScreen);
            }
        }
        return this.tempDataPackDir;
    }
    
    private void openDataPackSelectionScreen() {
        final Path path2 = this.getTempDataPackDir();
        if (path2 != null) {
            final File file3 = path2.toFile();
            final PackRepository<Pack> aar4 = new PackRepository<Pack>(Pack::new, new RepositorySource[] { new ServerPacksSource(), new FolderRepositorySource(file3, PackSource.DEFAULT) });
            aar4.reload();
            aar4.setSelected(this.dataPacks.getEnabled());
            this.minecraft.setScreen(new DataPackSelectScreen(this, aar4, this::tryApplyNewDataPacks, file3));
        }
    }
    
    private void tryApplyNewDataPacks(final PackRepository<Pack> aar) {
        final List<String> list3 = ImmutableList.copyOf(aar.getSelectedIds());
        final List<String> list4 = aar.getAvailableIds().stream().filter(string -> !list3.contains(string)).collect(ImmutableList.toImmutableList());
        final DataPackConfig bpn5 = new DataPackConfig(list3, list4);
        if (list3.equals(this.dataPacks.getEnabled())) {
            this.dataPacks = bpn5;
            return;
        }
        final Minecraft minecraft;
        final GenericDirtMessageScreen screen;
        this.minecraft.tell(() -> {
            minecraft = this.minecraft;
            new GenericDirtMessageScreen(new TranslatableComponent("dataPack.validation.working"));
            minecraft.setScreen(screen);
            return;
        });
        Minecraft minecraft2;
        final ConfirmScreen screen2;
        final DataPackConfig dataPacks;
        ServerResources.loadResources(aar.openAllSelected(), Commands.CommandSelection.INTEGRATED, 2, Util.backgroundExecutor(), this.minecraft).handle((uw, throwable) -> {
            if (throwable != null) {
                CreateWorldScreen.LOGGER.warn("Failed to validate datapack", throwable);
                this.minecraft.tell(() -> {
                    minecraft2 = this.minecraft;
                    new ConfirmScreen(boolean1 -> {
                        if (boolean1) {
                            this.openDataPackSelectionScreen();
                        }
                        else {
                            this.dataPacks = DataPackConfig.DEFAULT;
                            this.minecraft.setScreen(this);
                        }
                    }, new TranslatableComponent("dataPack.validation.failed"), TextComponent.EMPTY, new TranslatableComponent("dataPack.validation.back"), new TranslatableComponent("dataPack.validation.reset"));
                    minecraft2.setScreen(screen2);
                    return;
                });
            }
            else {
                this.minecraft.tell(() -> {
                    this.dataPacks = dataPacks;
                    this.minecraft.setScreen(this);
                    return;
                });
            }
            return null;
        });
    }
    
    private void removeTempDataPackDir() {
        if (this.tempDataPackDir != null) {
            try (final Stream<Path> stream2 = Files.walk(this.tempDataPackDir)) {
                stream2.sorted(Comparator.reverseOrder()).forEach(path -> {
                    try {
                        Files.delete(path);
                    }
                    catch (IOException iOException2) {
                        CreateWorldScreen.LOGGER.warn("Failed to remove temporary file {}", path, iOException2);
                    }
                    return;
                });
            }
            catch (IOException iOException3) {
                CreateWorldScreen.LOGGER.warn("Failed to list temporary dir {}", this.tempDataPackDir);
            }
            this.tempDataPackDir = null;
        }
    }
    
    private static void copyBetweenDirs(final Path path1, final Path path2, final Path path3) {
        try {
            Util.copyBetweenDirs(path1, path2, path3);
        }
        catch (IOException iOException4) {
            CreateWorldScreen.LOGGER.warn("Failed to copy datapack file from {} to {}", path3, path2);
            throw new OperationFailedException(iOException4);
        }
    }
    
    private boolean copyTempDataPackDirToNewWorld() {
        if (this.tempDataPackDir != null) {
            try (final LevelStorageSource.LevelStorageAccess a2 = this.minecraft.getLevelSource().createAccess(this.resultFolder);
                 final Stream<Path> stream4 = Files.walk(this.tempDataPackDir)) {
                final Path path3 = a2.getLevelPath(LevelResource.DATAPACK_DIR);
                Files.createDirectories(path3, new FileAttribute[0]);
                stream4.filter(path -> !path.equals(this.tempDataPackDir)).forEach(path2 -> copyBetweenDirs(this.tempDataPackDir, path3, path2));
            }
            catch (IOException | OperationFailedException ex2) {
                final Exception ex;
                final Exception exception2 = ex;
                CreateWorldScreen.LOGGER.warn("Failed to copy datapacks to world {}", this.resultFolder, exception2);
                SystemToast.onPackCopyFailure(this.minecraft, this.resultFolder);
                this.minecraft.setScreen(this.lastScreen);
                this.removeTempDataPackDir();
                return false;
            }
            this.removeTempDataPackDir();
        }
        return true;
    }
    
    @Nullable
    public static Path createTempDataPackDirFromExistingWorld(final Path path, final Minecraft dlx) {
        final MutableObject<Path> mutableObject3 = (MutableObject<Path>)new MutableObject();
        try (final Stream<Path> stream4 = Files.walk(path)) {
            final MutableObject mutableObject4;
            Path path4;
            stream4.filter(path2 -> !path2.equals(path)).forEach(path3 -> {
                path4 = (Path)mutableObject4.getValue();
                if (path4 == null) {
                    try {
                        path4 = Files.createTempDirectory("mcworld-", new FileAttribute[0]);
                    }
                    catch (IOException iOException5) {
                        CreateWorldScreen.LOGGER.warn("Failed to create temporary dir");
                        throw new OperationFailedException(iOException5);
                    }
                    mutableObject4.setValue(path4);
                }
                copyBetweenDirs(path, path4, path3);
                return;
            });
        }
        catch (IOException | OperationFailedException ex2) {
            final Exception ex;
            final Exception exception4 = ex;
            CreateWorldScreen.LOGGER.warn("Failed to copy datapacks from world {}", path, exception4);
            SystemToast.onPackCopyFailure(dlx, path.toString());
            return null;
        }
        return (Path)mutableObject3.getValue();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
    
    enum SelectedGameMode {
        SURVIVAL("survival", GameType.SURVIVAL), 
        HARDCORE("hardcore", GameType.SURVIVAL), 
        CREATIVE("creative", GameType.CREATIVE), 
        DEBUG("spectator", GameType.SPECTATOR);
        
        private final String name;
        private final GameType gameType;
        
        private SelectedGameMode(final String string3, final GameType bpy) {
            this.name = string3;
            this.gameType = bpy;
        }
    }
    
    static class OperationFailedException extends RuntimeException {
        public OperationFailedException(final Throwable throwable) {
            super(throwable);
        }
    }
}
