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

package com.hypixel.hytale.server.core.prefab;

import com.hypixel.hytale.common.plugin.PluginManifest;
import java.util.Iterator;
import com.hypixel.hytale.server.core.asset.AssetModule;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import com.hypixel.hytale.assetstore.AssetPack;
import com.hypixel.hytale.server.core.universe.Universe;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.core.util.AssetUtil;
import java.io.File;
import java.util.stream.Stream;
import java.io.IOException;
import java.util.stream.Collector;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.LinkedHashMap;
import java.util.function.Function;
import com.hypixel.hytale.server.core.prefab.config.SelectionPrefabSerializer;
import com.hypixel.hytale.server.core.util.BsonUtil;
import org.bson.BsonDocument;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import javax.annotation.Nonnull;
import java.util.concurrent.ConcurrentHashMap;
import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
import java.util.Map;
import java.nio.file.Path;
import java.util.function.Predicate;

public class PrefabStore
{
    public static final Predicate<Path> PREFAB_FILTER;
    public static final Path PREFABS_PATH;
    private static final String DEFAULT_WORLDGEN_NAME = "Default";
    private static final PrefabStore INSTANCE;
    private final Map<Path, BlockSelection> PREFAB_CACHE;
    
    private PrefabStore() {
        this.PREFAB_CACHE = new ConcurrentHashMap<Path, BlockSelection>();
    }
    
    @Nonnull
    public BlockSelection getServerPrefab(@Nonnull final String key) {
        return this.getPrefab(this.getServerPrefabsPath().resolve(key));
    }
    
    @Nonnull
    public BlockSelection getPrefab(@Nonnull final Path path) {
        return this.PREFAB_CACHE.computeIfAbsent(path.toAbsolutePath().normalize(), p -> {
            if (Files.exists(p, new LinkOption[0])) {
                return SelectionPrefabSerializer.deserialize(BsonUtil.readDocument(p).join());
            }
            else {
                throw new PrefabLoadException(PrefabLoadException.Type.NOT_FOUND);
            }
        });
    }
    
    public Path getServerPrefabsPath() {
        return PrefabStore.PREFABS_PATH;
    }
    
    @Nonnull
    public Map<Path, BlockSelection> getServerPrefabDir(@Nonnull final String key) {
        return this.getPrefabDir(this.getServerPrefabsPath().resolve(key));
    }
    
    @Nonnull
    public Map<Path, BlockSelection> getPrefabDir(@Nonnull final Path dir) {
        try (final Stream<Path> stream = Files.list(dir)) {
            return stream.filter(PrefabStore.PREFAB_FILTER).sorted().collect((Collector<? super Path, ?, Map>)Collectors.toMap((Function<? super Path, ?>)Function.identity(), (Function<? super Path, ?>)this::getPrefab, (u, v) -> {
                new IllegalStateException(String.format("Duplicate key %s", u));
                throw;
            }, (Supplier<R>)LinkedHashMap::new));
        }
        catch (final IOException e) {
            throw new RuntimeException("Failed to list directory " + String.valueOf(dir), (Throwable)e);
        }
    }
    
