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

package com.hypixel.hytale.server.core.ui.browser;

import java.util.Collections;
import java.util.function.Predicate;
import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.assetstore.AssetPack;
import com.hypixel.hytale.server.core.asset.AssetModule;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.ToIntFunction;
import java.util.Comparator;
import java.util.Objects;
import com.hypixel.hytale.common.util.StringCompareUtil;
import java.util.Locale;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.nio.file.FileVisitor;
import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.DirectoryStream;
import java.io.IOException;
import com.hypixel.hytale.common.util.PathUtil;
import java.util.Iterator;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import java.util.List;
import com.hypixel.hytale.server.core.ui.DropdownEntryInfo;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import javax.annotation.Nullable;
import java.nio.file.Paths;
import java.util.LinkedHashSet;
import java.util.Set;
import java.nio.file.Path;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.ui.Value;
import com.hypixel.hytale.logger.HytaleLogger;

public class ServerFileBrowser
{
    private static final HytaleLogger LOGGER;
    private static final Value<String> BUTTON_HIGHLIGHTED;
    private static final String BASE_ASSET_PACK_DISPLAY_NAME = "HytaleAssets";
    @Nonnull
    private final FileBrowserConfig config;
    @Nonnull
    private Path root;
    @Nonnull
    private Path currentDir;
    @Nonnull
    private String searchQuery;
    @Nonnull
    private final Set<String> selectedItems;
    
    public ServerFileBrowser(@Nonnull final FileBrowserConfig config) {
        this.config = config;
        this.selectedItems = new LinkedHashSet<String>();
        this.searchQuery = "";
        if (!config.roots().isEmpty()) {
            this.root = config.roots().get(0).path();
        }
        else {
            this.root = Paths.get("", new String[0]);
        }
        this.currentDir = this.root.getFileSystem().getPath("", new String[0]);
    }
    
    public ServerFileBrowser(@Nonnull final FileBrowserConfig config, @Nullable final Path initialRoot, @Nullable final Path initialDir) {
        this(config);
        if (initialRoot != null && Files.isDirectory(initialRoot, new LinkOption[0])) {
            this.root = initialRoot;
            this.currentDir = this.root.getFileSystem().getPath("", new String[0]);
        }
        if (initialDir != null) {
            this.currentDir = initialDir;
        }
    }
    
