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

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

import org.bson.json.JsonMode;
import org.bson.json.StrictJsonWriter;
import java.util.concurrent.CompletionStage;
import java.io.BufferedWriter;
import java.nio.file.StandardOpenOption;
import com.hypixel.hytale.codec.ExtraInfo;
import com.hypixel.hytale.codec.Codec;
import com.google.gson.JsonParser;
import java.io.Writer;
import org.bson.json.JsonWriter;
import java.io.StringWriter;
import org.bson.BsonString;
import org.bson.BsonValue;
import com.google.gson.JsonElement;
import com.hypixel.hytale.common.util.ExceptionUtil;
import java.util.logging.Level;
import java.util.function.Function;
import java.util.function.Supplier;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import com.hypixel.hytale.common.util.PathUtil;
import java.io.IOException;
import com.hypixel.hytale.sneakythrow.SneakyThrow;
import java.nio.file.OpenOption;
import java.nio.file.StandardCopyOption;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.util.concurrent.CompletableFuture;
import java.nio.file.Path;
import com.hypixel.hytale.server.core.util.io.ByteBufUtil;
import javax.annotation.Nonnull;
import io.netty.buffer.ByteBuf;
import org.bson.BsonReader;
import org.bson.BsonBinaryReader;
import java.nio.ByteBuffer;
import org.bson.BsonWriter;
import org.bson.io.BsonOutput;
import org.bson.BsonBinaryWriter;
import org.bson.io.BasicOutputBuffer;
import com.hypixel.hytale.common.util.ArrayUtil;
import javax.annotation.Nullable;
import org.bson.BsonDocument;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.json.JsonWriterSettings;
import com.hypixel.hytale.logger.HytaleLogger;

public class BsonUtil
{
    private static final HytaleLogger LOGGER;
    public static final JsonWriterSettings SETTINGS;
    private static final BsonDocumentCodec codec;
    private static final DecoderContext decoderContext;
    private static final EncoderContext encoderContext;
    public static final BsonDocumentCodec BSON_DOCUMENT_CODEC;
    
    public static byte[] writeToBytes(@Nullable final BsonDocument document) {
        if (document == null) {
            return ArrayUtil.EMPTY_BYTE_ARRAY;
        }
        try (final BasicOutputBuffer buffer = new BasicOutputBuffer()) {
            BsonUtil.codec.encode((BsonWriter)new BsonBinaryWriter(buffer), document, BsonUtil.encoderContext);
            return buffer.toByteArray();
        }
    }
    
    public static BsonDocument readFromBytes(@Nullable final byte[] buf) {
        if (buf == null || buf.length == 0) {
            return null;
        }
        return BsonUtil.codec.decode((BsonReader)new BsonBinaryReader(ByteBuffer.wrap(buf)), BsonUtil.decoderContext);
    }
    
    public static BsonDocument readFromBuffer(@Nullable final ByteBuffer buf) {
        if (buf == null || !buf.hasRemaining()) {
            return null;
        }
        return BsonUtil.codec.decode((BsonReader)new BsonBinaryReader(buf), BsonUtil.decoderContext);
    }
    
    public static BsonDocument readFromBinaryStream(@Nonnull final ByteBuf buf) {
        return readFromBytes(ByteBufUtil.readByteArray(buf));
    }
    
    public static void writeToBinaryStream(@Nonnull final ByteBuf buf, final BsonDocument doc) {
        ByteBufUtil.writeByteArray(buf, writeToBytes(doc));
    }
    
    @Nonnull
    public static CompletableFuture<Void> writeDocumentBytes(@Nonnull final Path file, final BsonDocument document) {
        try {
            if (Files.isRegularFile(file, new LinkOption[0])) {
                final Path resolve = file.resolveSibling(String.valueOf(file.getFileName()) + ".bak");
                Files.move(file, resolve, StandardCopyOption.REPLACE_EXISTING);
            }
            byte[] bytes;
            try (final BasicOutputBuffer bob = new BasicOutputBuffer()) {
                BsonUtil.codec.encode((BsonWriter)new BsonBinaryWriter(bob), document, BsonUtil.encoderContext);
                bytes = bob.toByteArray();
            }
            return CompletableFuture.runAsync(SneakyThrow.sneakyRunnable(() -> Files.write(file, bytes, new OpenOption[0])));
        }
        catch (final IOException e) {
            return CompletableFuture.failedFuture(e);
        }
    }
    
    @Nonnull
    public static CompletableFuture<Void> writeDocument(@Nonnull final Path file, final BsonDocument document) {
        return writeDocument(file, document, true);
    }
    
    @Nonnull
    public static CompletableFuture<Void> writeDocument(@Nonnull final Path file, final BsonDocument document, final boolean backup) {
        try {
            final Path parent = PathUtil.getParent(file);
            if (!Files.exists(parent, new LinkOption[0])) {
                Files.createDirectories(parent, (FileAttribute<?>[])new FileAttribute[0]);
            }
            if (backup && Files.isRegularFile(file, new LinkOption[0])) {
                final Path resolve = file.resolveSibling(String.valueOf(file.getFileName()) + ".bak");
                Files.move(file, resolve, StandardCopyOption.REPLACE_EXISTING);
            }
            final String json = toJson(document);
            return CompletableFuture.runAsync(SneakyThrow.sneakyRunnable(() -> Files.writeString(file, json, new OpenOption[0])));
        }
        catch (final IOException e) {
            return CompletableFuture.failedFuture(e);
        }
    }
    
    @Nonnull
    public static CompletableFuture<BsonDocument> readDocument(@Nonnull final Path file) {
        return readDocument(file, true);
    }
    