    public void saveServerPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab) {
        this.saveWorldGenPrefab(key, prefab, false);
    }
    
    public void saveWorldGenPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab, final boolean overwrite) {
        this.savePrefab(this.getWorldGenPrefabsPath().resolve(key), prefab, overwrite);
    }
    
    public void savePrefab(@Nonnull final Path path, @Nonnull final BlockSelection prefab, final boolean overwrite) {
        final File file = path.toFile();
        if (!file.exists() || overwrite) {
            file.getParentFile().mkdirs();
            Label_0070: {
                try {
                    BsonUtil.writeDocument(path, SelectionPrefabSerializer.serialize(prefab)).join();
                    break Label_0070;
                }
                catch (final Throwable e) {
                    throw new PrefabSaveException(PrefabSaveException.Type.ERROR, e);
                }
                throw new PrefabSaveException(PrefabSaveException.Type.ALREADY_EXISTS);
            }
            this.PREFAB_CACHE.remove(path);
            return;
        }
        throw new PrefabSaveException(PrefabSaveException.Type.ALREADY_EXISTS);
    }
    
    @Nonnull
    public Path getWorldGenPrefabsPath() {
        return this.getWorldGenPrefabsPath("Default");
    }
    
    public Path getAssetRootPath() {
        return AssetUtil.getHytaleAssetsPath();
    }
    
    @Nonnull
    public Path getWorldGenPrefabsPath(@Nullable String name) {
        name = ((name == null) ? "Default" : name);
        return Universe.getWorldGenPath().resolve(name).resolve("Prefabs");
    }
    
    public void saveServerPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab, final boolean overwrite) {
        this.savePrefab(this.getServerPrefabsPath().resolve(key), prefab, overwrite);
    }
    
    @Nonnull
    public Path getAssetPrefabsPath() {
        return AssetUtil.getHytaleAssetsPath().resolve("Server").resolve("Prefabs");
    }
    
    @Nonnull
    public Path getAssetPrefabsPathForPack(@Nonnull final AssetPack pack) {
        return pack.getRoot().resolve("Server").resolve("Prefabs");
    }
    
    @Nonnull
    public List<AssetPackPrefabPath> getAllAssetPrefabPaths() {
        final List<AssetPackPrefabPath> result = new ObjectArrayList<AssetPackPrefabPath>();
        for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
            final Path prefabsPath = this.getAssetPrefabsPathForPack(pack);
            if (Files.isDirectory(prefabsPath, new LinkOption[0])) {
                result.add(new AssetPackPrefabPath(pack, prefabsPath));
            }
        }
        return result;
    }
    
    @Nullable
    public BlockSelection getAssetPrefabFromAnyPack(@Nonnull final String key) {
        for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
            final Path prefabsPath = this.getAssetPrefabsPathForPack(pack);
            final Path prefabPath = prefabsPath.resolve(key);
            if (Files.exists(prefabPath, new LinkOption[0])) {
                return this.getPrefab(prefabPath);
            }
        }
        return null;
    }
    
    @Nullable
    public Path findAssetPrefabPath(@Nonnull final String key) {
        for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
            final Path prefabsPath = this.getAssetPrefabsPathForPack(pack);
            final Path prefabPath = prefabsPath.resolve(key);
            if (Files.exists(prefabPath, new LinkOption[0])) {
                return prefabPath;
            }
        }
        return null;
    }
    
    @Nullable
    public AssetPack findAssetPackForPrefabPath(@Nonnull final Path prefabPath) {
        final Path normalizedPath = prefabPath.toAbsolutePath().normalize();
        for (final AssetPack pack : AssetModule.get().getAssetPacks()) {
            final Path prefabsPath = this.getAssetPrefabsPathForPack(pack).toAbsolutePath().normalize();
            if (normalizedPath.startsWith(prefabsPath)) {
                return pack;
            }
        }
        return null;
    }
    
    @Nonnull
    public BlockSelection getAssetPrefab(@Nonnull final String key) {
        return this.getPrefab(this.getAssetPrefabsPath().resolve(key));
    }
    
    @Nonnull
    public Map<Path, BlockSelection> getAssetPrefabDir(@Nonnull final String key) {
        return this.getPrefabDir(this.getAssetPrefabsPath().resolve(key));
    }
    
    public void saveAssetPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab) {
        this.saveWorldGenPrefab(key, prefab, false);
    }
    
    public void saveAssetPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab, final boolean overwrite) {
        this.savePrefab(this.getAssetPrefabsPath().resolve(key), prefab, overwrite);
    }
    
    @Nonnull
    public BlockSelection getWorldGenPrefab(@Nonnull final String key) {
        return this.getWorldGenPrefab(this.getWorldGenPrefabsPath(), key);
    }
    
    @Nonnull
    public BlockSelection getWorldGenPrefab(@Nonnull final Path prefabsPath, @Nonnull final String key) {
        return this.getPrefab(prefabsPath.resolve(key));
    }
    
    @Nonnull
    public Map<Path, BlockSelection> getWorldGenPrefabDir(@Nonnull final String key) {
        return this.getPrefabDir(this.getWorldGenPrefabsPath().resolve(key));
    }
    
    public void saveWorldGenPrefab(@Nonnull final String key, @Nonnull final BlockSelection prefab) {
        this.saveWorldGenPrefab(key, prefab, false);
    }
    
    public static PrefabStore get() {
        return PrefabStore.INSTANCE;
    }
    
    static {
        PREFAB_FILTER = (path -> path.toString().endsWith(".prefab.json"));
        PREFABS_PATH = Path.of("prefabs", new String[0]);
        INSTANCE = new PrefabStore();
    }
    
    record AssetPackPrefabPath(@Nullable AssetPack pack, @Nonnull Path prefabsPath) {
        public boolean isBasePack() {
            return this.pack != null && this.pack.equals(AssetModule.get().getBaseAssetPack());
        }
        
        public boolean isFromAssetPack() {
            return this.pack != null;
        }
        
        @Nonnull
        public String getPackName() {
            return (this.pack != null) ? this.pack.getName() : "Server";
        }
        
        @Nonnull
        public String getDisplayName() {
            if (this.pack == null) {
                return "Server";
            }
            if (this.isBasePack()) {
                return "HytaleAssets";
            }
            final PluginManifest manifest = this.pack.getManifest();
            return (manifest != null) ? manifest.getName() : this.pack.getName();
        }
        
        @Nullable
        public AssetPack pack() {
            return this.pack;
        }
        
        @Nonnull
        public Path prefabsPath() {
            return this.prefabsPath;
        }
    }
}
