package net.minecraft.world.level.storage;

import org.apache.logging.log4j.LogManager;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.LevelConflictException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.OutputStream;
import net.minecraft.nbt.NbtIo;
import java.io.FileOutputStream;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.Util;
import javax.annotation.Nullable;
import net.minecraft.server.MinecraftServer;
import com.mojang.datafixers.DataFixer;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import java.io.File;
import org.apache.logging.log4j.Logger;

public class LevelStorage implements PlayerIO {
    private static final Logger LOGGER;
    private final File worldDir;
    private final File playerDir;
    private final long sessionId;
    private final String levelId;
    private final StructureManager structureManager;
    protected final DataFixer fixerUpper;
    
    public LevelStorage(final File file, final String string, @Nullable final MinecraftServer minecraftServer, final DataFixer dataFixer) {
        this.sessionId = Util.getMillis();
        this.fixerUpper = dataFixer;
        (this.worldDir = new File(file, string)).mkdirs();
        this.playerDir = new File(this.worldDir, "playerdata");
        this.levelId = string;
        if (minecraftServer != null) {
            this.playerDir.mkdirs();
            this.structureManager = new StructureManager(minecraftServer, this.worldDir, dataFixer);
        }
        else {
            this.structureManager = null;
        }
        this.initiateSession();
    }
    
    public void saveLevelData(final LevelData crm, @Nullable final CompoundTag jt) {
        crm.setVersion(19133);
        final CompoundTag jt2 = crm.createTag(jt);
        final CompoundTag jt3 = new CompoundTag();
        jt3.put("Data", jt2);
        try {
            final File file6 = new File(this.worldDir, "level.dat_new");
            final File file7 = new File(this.worldDir, "level.dat_old");
            final File file8 = new File(this.worldDir, "level.dat");
            NbtIo.writeCompressed(jt3, new FileOutputStream(file6));
            if (file7.exists()) {
                file7.delete();
            }
            file8.renameTo(file7);
            if (file8.exists()) {
                file8.delete();
            }
            file6.renameTo(file8);
            if (file6.exists()) {
                file6.delete();
            }
        }
        catch (Exception exception6) {
            exception6.printStackTrace();
        }
    }
    
    private void initiateSession() {
        try {
            final File file2 = new File(this.worldDir, "session.lock");
            final DataOutputStream dataOutputStream3 = new DataOutputStream(new FileOutputStream(file2));
            try {
                dataOutputStream3.writeLong(this.sessionId);
            }
            finally {
                dataOutputStream3.close();
            }
        }
        catch (IOException iOException2) {
            iOException2.printStackTrace();
            throw new RuntimeException("Failed to check session lock, aborting");
        }
    }
    
    public File getFolder() {
        return this.worldDir;
    }
    
    public void checkSession() throws LevelConflictException {
        try {
            final File file2 = new File(this.worldDir, "session.lock");
            final DataInputStream dataInputStream3 = new DataInputStream(new FileInputStream(file2));
            try {
                if (dataInputStream3.readLong() != this.sessionId) {
                    throw new LevelConflictException("The save is being accessed from another location, aborting");
                }
            }
            finally {
                dataInputStream3.close();
            }
        }
        catch (IOException iOException2) {
            throw new LevelConflictException("Failed to check session lock, aborting");
        }
    }
    
    @Nullable
    public LevelData prepareLevel() {
        File file2 = new File(this.worldDir, "level.dat");
        if (file2.exists()) {
            final LevelData crm3 = LevelStorageSource.getLevelData(file2, this.fixerUpper);
            if (crm3 != null) {
                return crm3;
            }
        }
        file2 = new File(this.worldDir, "level.dat_old");
        if (file2.exists()) {
            return LevelStorageSource.getLevelData(file2, this.fixerUpper);
        }
        return null;
    }
    
    public void saveLevelData(final LevelData crm) {
        this.saveLevelData(crm, null);
    }
    
    @Override
    public void save(final Player ayj) {
        try {
            final CompoundTag jt3 = ayj.saveWithoutId(new CompoundTag());
            final File file4 = new File(this.playerDir, ayj.getStringUUID() + ".dat.tmp");
            final File file5 = new File(this.playerDir, ayj.getStringUUID() + ".dat");
            NbtIo.writeCompressed(jt3, new FileOutputStream(file4));
            if (file5.exists()) {
                file5.delete();
            }
            file4.renameTo(file5);
        }
        catch (Exception exception3) {
            LevelStorage.LOGGER.warn("Failed to save player data for {}", ayj.getName().getString());
        }
    }
    
    @Nullable
    @Override
    public CompoundTag load(final Player ayj) {
        CompoundTag jt3 = null;
        try {
            final File file4 = new File(this.playerDir, ayj.getStringUUID() + ".dat");
            if (file4.exists() && file4.isFile()) {
                jt3 = NbtIo.readCompressed(new FileInputStream(file4));
            }
        }
        catch (Exception exception4) {
            LevelStorage.LOGGER.warn("Failed to load player data for {}", ayj.getName().getString());
        }
        if (jt3 != null) {
            final int integer4 = jt3.contains("DataVersion", 3) ? jt3.getInt("DataVersion") : -1;
            ayj.load(NbtUtils.update(this.fixerUpper, DataFixTypes.PLAYER, jt3, integer4));
        }
        return jt3;
    }
    
    public String[] getSeenPlayers() {
        String[] arr2 = this.playerDir.list();
        if (arr2 == null) {
            arr2 = new String[0];
        }
        for (int integer3 = 0; integer3 < arr2.length; ++integer3) {
            if (arr2[integer3].endsWith(".dat")) {
                arr2[integer3] = arr2[integer3].substring(0, arr2[integer3].length() - 4);
            }
        }
        return arr2;
    }
    
    public StructureManager getStructureManager() {
        return this.structureManager;
    }
    
    public DataFixer getFixerUpper() {
        return this.fixerUpper;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
