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

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

import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.util.RawJsonReader;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.util.Map;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import org.bson.BsonDocument;
import com.hypixel.hytale.server.core.util.BsonUtil;
import com.hypixel.hytale.codec.ExtraInfo;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.nio.file.DirectoryStream;
import java.util.logging.Level;
import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import java.nio.file.Path;
import javax.annotation.Nonnull;
import com.hypixel.hytale.logger.HytaleLogger;

public class DiskDataStore<T> implements DataStore<T>
{
    private static final String EXTENSION = ".json";
    private static final int EXTENSION_LEN;
    private static final String EXTENSION_BACKUP = ".json.bak";
    private static final String GLOB = "*.json";
    private static final String GLOB_WITH_BACKUP = "*.{json,json.bak}";
    @Nonnull
    private final HytaleLogger logger;
    @Nonnull
    private final Path path;
    private final BuilderCodec<T> codec;
    
    public DiskDataStore(@Nonnull final String path, final BuilderCodec<T> codec) {
        this.logger = HytaleLogger.get("DataStore|" + path);
        this.path = Universe.get().getPath().resolve(path);
        this.codec = codec;
        if (Files.isDirectory(this.path, new LinkOption[0])) {
            try (final DirectoryStream<Path> paths = Files.newDirectoryStream(this.path, "*.bson")) {
                for (final Path oldPath : paths) {
                    final Path newPath = getPathFromId(this.path, getIdFromPath(oldPath));
                    try {
                        Files.move(oldPath, newPath, new CopyOption[0]);
                    }
                    catch (final IOException ex) {}
                }
            }
            catch (final IOException e) {
                this.logger.at(Level.SEVERE).withCause(e).log("Failed to migrate files form .bson to .json!");
            }
        }
    }
    
    @Nonnull
    public Path getPath() {
        return this.path;
    }
    
    @Override
    public BuilderCodec<T> getCodec() {
        return this.codec;
    }
    
    @Nullable
    @Override
    public T load(final String id) throws IOException {
        final Path filePath = getPathFromId(this.path, id);
        return Files.exists(filePath, new LinkOption[0]) ? this.load0(filePath) : null;
    }
    
    @Override
    public void save(final String id, final T value) {
        final ExtraInfo extraInfo = ExtraInfo.THREAD_LOCAL.get();
        final BsonDocument bsonValue = this.codec.encode(value, extraInfo);
        extraInfo.getValidationResults().logOrThrowValidatorExceptions(this.logger);
        BsonUtil.writeDocument(getPathFromId(this.path, id), bsonValue.asDocument()).join();
    }
    
    @Override
    public void remove(final String id) throws IOException {
        Files.deleteIfExists(getPathFromId(this.path, id));
        Files.deleteIfExists(getBackupPathFromId(this.path, id));
    }
    
    @Nonnull
    @Override
    public List<String> list() throws IOException {
        final List<String> list = new ObjectArrayList<String>();
        try (final DirectoryStream<Path> paths = Files.newDirectoryStream(this.path, "*.json")) {
            for (final Path path : paths) {
                list.add(getIdFromPath(path));
            }
        }
        return list;
    }
    
    @Nonnull
    @Override
    public Map<String, T> loadAll() throws IOException {
        final Map<String, T> map = new Object2ObjectOpenHashMap<String, T>();
        try (final DirectoryStream<Path> paths = Files.newDirectoryStream(this.path, "*.json")) {
            for (final Path path : paths) {
                map.put(getIdFromPath(path), this.load0(path));
            }
        }
        return map;
    }
    
    @Override
    public void removeAll() throws IOException {
        try (final DirectoryStream<Path> paths = Files.newDirectoryStream(this.path, "*.{json,json.bak}")) {
            for (final Path path : paths) {
                Files.delete(path);
            }
        }
    }
    
    @Nullable
    protected T load0(@Nonnull final Path path) throws IOException {
        return RawJsonReader.readSync(path, this.codec, this.logger);
    }
    
    @Nonnull
    protected static Path getPathFromId(@Nonnull final Path path, final String id) {
        return path.resolve(id + ".json");
    }
    
    @Nonnull
    protected static Path getBackupPathFromId(@Nonnull final Path path, final String id) {
        return path.resolve(id + ".json.bak");
    }
    
    @Nonnull
    protected static String getIdFromPath(@Nonnull final Path path) {
        final String fileName = path.getFileName().toString();
        return fileName.substring(0, fileName.length() - DiskDataStore.EXTENSION_LEN);
    }
    
    static {
        EXTENSION_LEN = ".json".length();
    }
}
