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

package com.hypixel.hytale.server.core.universe.playerdata;

import java.util.stream.Stream;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Predicate;
import java.util.Objects;
import java.util.Set;
import org.bson.BsonDocument;
import com.hypixel.hytale.server.core.util.BsonUtil;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Holder;
import java.util.concurrent.CompletableFuture;
import java.util.UUID;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import joptsimple.OptionSpec;
import com.hypixel.hytale.server.core.Options;
import com.hypixel.hytale.common.util.PathUtil;
import com.hypixel.hytale.server.core.Constants;
import javax.annotation.Nonnull;
import java.nio.file.Path;
import com.hypixel.hytale.codec.builder.BuilderCodec;

public class DiskPlayerStorageProvider implements PlayerStorageProvider
{
    public static final String ID = "Disk";
    public static final BuilderCodec<DiskPlayerStorageProvider> CODEC;
    @Nonnull
    private Path path;
    
    public DiskPlayerStorageProvider() {
        this.path = Constants.UNIVERSE_PATH.resolve("players");
    }
    
    @Nonnull
    public Path getPath() {
        return this.path;
    }
    
    @Nonnull
    @Override
    public PlayerStorage getPlayerStorage() {
        return new DiskPlayerStorage(this.path);
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "DiskPlayerStorageProvider{path=" + String.valueOf(this.path);
    }
    
    static {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     2: invokedynamic   BootstrapMethod #1, get:()Ljava/util/function/Supplier;
        //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    13: dup            
        //    14: ldc             "Path"
        //    16: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
        //    19: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    22: invokedynamic   BootstrapMethod #2, accept:()Ljava/util/function/BiConsumer;
        //    27: invokedynamic   BootstrapMethod #3, apply:()Ljava/util/function/Function;
        //    32: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
        //    35: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    38: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    41: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    44: putstatic       com/hypixel/hytale/server/core/universe/playerdata/DiskPlayerStorageProvider.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    47: return         
        // 
        // The error that occurred was:
        // 
        // java.lang.UnsupportedOperationException: The requested operation is not supported.
        //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
        //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
        //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
        //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypesForVariables(TypeAnalysis.java:593)
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:405)
        //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:109)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
        //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
    
    public static class DiskPlayerStorage implements PlayerStorage
    {
        public static final String FILE_EXTENSION = ".json";
        @Nonnull
        private final Path path;
        
        public DiskPlayerStorage(@Nonnull final Path path) {
            this.path = path;
            if (!Options.getOptionSet().has(Options.BARE)) {
                try {
                    Files.createDirectories(path, (FileAttribute<?>[])new FileAttribute[0]);
                }
                catch (final IOException e) {
                    throw new RuntimeException("Failed to create players directory", e);
                }
            }
        }
        
        @Nonnull
        @Override
        public CompletableFuture<Holder<EntityStore>> load(@Nonnull final UUID uuid) {
            final Path file = this.path.resolve(String.valueOf(uuid) + ".json");
            return BsonUtil.readDocument(file).thenApply(bsonDocument -> {
                if (bsonDocument == null) {
                    bsonDocument = new BsonDocument();
                }
                return EntityStore.REGISTRY.deserialize(bsonDocument);
            });
        }
        
        @Nonnull
        @Override
        public CompletableFuture<Void> save(@Nonnull final UUID uuid, @Nonnull final Holder<EntityStore> holder) {
            final Path file = this.path.resolve(String.valueOf(uuid) + ".json");
            final BsonDocument document = EntityStore.REGISTRY.serialize(holder);
            return BsonUtil.writeDocument(file, document);
        }
        
        @Nonnull
        @Override
        public CompletableFuture<Void> remove(@Nonnull final UUID uuid) {
            final Path file = this.path.resolve(String.valueOf(uuid) + ".json");
            try {
                Files.deleteIfExists(file);
                return CompletableFuture.completedFuture((Void)null);
            }
            catch (final IOException e) {
                return CompletableFuture.failedFuture(e);
            }
        }
        
        @Nonnull
        @Override
        public Set<UUID> getPlayers() throws IOException {
            try (final Stream<Path> stream = Files.list(this.path)) {
                final Set<? super Object> set = stream.map(p -> {
                    final String fileName = p.getFileName().toString();
                    if (!fileName.endsWith(".json")) {
                        return null;
                    }
                    else {
                        try {
                            return UUID.fromString(fileName.substring(0, fileName.length() - ".json".length()));
                        }
                        catch (final IllegalArgumentException e) {
                            return null;
                        }
                    }
                }).filter(Objects::nonNull).collect((Collector<? super Object, ?, Set<? super Object>>)Collectors.toSet());
                return (Set<UUID>)set;
            }
        }
    }
}
