package net.minecraft.world.level.chunk.storage;

import org.apache.logging.log4j.LogManager;
import com.mojang.serialization.DataResult;
import com.google.common.collect.ImmutableMap;
import net.minecraft.SharedConstants;
import java.util.Map;
import java.util.function.Consumer;
import com.google.common.collect.Maps;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.Tag;
import java.io.IOException;
import com.mojang.serialization.DynamicOps;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.world.level.Level;
import net.minecraft.Util;
import javax.annotation.Nullable;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.core.SectionPos;
import java.util.function.BooleanSupplier;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.io.File;
import net.minecraft.util.datafix.DataFixTypes;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Codec;
import java.util.function.Function;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import java.util.Optional;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import org.apache.logging.log4j.Logger;

public class SectionStorage<R> implements AutoCloseable {
    private static final Logger LOGGER;
    private final IOWorker worker;
    private final Long2ObjectMap<Optional<R>> storage;
    private final LongLinkedOpenHashSet dirty;
    private final Function<Runnable, Codec<R>> codec;
    private final Function<Runnable, R> factory;
    private final DataFixer fixerUpper;
    private final DataFixTypes type;
    
    public SectionStorage(final File file, final Function<Runnable, Codec<R>> function2, final Function<Runnable, R> function3, final DataFixer dataFixer, final DataFixTypes aeo, final boolean boolean6) {
        this.storage = (Long2ObjectMap<Optional<R>>)new Long2ObjectOpenHashMap();
        this.dirty = new LongLinkedOpenHashSet();
        this.codec = function2;
        this.factory = function3;
        this.fixerUpper = dataFixer;
        this.type = aeo;
        this.worker = new IOWorker(file, boolean6, file.getName());
    }
    
    protected void tick(final BooleanSupplier booleanSupplier) {
        while (!this.dirty.isEmpty() && booleanSupplier.getAsBoolean()) {
            final ChunkPos bph3 = SectionPos.of(this.dirty.firstLong()).chunk();
            this.writeColumn(bph3);
        }
    }
    
    @Nullable
    protected Optional<R> get(final long long1) {
        return (Optional<R>)this.storage.get(long1);
    }
    
    protected Optional<R> getOrLoad(final long long1) {
        final SectionPos go4 = SectionPos.of(long1);
        if (this.outsideStoredRange(go4)) {
            return Optional.<R>empty();
        }
        Optional<R> optional5 = this.get(long1);
        if (optional5 != null) {
            return optional5;
        }
        this.readColumn(go4.chunk());
        optional5 = this.get(long1);
        if (optional5 == null) {
            throw Util.<IllegalStateException>pauseInIde(new IllegalStateException());
        }
        return optional5;
    }
    
    protected boolean outsideStoredRange(final SectionPos go) {
        return Level.isOutsideBuildHeight(SectionPos.sectionToBlockCoord(go.y()));
    }
    
    protected R getOrCreate(final long long1) {
        final Optional<R> optional4 = this.getOrLoad(long1);
        if (optional4.isPresent()) {
            return optional4.get();
        }
        final R object5 = this.factory.apply(() -> this.setDirty(long1));
        this.storage.put(long1, Optional.<R>of(object5));
        return object5;
    }
    
    private void readColumn(final ChunkPos bph) {
        this.<CompoundTag>readColumn(bph, (com.mojang.serialization.DynamicOps<CompoundTag>)NbtOps.INSTANCE, this.tryRead(bph));
    }
    
    @Nullable
    private CompoundTag tryRead(final ChunkPos bph) {
        try {
            return this.worker.load(bph);
        }
        catch (IOException iOException3) {
            SectionStorage.LOGGER.error("Error reading chunk {} data from disk", bph, iOException3);
            return null;
        }
    }
    
