package net.minestom.codegen.color;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.squareup.javapoet.*;
import net.minestom.codegen.MinestomCodeGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.List;
import java.util.stream.StreamSupport;

public class DyeColorGenerator extends MinestomCodeGenerator {
    private static final Logger LOGGER = LoggerFactory.getLogger(DyeColorGenerator.class);
    private final InputStream dyeColorsFile;
    private final File outputFolder;

    public DyeColorGenerator(@Nullable InputStream dyeColorsFile, @NotNull File outputFolder) {
        this.dyeColorsFile = dyeColorsFile;
        this.outputFolder = outputFolder;
    }

    @Override
    public void generate() {
        if (dyeColorsFile == null) {
            LOGGER.error("Failed to find dye_colors.json.");
            LOGGER.error("Stopped code generation for dye colors.");
            return;
        }
        if (!outputFolder.exists() && !outputFolder.mkdirs()) {
            LOGGER.error("Output folder for code generation does not exist and could not be created.");
            return;
        }
        // Important classes we use alot
        ClassName colorCN = ClassName.get("net.minestom.server.color", "Color");

        JsonArray dyeColors = GSON.fromJson(new InputStreamReader(dyeColorsFile), JsonArray.class);
        ClassName dyeColorCN = ClassName.get("net.minestom.server.color", "DyeColor");
        // Dye Color Enum
        TypeSpec.Builder dyeColorEnum = TypeSpec.enumBuilder(dyeColorCN)
                .addSuperinterface(ClassName.get("net.kyori.adventure.util", "RGBLike"))
                .addModifiers(Modifier.PUBLIC).addJavadoc("AUTOGENERATED by " + getClass().getSimpleName());

        // Fields
        dyeColorEnum.addFields(
                List.of(
                        FieldSpec.builder(colorCN, "textureDiffuseColor", Modifier.PRIVATE, Modifier.FINAL).build(),
                        FieldSpec.builder(colorCN, "textColor", Modifier.PRIVATE, Modifier.FINAL).build(),
                        FieldSpec.builder(colorCN, "fireworkColor", Modifier.PRIVATE, Modifier.FINAL).build(),
                        FieldSpec.builder(TypeName.INT, "mapColorId", Modifier.PRIVATE, Modifier.FINAL).build()
                )
        );

        // Methods
        dyeColorEnum.addMethods(
                List.of(
                        // Constructor
                        MethodSpec.constructorBuilder()
                                .addParameters(
                                        List.of(
                                                ParameterSpec.builder(colorCN, "textureDiffuseColor").addAnnotation(NotNull.class).build(),
                                                ParameterSpec.builder(colorCN, "textColor").addAnnotation(NotNull.class).build(),
                                                ParameterSpec.builder(colorCN, "fireworkColor").addAnnotation(NotNull.class).build(),
                                                ParameterSpec.builder(TypeName.INT, "mapColorId").build()
                                        )
                                )
                                .addStatement("this.textureDiffuseColor = textureDiffuseColor")
                                .addStatement("this.textColor = textColor")
                                .addStatement("this.fireworkColor = fireworkColor")
                                .addStatement("this.mapColorId = mapColorId")
                                .build(),
                        MethodSpec.methodBuilder("color")
                                .addModifiers(Modifier.PUBLIC)
                                .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build()))
                                .addStatement("return this.textureDiffuseColor")
                                .build(),
                        MethodSpec.methodBuilder("textColor")
                                .addModifiers(Modifier.PUBLIC)
                                .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build()))
                                .addStatement("return this.textColor")
                                .build(),
                        MethodSpec.methodBuilder("fireworkColor")
                                .addModifiers(Modifier.PUBLIC)
                                .returns(colorCN.annotated(AnnotationSpec.builder(NotNull.class).build()))
                                .addStatement("return this.fireworkColor")
                                .build(),
                        MethodSpec.methodBuilder("red")
                                .addModifiers(Modifier.PUBLIC)
                                .returns(TypeName.INT)
                                .addAnnotation(Override.class)
                                .addStatement("return this.textureDiffuseColor.red()")
                                .build(),
                        MethodSpec.methodBuilder("green")
                                .returns(TypeName.INT)
                                .addModifiers(Modifier.PUBLIC)
                                .addAnnotation(Override.class)
                                .addStatement("return this.textureDiffuseColor.green()")
                                .build(),
                        MethodSpec.methodBuilder("blue")
                                .returns(TypeName.INT)
                                .addModifiers(Modifier.PUBLIC)
                                .addAnnotation(Override.class)
                                .addStatement("return this.textureDiffuseColor.blue()")
                                .build(),
                        MethodSpec.methodBuilder("mapColorId")
                                .addModifiers(Modifier.PUBLIC)
                                .returns(TypeName.INT)
                                .addStatement("return this.mapColorId")
                                .build()
                )
        );

        // Use data
        for (JsonObject dyeColorObject : StreamSupport.stream(dyeColors.spliterator(), true).map(JsonElement::getAsJsonObject).sorted(Comparator.comparingInt(o -> o.get("id").getAsInt())).toList()) {
            String dyeColorName = dyeColorObject.get("name").getAsString();
            dyeColorEnum.addEnumConstant(toConstant(dyeColorName), TypeSpec.anonymousClassBuilder(
                            "new $T(0x$L), new $T(0x$L), new $T(0x$L), $L",
                            colorCN, Integer.toString(dyeColorObject.get("textureDiffuseColor").getAsInt(), 16),
                            colorCN, Integer.toString(dyeColorObject.get("textColor").getAsInt(), 16),
                            colorCN, Integer.toString(dyeColorObject.get("fireworkColor").getAsInt(), 16),
                            dyeColorObject.get("mapColorId").getAsInt()
                    ).build()
            );
        }

        // Write files to outputFolder
        writeFiles(
                List.of(
                        JavaFile.builder("net.minestom.server.color", dyeColorEnum.build())
                                .indent("    ")
                                .skipJavaLangImports(true)
                                .build()
                ),
                outputFolder
        );
    }
}