    public void buildRootSelector(@Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        if (!this.config.enableRootSelector() || this.config.rootSelectorId() == null) {
            if (this.config.rootSelectorId() != null) {
                commandBuilder.set(this.config.rootSelectorId() + ".Visible", false);
            }
            return;
        }
        final ObjectArrayList<DropdownEntryInfo> entries = new ObjectArrayList<DropdownEntryInfo>();
        for (final FileBrowserConfig.RootEntry rootEntry : this.config.roots()) {
            entries.add(new DropdownEntryInfo(rootEntry.displayName(), rootEntry.path().toString()));
        }
        commandBuilder.set(this.config.rootSelectorId() + ".Entries", (List<DropdownEntryInfo>)entries);
        commandBuilder.set(this.config.rootSelectorId() + ".Value", this.root.toString());
        eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, this.config.rootSelectorId(), new EventData().append("@Root", this.config.rootSelectorId() + ".Value"), false);
    }
    
    public void buildSearchInput(@Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        if (!this.config.enableSearch() || this.config.searchInputId() == null) {
            return;
        }
        if (!this.searchQuery.isEmpty()) {
            commandBuilder.set(this.config.searchInputId() + ".Value", this.searchQuery);
        }
        eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, this.config.searchInputId(), EventData.of("@SearchQuery", this.config.searchInputId() + ".Value"), false);
    }
    
    public void buildCurrentPath(@Nonnull final UICommandBuilder commandBuilder) {
        if (this.config.currentPathId() == null) {
            return;
        }
        String displayPath;
        if (this.config.assetPackMode()) {
            final String currentDirStr = this.currentDir.toString().replace('\\', '/');
            if (currentDirStr.isEmpty()) {
                displayPath = "Assets";
            }
            else {
                final String[] parts = currentDirStr.split("/", 2);
                final String packName = parts[0];
                final String subPath = (parts.length > 1) ? ("/" + parts[1]) : "";
                if ("HytaleAssets".equals(packName)) {
                    displayPath = packName + subPath;
                }
                else {
                    displayPath = "Mods/" + packName + subPath;
                }
            }
        }
        else {
            final String rootDisplay = this.root.toString().replace("\\", "/");
            final String relativeDisplay = this.currentDir.toString().isEmpty() ? "" : ("/" + this.currentDir.toString().replace("\\", "/"));
            displayPath = rootDisplay + relativeDisplay;
        }
        commandBuilder.set(this.config.currentPathId() + ".Text", displayPath);
    }
    
    public void buildFileList(@Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        commandBuilder.clear(this.config.listElementId());
        List<FileListProvider.FileEntry> entries;
        if (this.config.customProvider() != null) {
            entries = this.config.customProvider().getFiles(this.currentDir, this.searchQuery);
        }
        else if (this.config.assetPackMode()) {
            if (!this.searchQuery.isEmpty() && this.config.enableSearch()) {
                entries = this.buildAssetPackSearchResults();
            }
            else {
                entries = this.buildAssetPackListing();
            }
        }
        else if (!this.searchQuery.isEmpty() && this.config.enableSearch()) {
            entries = this.buildSearchResults();
        }
        else {
            entries = this.buildDirectoryListing();
        }
        int buttonIndex = 0;
        if (this.config.enableDirectoryNav() && !this.currentDir.toString().isEmpty() && this.searchQuery.isEmpty()) {
            commandBuilder.append(this.config.listElementId(), "Pages/BasicTextButton.ui");
            commandBuilder.set(this.config.listElementId() + "[0].Text", "../");
            eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, this.config.listElementId() + "[0]", EventData.of("File", ".."));
            ++buttonIndex;
        }
        for (FileListProvider.FileEntry entry : entries) {
            final boolean isNavigableDir = entry.isDirectory() && !entry.isTerminal();
            final String displayText = isNavigableDir ? entry.displayName() : entry.displayName();
            commandBuilder.append(this.config.listElementId(), "Pages/BasicTextButton.ui");
            commandBuilder.set(this.config.listElementId() + "[" + buttonIndex + "].Text", displayText);
            if (!entry.isDirectory() || entry.isTerminal()) {
                commandBuilder.set(this.config.listElementId() + "[" + buttonIndex + "].Style", ServerFileBrowser.BUTTON_HIGHLIGHTED);
            }
            final String eventKey = (!this.searchQuery.isEmpty() && !isNavigableDir) ? "SearchResult" : "File";
            eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, this.config.listElementId() + "[" + buttonIndex, EventData.of(eventKey, entry.name()));
            ++buttonIndex;
        }
    }
    
    public void buildUI(@Nonnull final UICommandBuilder commandBuilder, @Nonnull final UIEventBuilder eventBuilder) {
        this.buildRootSelector(commandBuilder, eventBuilder);
        this.buildSearchInput(commandBuilder, eventBuilder);
        this.buildCurrentPath(commandBuilder);
        this.buildFileList(commandBuilder, eventBuilder);
    }
    
    public boolean handleEvent(@Nonnull final FileBrowserEventData data) {
        if (data.getSearchQuery() != null) {
            this.searchQuery = data.getSearchQuery().trim().toLowerCase();
            return true;
        }
        if (data.getRoot() != null) {
            Path newRoot = this.findConfigRoot(data.getRoot());
            if (newRoot == null) {
                newRoot = Path.of(data.getRoot(), new String[0]);
            }
            this.setRoot(newRoot);
            this.currentDir = this.root.getFileSystem().getPath("", new String[0]);
            this.searchQuery = "";
            return true;
        }
        if (data.getFile() == null) {
            return data.getSearchResult() != null && false;
        }
        final String fileName = data.getFile();
        if ("..".equals(fileName)) {
            this.navigateUp();
            return true;
        }
        if (this.config.assetPackMode()) {
            return this.handleAssetPackNavigation(fileName);
        }
        if (this.config.enableDirectoryNav()) {
            final Path targetPath = this.root.resolve(this.currentDir.toString()).resolve(fileName);
            if (Files.isDirectory(targetPath, new LinkOption[0])) {
                this.currentDir = PathUtil.relativize(this.root, targetPath);
                return true;
            }
        }
        return false;
    }
    
    private List<FileListProvider.FileEntry> buildDirectoryListing() {
        final List<FileListProvider.FileEntry> entries = new ObjectArrayList<FileListProvider.FileEntry>();
        final Path path = this.root.resolve(this.currentDir.toString());
        if (!Files.isDirectory(path, new LinkOption[0])) {
            return entries;
        }
        try (final DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
            for (final Path file : stream) {
                final String fileName = file.getFileName().toString();
                if (fileName.startsWith(".")) {
                    continue;
                }
                final boolean isDirectory = Files.isDirectory(file, new LinkOption[0]);
                if (!isDirectory && !this.matchesExtension(fileName)) {
                    continue;
                }
                entries.add(new FileListProvider.FileEntry(fileName, isDirectory));
            }
        }
        catch (final IOException e) {
            ServerFileBrowser.LOGGER.atSevere().withCause(e).log("Error listing directory: %s", path);
        }
        entries.sort((a, b) -> {
            if (a.isDirectory() == b.isDirectory()) {
                return a.name().compareToIgnoreCase(b.name());
            }
            else {
                return a.isDirectory() ? -1 : 1;
            }
        });
        return entries;
    }
    
    private List<FileListProvider.FileEntry> buildSearchResults() {
        final List<Path> allFiles = new ObjectArrayList<Path>();
        if (!Files.isDirectory(this.root, new LinkOption[0])) {
            return List.of();
        }
        try {
            Files.walkFileTree(this.root, new SimpleFileVisitor<Path>() {
                @Nonnull
                @Override
                public FileVisitResult visitFile(@Nonnull final Path file, @Nonnull final BasicFileAttributes attrs) {
                    final String fileName = file.getFileName().toString();
                    if (ServerFileBrowser.this.matchesExtension(fileName)) {
                        allFiles.add(file);
                    }
                    return FileVisitResult.CONTINUE;
                }
            });
        }
        catch (final IOException e) {
            ServerFileBrowser.LOGGER.atSevere().withCause(e).log("Error walking directory: %s", this.root);
        }
        final Object2IntMap<Path> matchScores = new Object2IntOpenHashMap<Path>(allFiles.size());
        final Iterator<Path> iterator = allFiles.iterator();
        Path file = null;
        while (iterator.hasNext()) {
            file = iterator.next();
            final String fileName = file.getFileName().toString();
            final String baseName = this.removeExtensions(fileName);
            final int score = StringCompareUtil.getFuzzyDistance(baseName, this.searchQuery, Locale.ENGLISH);
            if (score > 0) {
                matchScores.put(file, score);
            }
        }
        final Stream<Object> stream = matchScores.keySet().stream();
        final Object2IntMap<Path> obj = matchScores;
        Objects.requireNonNull(obj);
        return stream.sorted(Comparator.comparingInt(obj::getInt).reversed()).limit(this.config.maxResults()).map(file -> {
            final Path relativePath = PathUtil.relativize(this.root, file);
            final String fileName2 = file.getFileName().toString();
            final String displayName = this.removeExtensions(fileName2);
            return new FileListProvider.FileEntry(relativePath.toString(), displayName, false, false, matchScores.getInt(file));
        }).collect((Collector<? super Object, ?, List<FileListProvider.FileEntry>>)Collectors.toList());
    }
    
    private boolean matchesExtension(@Nonnull final String fileName) {
        if (this.config.allowedExtensions().isEmpty()) {
            return true;
        }
        for (final String ext : this.config.allowedExtensions()) {
            if (fileName.endsWith(ext)) {
                return true;
            }
        }
        return false;
    }
    
    private List<FileListProvider.FileEntry> buildAssetPackListing() {
        final List<FileListProvider.FileEntry> entries = new ObjectArrayList<FileListProvider.FileEntry>();
        final String currentDirStr = this.currentDir.toString().replace('\\', '/');
        if (currentDirStr.isEmpty()) {
            for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
                final Path subPath = this.getAssetPackSubPath(pack);
                if (subPath != null && Files.isDirectory(subPath, new LinkOption[0])) {
                    final String displayName = this.getAssetPackDisplayName(pack);
                    entries.add(new FileListProvider.FileEntry(displayName, displayName, true));
                }
            }
        }
        else {
            final String[] parts = currentDirStr.split("/", 2);
            final String packName = parts[0];
            final String subDir = (parts.length > 1) ? parts[1] : "";
            final AssetPack pack2 = this.findAssetPackByDisplayName(packName);
            if (pack2 != null) {
                final Path packSubPath = this.getAssetPackSubPath(pack2);
                if (packSubPath != null) {
                    final Path targetDir = subDir.isEmpty() ? packSubPath : packSubPath.resolve(subDir);
                    if (Files.isDirectory(targetDir, new LinkOption[0])) {
                        try (final DirectoryStream<Path> stream = Files.newDirectoryStream(targetDir)) {
                            for (final Path file : stream) {
                                final String fileName = file.getFileName().toString();
                                if (fileName.startsWith(".")) {
                                    continue;
                                }
                                final boolean isDirectory = Files.isDirectory(file, new LinkOption[0]);
                                if (!isDirectory && !this.matchesExtension(fileName)) {
                                    continue;
                                }
                                final String displayName2 = isDirectory ? fileName : this.removeExtensions(fileName);
                                final boolean isTerminal = isDirectory && this.isTerminalDirectory(file);
                                entries.add(new FileListProvider.FileEntry(fileName, displayName2, isDirectory, isTerminal));
                            }
                        }
                        catch (final IOException e) {
                            ServerFileBrowser.LOGGER.atSevere().withCause(e).log("Error listing asset pack directory: %s", targetDir);
                        }
                    }
                }
            }
        }
        entries.sort((a, b) -> {
            final boolean aIsBase = "HytaleAssets".equals(a.name());
            final boolean bIsBase = "HytaleAssets".equals(b.name());
            if (aIsBase != bIsBase) {
                return aIsBase ? -1 : 1;
            }
            else if (a.isDirectory() == b.isDirectory()) {
                return a.displayName().compareToIgnoreCase(b.displayName());
            }
            else {
                return a.isDirectory() ? -1 : 1;
            }
        });
        return entries;
    }
    
    private List<FileListProvider.FileEntry> buildAssetPackSearchResults() {
        final List<AssetPackSearchResult> allResults = new ObjectArrayList<AssetPackSearchResult>();
        final String currentDirStr = this.currentDir.toString().replace('\\', '/');
        if (currentDirStr.isEmpty()) {
            for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
                final Path subPath = this.getAssetPackSubPath(pack);
                if (subPath != null && Files.isDirectory(subPath, new LinkOption[0])) {
                    final String packDisplayName = this.getAssetPackDisplayName(pack);
                    this.searchInAssetPackDirectory(subPath, packDisplayName, "", allResults);
                }
            }
        }
        else {
            final String[] parts = currentDirStr.split("/", 2);
            final String packName = parts[0];
            final String subDir = (parts.length > 1) ? parts[1] : "";
            final AssetPack pack2 = this.findAssetPackByDisplayName(packName);
            if (pack2 != null) {
                final Path packSubPath = this.getAssetPackSubPath(pack2);
                if (packSubPath != null) {
                    final Path searchRoot = subDir.isEmpty() ? packSubPath : packSubPath.resolve(subDir);
                    if (Files.isDirectory(searchRoot, new LinkOption[0])) {
                        this.searchInAssetPackDirectory(searchRoot, packName, subDir, allResults);
                    }
                }
            }
        }
        allResults.sort(Comparator.comparingInt(AssetPackSearchResult::score).reversed());
        return allResults.stream().limit(this.config.maxResults()).map(r -> new FileListProvider.FileEntry(r.virtualPath(), r.displayName(), r.isTerminal(), r.isTerminal(), r.score())).collect((Collector<? super Object, ?, List<FileListProvider.FileEntry>>)Collectors.toList());
    }
    
    private void searchInAssetPackDirectory(@Nonnull final Path searchRoot, @Nonnull final String packName, @Nonnull final String basePath, @Nonnull final List<AssetPackSearchResult> results) {
        try {
            Files.walkFileTree(searchRoot, new SimpleFileVisitor<Path>() {
                @Nonnull
                @Override
                public FileVisitResult preVisitDirectory(@Nonnull final Path dir, @Nonnull final BasicFileAttributes attrs) {
                    if (dir.equals(searchRoot)) {
                        return FileVisitResult.CONTINUE;
                    }
                    if (ServerFileBrowser.this.isTerminalDirectory(dir)) {
                        final String dirName = dir.getFileName().toString();
                        final int score = StringCompareUtil.getFuzzyDistance(dirName.toLowerCase(), ServerFileBrowser.this.searchQuery, Locale.ENGLISH);
                        if (score > 0) {
                            final Path relativePath = searchRoot.relativize(dir);
                            final String relativeStr = relativePath.toString().replace('\\', '/');
                            final String virtualPath = basePath.isEmpty() ? (packName + "/" + relativeStr) : (packName + "/" + basePath + "/" + relativeStr);
                            results.add(new AssetPackSearchResult(virtualPath, dirName, score, true));
                        }
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    return FileVisitResult.CONTINUE;
                }
                
                @Nonnull
                @Override
                public FileVisitResult visitFile(@Nonnull final Path file, @Nonnull final BasicFileAttributes attrs) {
                    final String fileName = file.getFileName().toString();
                    if (ServerFileBrowser.this.matchesExtension(fileName)) {
                        final String baseName = ServerFileBrowser.this.removeExtensions(fileName);
                        final int score = StringCompareUtil.getFuzzyDistance(baseName.toLowerCase(), ServerFileBrowser.this.searchQuery, Locale.ENGLISH);
                        if (score > 0) {
                            final Path relativePath = searchRoot.relativize(file);
                            final String relativeStr = relativePath.toString().replace('\\', '/');
                            final String virtualPath = basePath.isEmpty() ? (packName + "/" + relativeStr) : (packName + "/" + basePath + "/" + relativeStr);
                            results.add(new AssetPackSearchResult(virtualPath, baseName, score, false));
                        }
                    }
                    return FileVisitResult.CONTINUE;
                }
            });
        }
        catch (final IOException e) {
            ServerFileBrowser.LOGGER.atSevere().withCause(e).log("Error searching asset pack directory: %s", searchRoot);
        }
    }
    
    private boolean handleAssetPackNavigation(@Nonnull final String fileName) {
        final String currentDirStr = this.currentDir.toString().replace('\\', '/');
        if (currentDirStr.isEmpty()) {
            final AssetPack pack = this.findAssetPackByDisplayName(fileName);
            if (pack != null) {
                final Path subPath = this.getAssetPackSubPath(pack);
                if (subPath != null && Files.isDirectory(subPath, new LinkOption[0])) {
                    this.currentDir = Paths.get(fileName, new String[0]);
                    return true;
                }
            }
            return false;
        }
        final String[] parts = currentDirStr.split("/", 2);
        final String packName = parts[0];
        final String subDir = (parts.length > 1) ? parts[1] : "";
        final AssetPack pack2 = this.findAssetPackByDisplayName(packName);
        if (pack2 == null) {
            return false;
        }
        final Path packSubPath = this.getAssetPackSubPath(pack2);
        if (packSubPath == null) {
            return false;
        }
        final Path targetDir = subDir.isEmpty() ? packSubPath : packSubPath.resolve(subDir);
        final Path targetPath = targetDir.resolve(fileName);
        if (!Files.isDirectory(targetPath, new LinkOption[0])) {
            return false;
        }
        if (this.isTerminalDirectory(targetPath)) {
            return false;
        }
        final String newPath = subDir.isEmpty() ? (packName + "/" + fileName) : (packName + "/" + subDir + "/" + fileName);
        this.currentDir = Paths.get(newPath, new String[0]);
        return true;
    }
    
    @Nullable
    private Path getAssetPackSubPath(@Nonnull final AssetPack pack) {
        if (this.config.assetPackSubPath() == null) {
            return null;
        }
        return pack.getRoot().resolve(this.config.assetPackSubPath());
    }
    
    @Nonnull
    private String getAssetPackDisplayName(@Nonnull final AssetPack pack) {
        if (pack.equals(AssetModule.get().getBaseAssetPack())) {
            return "HytaleAssets";
        }
        final PluginManifest manifest = pack.getManifest();
        return (manifest != null) ? manifest.getName() : pack.getName();
    }
    
    @Nullable
    private AssetPack findAssetPackByDisplayName(@Nonnull final String displayName) {
        for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
            if (this.getAssetPackDisplayName(pack).equals(displayName)) {
                return pack;
            }
        }
        return null;
    }
    
    private boolean isTerminalDirectory(@Nonnull final Path path) {
        final Predicate<Path> predicate = this.config.terminalDirectoryPredicate();
        return predicate != null && predicate.test(path);
    }
    
    @Nullable
    public Path resolveAssetPackPath(@Nonnull final String virtualPath) {
        if (!this.config.assetPackMode() || virtualPath.isEmpty()) {
            return null;
        }
        final String[] parts = virtualPath.replace('\\', '/').split("/", 2);
        final String packName = parts[0];
        final String subPath = (parts.length > 1) ? parts[1] : "";
        final AssetPack pack = this.findAssetPackByDisplayName(packName);
        if (pack == null) {
            return null;
        }
        final Path packSubPath = this.getAssetPackSubPath(pack);
        if (packSubPath == null) {
            return null;
        }
        return subPath.isEmpty() ? packSubPath : packSubPath.resolve(subPath);
    }
    
    @Nonnull
    public String getAssetPackCurrentPath() {
        return this.currentDir.toString().replace('\\', '/');
    }
    
    private String removeExtensions(@Nonnull final String fileName) {
        for (final String ext : this.config.allowedExtensions()) {
            if (fileName.endsWith(ext)) {
                return fileName.substring(0, fileName.length() - ext.length());
            }
        }
        return fileName;
    }
    
    @Nonnull
    public Path getRoot() {
        return this.root;
    }
    
    public void setRoot(@Nonnull final Path root) {
        this.root = root;
    }
    
    @Nonnull
    public Path getCurrentDir() {
        return this.currentDir;
    }
    
    public void setCurrentDir(@Nonnull final Path currentDir) {
        this.currentDir = currentDir;
    }
    
    @Nonnull
    public String getSearchQuery() {
        return this.searchQuery;
    }
    
    public void setSearchQuery(@Nonnull final String searchQuery) {
        this.searchQuery = searchQuery;
    }
    
    public void navigateUp() {
        if (!this.currentDir.toString().isEmpty()) {
            final Path parent = this.currentDir.getParent();
            this.currentDir = ((parent != null) ? parent : Paths.get("", new String[0]));
        }
    }
    
    public void navigateTo(@Nonnull final Path relativePath) {
        final Path targetPath = this.root.resolve(this.currentDir.toString()).resolve(relativePath.toString());
        if (!targetPath.normalize().startsWith(this.root.normalize())) {
            return;
        }
        if (Files.isDirectory(targetPath, new LinkOption[0])) {
            this.currentDir = PathUtil.relativize(this.root, targetPath);
        }
    }
    
    @Nonnull
    public Set<String> getSelectedItems() {
        return Collections.unmodifiableSet((Set<? extends String>)this.selectedItems);
    }
    
    public void addSelection(@Nonnull final String item) {
        if (this.config.enableMultiSelect()) {
            this.selectedItems.add(item);
        }
        else {
            this.selectedItems.clear();
            this.selectedItems.add(item);
        }
    }
    
    public void clearSelection() {
        this.selectedItems.clear();
    }
    
    @Nonnull
    public FileBrowserConfig getConfig() {
        return this.config;
    }
    
    @Nullable
    public Path resolveSecure(@Nonnull final String relativePath) {
        final Path resolved = this.root.resolve(relativePath);
        if (!resolved.normalize().startsWith(this.root.normalize())) {
            return null;
        }
        return resolved;
    }
    
    @Nullable
    public Path resolveFromCurrent(@Nonnull final String fileName) {
        final Path resolved = this.root.resolve(this.currentDir.toString()).resolve(fileName);
        if (!resolved.normalize().startsWith(this.root.normalize())) {
            return null;
        }
        return resolved;
    }
    
    @Nullable
    private Path findConfigRoot(@Nonnull final String pathStr) {
        for (final FileBrowserConfig.RootEntry rootEntry : this.config.roots()) {
            if (rootEntry.path().toString().equals(pathStr)) {
                return rootEntry.path();
            }
        }
        return null;
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        BUTTON_HIGHLIGHTED = Value.ref("Pages/BasicTextButton.ui", "SelectedLabelStyle");
    }
    
    record AssetPackSearchResult(@Nonnull String virtualPath, @Nonnull String displayName, int score, boolean isTerminal) {
        @Nonnull
        public String virtualPath() {
            return this.virtualPath;
        }
        
        @Nonnull
        public String displayName() {
            return this.displayName;
        }
    }
}