    private <T> void readColumn(final ChunkPos bph, final DynamicOps<T> dynamicOps, @Nullable final T object) {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     1: ifnonnull       45
        //     4: iconst_0       
        //     5: istore          integer5
        //     7: iload           integer5
        //     9: bipush          16
        //    11: if_icmpge       42
        //    14: aload_0         /* this */
        //    15: getfield        net/minecraft/world/level/chunk/storage/SectionStorage.storage:Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;
        //    18: aload_1         /* bph */
        //    19: iload           integer5
        //    21: invokestatic    net/minecraft/core/SectionPos.of:(Lnet/minecraft/world/level/ChunkPos;I)Lnet/minecraft/core/SectionPos;
        //    24: invokevirtual   net/minecraft/core/SectionPos.asLong:()J
        //    27: invokestatic    java/util/Optional.empty:()Ljava/util/Optional;
        //    30: invokeinterface it/unimi/dsi/fastutil/longs/Long2ObjectMap.put:(JLjava/lang/Object;)Ljava/lang/Object;
        //    35: pop            
        //    36: iinc            integer5, 1
        //    39: goto            7
        //    42: goto            203
        //    45: new             Lcom/mojang/serialization/Dynamic;
        //    48: dup            
        //    49: aload_2         /* dynamicOps */
        //    50: aload_3         /* object */
        //    51: invokespecial   com/mojang/serialization/Dynamic.<init>:(Lcom/mojang/serialization/DynamicOps;Ljava/lang/Object;)V
        //    54: astore          dynamic5
        //    56: aload           dynamic5
        //    58: invokestatic    net/minecraft/world/level/chunk/storage/SectionStorage.getVersion:(Lcom/mojang/serialization/Dynamic;)I
        //    61: istore          integer6
        //    63: invokestatic    net/minecraft/SharedConstants.getCurrentVersion:()Lcom/mojang/bridge/game/GameVersion;
        //    66: invokeinterface com/mojang/bridge/game/GameVersion.getWorldVersion:()I
        //    71: istore          integer7
        //    73: iload           integer6
        //    75: iload           integer7
        //    77: if_icmpeq       84
        //    80: iconst_1       
        //    81: goto            85
        //    84: iconst_0       
        //    85: istore          boolean8
        //    87: aload_0         /* this */
        //    88: getfield        net/minecraft/world/level/chunk/storage/SectionStorage.fixerUpper:Lcom/mojang/datafixers/DataFixer;
        //    91: aload_0         /* this */
        //    92: getfield        net/minecraft/world/level/chunk/storage/SectionStorage.type:Lnet/minecraft/util/datafix/DataFixTypes;
        //    95: invokevirtual   net/minecraft/util/datafix/DataFixTypes.getType:()Lcom/mojang/datafixers/DSL$TypeReference;
        //    98: aload           dynamic5
        //   100: iload           integer6
        //   102: iload           integer7
        //   104: invokeinterface com/mojang/datafixers/DataFixer.update:(Lcom/mojang/datafixers/DSL$TypeReference;Lcom/mojang/serialization/Dynamic;II)Lcom/mojang/serialization/Dynamic;
        //   109: astore          dynamic9
        //   111: aload           dynamic9
        //   113: ldc_w           "Sections"
        //   116: invokevirtual   com/mojang/serialization/Dynamic.get:(Ljava/lang/String;)Lcom/mojang/serialization/OptionalDynamic;
        //   119: astore          optionalDynamic10
        //   121: iconst_0       
        //   122: istore          integer11
        //   124: iload           integer11
        //   126: bipush          16
        //   128: if_icmpge       203
        //   131: aload_1         /* bph */
        //   132: iload           integer11
        //   134: invokestatic    net/minecraft/core/SectionPos.of:(Lnet/minecraft/world/level/ChunkPos;I)Lnet/minecraft/core/SectionPos;
        //   137: invokevirtual   net/minecraft/core/SectionPos.asLong:()J
        //   140: lstore          long12
        //   142: aload           optionalDynamic10
        //   144: iload           integer11
        //   146: invokestatic    java/lang/Integer.toString:(I)Ljava/lang/String;
        //   149: invokevirtual   com/mojang/serialization/OptionalDynamic.get:(Ljava/lang/String;)Lcom/mojang/serialization/OptionalDynamic;
        //   152: invokevirtual   com/mojang/serialization/OptionalDynamic.result:()Ljava/util/Optional;
        //   155: aload_0         /* this */
        //   156: lload           long12
        //   158: invokedynamic   BootstrapMethod #1, apply:(Lnet/minecraft/world/level/chunk/storage/SectionStorage;J)Ljava/util/function/Function;
        //   163: invokevirtual   java/util/Optional.flatMap:(Ljava/util/function/Function;)Ljava/util/Optional;
        //   166: astore          optional14
        //   168: aload_0         /* this */
        //   169: getfield        net/minecraft/world/level/chunk/storage/SectionStorage.storage:Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;
        //   172: lload           long12
        //   174: aload           optional14
        //   176: invokeinterface it/unimi/dsi/fastutil/longs/Long2ObjectMap.put:(JLjava/lang/Object;)Ljava/lang/Object;
        //   181: pop            
        //   182: aload           optional14
        //   184: aload_0         /* this */
        //   185: lload           long12
        //   187: iload           boolean8
        //   189: invokedynamic   BootstrapMethod #2, accept:(Lnet/minecraft/world/level/chunk/storage/SectionStorage;JZ)Ljava/util/function/Consumer;
        //   194: invokevirtual   java/util/Optional.ifPresent:(Ljava/util/function/Consumer;)V
        //   197: iinc            integer11, 1
        //   200: goto            124
        //   203: return         
        //    Signature:
        //  <T:Ljava/lang/Object;>(Lnet/minecraft/world/level/ChunkPos;Lcom/mojang/serialization/DynamicOps<TT;>;TT;)V
        //    MethodParameters:
        //  Name        Flags  
        //  ----------  -----
        //  bph         
        //  dynamicOps  
        //  object      
        //    StackMapTable: 00 07 FF 00 07 00 05 07 00 02 07 00 EE 00 00 01 00 00 FF 00 22 00 00 00 00 FF 00 02 00 04 07 00 02 07 00 EE 07 01 05 07 00 05 00 00 FF 00 26 00 07 07 00 02 07 00 EE 00 00 07 01 07 01 01 00 00 40 01 FF 00 26 00 0B 07 00 02 07 00 EE 00 00 00 00 00 01 00 07 01 2C 01 00 00 FF 00 4E 00 00 00 00
        // 
        // The error that occurred was:
        // 
        // java.lang.NullPointerException
        //     at com.strobel.decompiler.languages.java.ast.NameVariables.generateNameForVariable(NameVariables.java:264)
        //     at com.strobel.decompiler.languages.java.ast.NameVariables.assignNamesToVariables(NameVariables.java:198)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:276)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:99)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:782)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:675)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:552)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:519)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:161)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:150)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:125)
        //     at cuchaz.enigma.SourceProvider.getSources(SourceProvider.java:85)
        //     at cuchaz.enigma.EnigmaProject$JarExport.decompileClass(EnigmaProject.java:241)
        //     at cuchaz.enigma.EnigmaProject$JarExport.lambda$decompile$1(EnigmaProject.java:230)
        //     at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
        //     at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
        //     at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
        //     at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
        //     at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:952)
        //     at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:926)
        //     at java.base/java.util.stream.AbstractTask.compute(AbstractTask.java:327)
        //     at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
        //     at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
        //     at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.helpCC(ForkJoinPool.java:1115)
        //     at java.base/java.util.concurrent.ForkJoinPool.externalHelpComplete(ForkJoinPool.java:1957)
        //     at java.base/java.util.concurrent.ForkJoinTask.tryExternalHelp(ForkJoinTask.java:378)
        //     at java.base/java.util.concurrent.ForkJoinTask.externalAwaitDone(ForkJoinTask.java:323)
        //     at java.base/java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:412)
        //     at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:736)
        //     at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:919)
        //     at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        //     at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
        //     at cuchaz.enigma.EnigmaProject$JarExport.decompile(EnigmaProject.java:233)
        //     at cuchaz.enigma.command.DecompileCommand.run(DecompileCommand.java:35)
        //     at cuchaz.enigma.CommandMain.main(CommandMain.java:43)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
    
    private void writeColumn(final ChunkPos bph) {
        final Dynamic<Tag> dynamic3 = this.<Tag>writeColumn(bph, (com.mojang.serialization.DynamicOps<Tag>)NbtOps.INSTANCE);
        final Tag lu4 = (Tag)dynamic3.getValue();
        if (lu4 instanceof CompoundTag) {
            this.worker.store(bph, (CompoundTag)lu4);
        }
        else {
            SectionStorage.LOGGER.error("Expected compound tag, got {}", lu4);
        }
    }
    
    private <T> Dynamic<T> writeColumn(final ChunkPos bph, final DynamicOps<T> dynamicOps) {
        final Map<T, T> map4 = Maps.newHashMap();
        for (int integer5 = 0; integer5 < 16; ++integer5) {
            final long long6 = SectionPos.of(bph, integer5).asLong();
            this.dirty.remove(long6);
            final Optional<R> optional8 = (Optional<R>)this.storage.get(long6);
            if (optional8 != null) {
                if (optional8.isPresent()) {
                    final DataResult<T> dataResult9 = (DataResult<T>)this.codec.apply(() -> this.setDirty(long6)).encodeStart((DynamicOps)dynamicOps, optional8.get());
                    final String string10 = Integer.toString(integer5);
                    dataResult9.resultOrPartial((Consumer)SectionStorage.LOGGER::error).ifPresent(object -> map4.put((T)dynamicOps.createString(string10), object));
                }
            }
        }
        return (Dynamic<T>)new Dynamic((DynamicOps)dynamicOps, dynamicOps.createMap((Map)ImmutableMap.of(dynamicOps.createString("Sections"), dynamicOps.createMap((Map)map4), dynamicOps.createString("DataVersion"), dynamicOps.createInt(SharedConstants.getCurrentVersion().getWorldVersion()))));
    }
    
    protected void onSectionLoad(final long long1) {
    }
    
    protected void setDirty(final long long1) {
        final Optional<R> optional4 = (Optional<R>)this.storage.get(long1);
        if (optional4 == null || !optional4.isPresent()) {
            SectionStorage.LOGGER.warn("No data for position: {}", SectionPos.of(long1));
            return;
        }
        this.dirty.add(long1);
    }
    
    private static int getVersion(final Dynamic<?> dynamic) {
        return dynamic.get("DataVersion").asInt(1945);
    }
    
    public void flush(final ChunkPos bph) {
        if (!this.dirty.isEmpty()) {
            for (int integer3 = 0; integer3 < 16; ++integer3) {
                final long long4 = SectionPos.of(bph, integer3).asLong();
                if (this.dirty.contains(long4)) {
                    this.writeColumn(bph);
                    return;
                }
            }
        }
    }
    
    @Override
    public void close() throws IOException {
        this.worker.close();
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