    @Nonnull
    public static CompletableFuture<BsonDocument> readDocument(@Nonnull final Path file, final boolean backup) {
        BasicFileAttributes attributes;
        try {
            attributes = Files.readAttributes(file, BasicFileAttributes.class, new LinkOption[0]);
        }
        catch (final IOException ignored) {
            if (backup) {
                return readDocumentBak(file);
            }
            return CompletableFuture.completedFuture((BsonDocument)null);
        }
        if (attributes.size() != 0L) {
            final CompletableFuture<BsonDocument> future = CompletableFuture.supplyAsync((Supplier<Object>)SneakyThrow.sneakySupplier(() -> Files.readString(file))).thenApply((Function<? super Object, ? extends BsonDocument>)BsonDocument::parse);
            return backup ? future.exceptionallyCompose(t -> readDocumentBak(file)) : future;
        }
        BsonUtil.LOGGER.at(Level.WARNING).log("Error loading file %s, file was found to be entirely empty", file);
        if (backup) {
            return readDocumentBak(file);
        }
        return CompletableFuture.completedFuture((BsonDocument)null);
    }
    
    @Nullable
    public static BsonDocument readDocumentNow(@Nonnull final Path file) {
        BasicFileAttributes attributes;
        try {
            attributes = Files.readAttributes(file, BasicFileAttributes.class, new LinkOption[0]);
        }
        catch (final IOException e) {
            HytaleLogger.getLogger().atWarning().log(ExceptionUtil.toStringWithStack(e));
            return null;
        }
        if (attributes.size() == 0L) {
            return null;
        }
        String contentsString;
        try {
            contentsString = Files.readString(file);
        }
        catch (final IOException e2) {
            return null;
        }
        return BsonDocument.parse(contentsString);
    }
    
    @Nonnull
    public static CompletableFuture<BsonDocument> readDocumentBak(@Nonnull final Path fileOrig) {
        final Path file = fileOrig.resolveSibling(String.valueOf(fileOrig.getFileName()) + ".bak");
        BasicFileAttributes attributes;
        try {
            attributes = Files.readAttributes(file, BasicFileAttributes.class, new LinkOption[0]);
        }
        catch (final IOException ignored) {
            return CompletableFuture.completedFuture((BsonDocument)null);
        }
        if (attributes.size() == 0L) {
            BsonUtil.LOGGER.at(Level.WARNING).log("Error loading backup file %s, file was found to be entirely empty", file);
            return CompletableFuture.completedFuture((BsonDocument)null);
        }
        BsonUtil.LOGGER.at(Level.WARNING).log("Loading %s backup file for %s!", file, fileOrig);
        return CompletableFuture.supplyAsync((Supplier<Object>)SneakyThrow.sneakySupplier(() -> Files.readString(file))).thenApply((Function<? super Object, ? extends BsonDocument>)BsonDocument::parse);
    }
    
    public static BsonValue translateJsonToBson(@Nonnull final JsonElement element) {
        if (element.isJsonObject()) {
            return BsonDocument.parse(element.toString());
        }
        return new BsonString(element.getAsString());
    }
    
    public static JsonElement translateBsonToJson(final BsonDocument value) {
        try (final StringWriter writer = new StringWriter()) {
            BsonUtil.codec.encode((BsonWriter)new JsonWriter(writer, BsonUtil.SETTINGS), value, BsonUtil.encoderContext);
            return JsonParser.parseString(writer.toString());
        }
        catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static String toJson(final BsonDocument document) {
        final StringWriter writer = new StringWriter();
        BsonUtil.BSON_DOCUMENT_CODEC.encode((BsonWriter)new JsonWriter(writer, BsonUtil.SETTINGS), document, BsonUtil.encoderContext);
        return writer.toString();
    }
    
    public static <T> void writeSync(@Nonnull final Path path, @Nonnull final Codec<T> codec, final T value, @Nonnull final HytaleLogger logger) throws IOException {
        final Path parent = PathUtil.getParent(path);
        if (!Files.exists(parent, new LinkOption[0])) {
            Files.createDirectories(parent, (FileAttribute<?>[])new FileAttribute[0]);
        }
        if (Files.isRegularFile(path, new LinkOption[0])) {
            final Path resolve = path.resolveSibling(String.valueOf(path.getFileName()) + ".bak");
            Files.move(path, resolve, StandardCopyOption.REPLACE_EXISTING);
        }
        final ExtraInfo extraInfo = ExtraInfo.THREAD_LOCAL.get();
        final BsonValue bsonValue = codec.encode(value, extraInfo);
        extraInfo.getValidationResults().logOrThrowValidatorExceptions(logger);
        final BsonDocument document = bsonValue.asDocument();
        try (final BufferedWriter writer = Files.newBufferedWriter(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
            BsonUtil.BSON_DOCUMENT_CODEC.encode((BsonWriter)new JsonWriter(writer, BsonUtil.SETTINGS), document, BsonUtil.encoderContext);
        }
    }
    
    static {
        LOGGER = HytaleLogger.forEnclosingClass();
        SETTINGS = JsonWriterSettings.builder().outputMode(JsonMode.STRICT).indent(true).newLineCharacters("\n").int64Converter((value, writer) -> writer.writeNumber(Long.toString(value))).build();
        codec = new BsonDocumentCodec();
        decoderContext = DecoderContext.builder().build();
        encoderContext = EncoderContext.builder().build();
        BSON_DOCUMENT_CODEC = new BsonDocumentCodec();
    }
}
