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

package com.hypixel.hytale.procedurallib.json;

import java.util.function.Function;
import java.util.function.Predicate;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import java.io.Reader;
import com.google.gson.stream.JsonReader;
import java.nio.file.Files;
import java.io.File;
import com.google.gson.JsonObject;
import java.util.Objects;
import javax.annotation.Nonnull;
import java.nio.file.Path;
import javax.annotation.Nullable;
import com.google.gson.JsonElement;

public abstract class JsonLoader<K extends SeedResource, T> extends Loader<K, T>
{
    @Nullable
    protected final JsonElement json;
    
    public JsonLoader(final SeedString<K> seed, final Path dataFolder, @Nullable final JsonElement json) {
        super(seed, dataFolder);
        if (json != null && json.isJsonObject() && json.getAsJsonObject().has("File")) {
            this.json = this.loadFileConstructor(json.getAsJsonObject().get("File").getAsString());
        }
        else {
            this.json = json;
        }
    }
    
    public boolean has(final String name) {
        return this.json != null && this.json.isJsonObject() && this.json.getAsJsonObject().has(name);
    }
    
    @Nonnull
    public JsonElement getOrLoad(@Nonnull JsonElement element) {
        if (element.isJsonObject()) {
            final JsonObject obj = element.getAsJsonObject();
            final JsonElement path = obj.get("File");
            if (path != null && path.isJsonPrimitive() && path.getAsJsonPrimitive().isString()) {
                final JsonElement loaded = this.loadFileElem(path.getAsString());
                element = Objects.requireNonNullElse(loaded, element);
            }
        }
        return element;
    }
    
    @Nullable
    public JsonElement get(final String name) {
        if (this.json == null || !this.json.isJsonObject()) {
            return null;
        }
        JsonElement element = this.json.getAsJsonObject().get(name);
        if (element != null && element.isJsonObject()) {
            element = this.getOrLoad(element);
        }
        return element;
    }
    
    @Nullable
    public JsonElement getRaw(final String name) {
        if (this.json == null || !this.json.isJsonObject()) {
            return null;
        }
        return this.json.getAsJsonObject().get(name);
    }
    
    protected JsonElement loadFile(@Nonnull final String filePath) {
        final Path file = this.dataFolder.resolve(filePath.replace('.', File.separatorChar) + ".json");
        try (final JsonReader reader = new JsonReader(Files.newBufferedReader(file))) {
            return JsonParser.parseReader(reader);
        }
        catch (final Throwable e) {
            throw new Error("Error while loading file reference." + file.toString(), e);
        }
    }
    
    protected JsonElement loadFileElem(@Nonnull final String filePath) {
        return this.loadFile(filePath);
    }
    
    protected JsonElement loadFileConstructor(@Nonnull final String filePath) {
        return this.loadFile(filePath);
    }
    
    @Nonnull
    protected JsonObject mustGetObject(@Nonnull final String key, @Nullable final JsonObject defaultValue) {
        return this.mustGet(key, defaultValue, JsonObject.class, JsonElement::isJsonObject, JsonElement::getAsJsonObject);
    }
    
    @Nonnull
    protected JsonArray mustGetArray(@Nonnull final String key, @Nullable final JsonArray defaultValue) {
        return this.mustGet(key, defaultValue, JsonArray.class, JsonElement::isJsonArray, JsonElement::getAsJsonArray);
    }
    
    @Nonnull
    protected String mustGetString(@Nonnull final String key, @Nullable final String defaultValue) {
        return this.mustGet(key, defaultValue, String.class, JsonLoader::isString, JsonElement::getAsString);
    }
    
    @Nonnull
    protected Boolean mustGetBool(@Nonnull final String key, @Nullable final Boolean defaultValue) {
        return this.mustGet(key, defaultValue, Boolean.class, JsonLoader::isBoolean, JsonElement::getAsBoolean);
    }
    
    @Nonnull
    protected Number mustGetNumber(@Nonnull final String key, @Nullable final Number defaultValue) {
        return this.mustGet(key, defaultValue, Number.class, JsonLoader::isNumber, JsonElement::getAsNumber);
    }
    
    protected <V> V mustGet(@Nonnull final String key, @Nullable final V defaultValue, @Nonnull final Class<V> type, @Nonnull final Predicate<JsonElement> predicate, @Nonnull final Function<JsonElement, V> mapper) {
        return mustGet(key, this.get(key), defaultValue, type, predicate, mapper);
    }
    
    protected static <V> V mustGet(@Nonnull final String key, @Nullable final JsonElement element, @Nullable final V defaultValue, @Nonnull final Class<V> type, @Nonnull final Predicate<JsonElement> predicate, @Nonnull final Function<JsonElement, V> mapper) {
        if (element == null) {
            if (defaultValue != null) {
                return defaultValue;
            }
            throw error("Missing property '%s'", key);
        }
        else {
            if (!predicate.test(element)) {
                throw error("Property '%s' must be of type '%s'", key, type.getSimpleName());
            }
            return mapper.apply(element);
        }
    }
    
    protected static Error error(final String format, final Object... args) {
        return new Error(String.format(format, args));
    }
    
    protected static Error error(final Throwable parent, final String format, final Object... args) {
        return new Error(String.format(format, args), parent);
    }
    
    private static boolean isString(final JsonElement element) {
        return element.isJsonPrimitive() && element.getAsJsonPrimitive().isString();
    }
    
    protected static boolean isNumber(final JsonElement element) {
        return element.isJsonPrimitive() && element.getAsJsonPrimitive().isNumber();
    }
    
    protected static boolean isBoolean(final JsonElement element) {
        return element.isJsonPrimitive() && element.getAsJsonPrimitive().isBoolean();
    }
    
    public interface Constants
    {
        public static final char JSON_FILEPATH_SEPARATOR = '.';
        public static final String KEY_FILE = "File";
    }
}
