package com.agentdid127.resourcepack.forwards.impl.other;

import com.agentdid127.resourcepack.library.Converter;
import com.agentdid127.resourcepack.library.PackConverter;
import com.agentdid127.resourcepack.library.pack.Pack;
import com.agentdid127.resourcepack.library.utilities.JsonUtil;
import com.agentdid127.resourcepack.library.utilities.Logger;
import com.agentdid127.resourcepack.library.utilities.Util;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.stream.Stream;

public class BlockStateConverter extends Converter {
    private final int from;
    private final int to;
    private boolean anyChanges;

    public BlockStateConverter(PackConverter packConverter, int from, int to) {
        super(packConverter);
        this.from = from;
        this.to = to;
        this.anyChanges = false;
    }

    @Override
    public boolean shouldConvert(Gson gson, int from, int to) {
        return true;
    }

    /**
     * Updates blockstates in blockstates folder
     *
     * @param pack
     * @throws IOException
     */
    @Override
    public void convert(Pack pack) throws IOException {
        Path states = pack.getWorkingPath().resolve("assets/minecraft/blockstates".replace("/", File.separator));
        if (!states.toFile().exists()) {
            return;
        }

        Logger.addTab();
        try (Stream<Path> pathStream = Files.list(states).filter(file -> file.toString().endsWith(".json"))) {
            pathStream.forEach(file -> {
                try {
                    JsonObject json = JsonUtil.readJson(packConverter.getGson(), file);
                    if (json != null) {
                        // process multipart
                        if (json.has("multipart")) {
                            JsonElement multipart = json.get("multipart");
                            if (multipart.isJsonArray()) {
                                JsonArray multipartArray = multipart.getAsJsonArray();
                                for (JsonElement element : multipartArray) {
                                    JsonObject multipartObject = element.getAsJsonObject();
                                    for (Map.Entry<String, JsonElement> entry : multipartObject.entrySet()) {
                                        updateModelPath(entry);
                                    }
                                }
                            }
                        }

                        // process variants
                        if (json.has("variants")) {
                            JsonElement variants = json.get("variants");
                            if (variants.isJsonObject()) {
                                JsonObject variantsObject = variants.getAsJsonObject();
                                // change "normal" key to ""
                                if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.12.2")
                                        && to >= Util.getVersionProtocol(packConverter.getGson(), "1.13")
                                        && variantsObject.has("normal")) {
                                    JsonElement normal = variantsObject.get("normal");
                                    if (normal instanceof JsonObject || normal instanceof JsonArray) {
                                        variantsObject.add("", normal);
                                        variantsObject.remove("normal");
                                        anyChanges = true;
                                    }
                                }

                                // update model paths to prepend block
                                for (Map.Entry<String, JsonElement> entry : variantsObject.entrySet()) {
                                    updateModelPath(entry);
                                }
                            }
                        }

                        if (anyChanges) {
                            JsonUtil.writeJson(packConverter.getGson(), file, json);
                            Logger.debug("Converted " + file.getFileName());
                        }
                    } else {
                        Logger.log("Failed to convert: " + file.getFileName() + " as JSON is invalid.");
                    }
                } catch (IOException e) {
                    Util.propagate(e);
                }
            });
        }
        Logger.subTab();
    }

    /**
     * Updates Model paths
     *
     * @param entry
     */
    private void updateModelPath(Map.Entry<String, JsonElement> entry) {
        if (entry.getValue() instanceof JsonObject) {
            JsonObject value = entry.getValue().getAsJsonObject();
            if (value.has("model")) {
                updateModelObject(value);
            }
        } else if (entry.getValue() instanceof JsonArray) {
            for (JsonElement jsonElement : ((JsonArray) entry.getValue())) {
                if (jsonElement instanceof JsonObject) {
                    JsonObject value = jsonElement.getAsJsonObject();
                    if (value.has("model")) {
                        updateModelObject(value);
                    }
                }
            }
        }
    }

    private void updateModelObject(JsonObject value) {
        NameConverter nameConverter = packConverter.getConverter(NameConverter.class);

        String[] split = value.get("model").getAsString().split("/");
        String val = split[split.length - 1];
        String prefix = value.get("model").getAsString().substring(0, value.get("model").getAsString().length() - val.length());

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.8.9")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.9")) {
            prefix = "block/" + prefix;
            anyChanges = true;
        }

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.12.2")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.13")) {
            val = nameConverter.getBlockMapping_1_13().remap(val);
            prefix = prefix.replaceAll("blocks", "block");
            anyChanges = true;
        }

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.13.2")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.14")) {
            val = nameConverter.getBlockMapping_1_14().remap(val);
            anyChanges = true;
        }

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.16.5")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.17")) {
            val = nameConverter.getBlockMapping_1_17().remap(val);
            anyChanges = true;
        }

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.18.2")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.19")) {
            val = nameConverter.getBlockMapping_1_19().remap(val);
            anyChanges = true;
        }

        if (from <= Util.getVersionProtocol(packConverter.getGson(), "1.19.2")
                && to >= Util.getVersionProtocol(packConverter.getGson(), "1.19.3")) {
            prefix = "minecraft:" + prefix;
            anyChanges = true;
        }

        String result = prefix + val;
        if (anyChanges) {
            value.addProperty("model", result);
        }
    }
}