package me.km.entities;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.NativeImage;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;

@OnlyIn(Dist.CLIENT)
public class HumanSkinLoader {
    public final static HumanSkinLoader INSTANCE = new HumanSkinLoader();

    private static final ResourceLocation TEXTURE_STEVE = new ResourceLocation("textures/entity/steve.png");

    private final HashMap<String, ResourceLocation> skins = new HashMap<>();
    private final HashSet<String> loading = new HashSet<>();

    private final TextureManager manager = Minecraft.getInstance().getRenderManager().textureManager;

    private HumanSkinLoader() {
    }

    public ResourceLocation getTexture(String name) {
        ResourceLocation loc = skins.get(name);
        if(loc != null) {
            return loc;
        }
        if(loading.add(name)) {
            downloadSkin(name, image -> {
                ResourceLocation rloc = manager.getDynamicTextureLocation("skin_" + name.toLowerCase(), new DynamicTexture(image));
                skins.put(name, rloc);
                loading.remove(name);
            });
        }
        return TEXTURE_STEVE;
    }

    private void downloadSkin(String name, Consumer<NativeImage> delayed) {
        new Thread(() -> {
            HttpURLConnection httpurlconnection = null;
            try {
                URL url = new URL("http://skins.hammerle.me/skins/" + name + ".png");
                httpurlconnection = (HttpURLConnection) url.openConnection(Minecraft.getInstance().getProxy());
                httpurlconnection.setDoInput(true);
                httpurlconnection.setDoOutput(false);
                httpurlconnection.connect();

                int code = httpurlconnection.getResponseCode();
                if(code != 200) {
                    // Failed own server, trying crafatar
                    httpurlconnection.disconnect();
                    httpurlconnection = (HttpURLConnection) (new URL("https://crafatar.com/skins/" + name + ".png"))
                            .openConnection(Minecraft.getInstance().getProxy());
                    httpurlconnection.setDoInput(true);
                    httpurlconnection.setDoOutput(false);
                    httpurlconnection.connect();
                    code = httpurlconnection.getResponseCode();
                    if(code != 200) {
                        LogManager.getLogger().warn("Server response code did return " + code + ", skin servers might be down.");
                        return;
                    }
                }

                NativeImage image = convert(NativeImage.read(httpurlconnection.getInputStream()));
                Minecraft.getInstance().enqueue(() -> delayed.accept(image));
            } catch(Exception ex) {
                LogManager.getLogger().warn("Error occurred when downloading skin, however, skin servers seem to be up.");
            } finally {
                if(httpurlconnection != null) {
                    httpurlconnection.disconnect();
                }
            }
        }).start();
    }

    private NativeImage convert(NativeImage old) {
        boolean flag = old.getHeight() == 32;
        if(flag) {
            NativeImage nativeimage = new NativeImage(64, 64, true);
            nativeimage.copyImageData(old);
            old.close();
            old = nativeimage;
            nativeimage.fillAreaRGBA(0, 32, 64, 32, 0);
            nativeimage.copyAreaRGBA(4, 16, 16, 32, 4, 4, true, false);
            nativeimage.copyAreaRGBA(8, 16, 16, 32, 4, 4, true, false);
            nativeimage.copyAreaRGBA(0, 20, 24, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(4, 20, 16, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(8, 20, 8, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(12, 20, 16, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(44, 16, -8, 32, 4, 4, true, false);
            nativeimage.copyAreaRGBA(48, 16, -8, 32, 4, 4, true, false);
            nativeimage.copyAreaRGBA(40, 20, 0, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(44, 20, -8, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(48, 20, -16, 32, 4, 12, true, false);
            nativeimage.copyAreaRGBA(52, 20, -8, 32, 4, 12, true, false);
        }

        setAreaOpaque(old, 0, 0, 32, 16);
        if(flag) {
            setAreaTransparent(old, 32, 0, 64, 32);
        }

        setAreaOpaque(old, 0, 16, 64, 32);
        setAreaOpaque(old, 16, 48, 48, 64);
        return old;
    }

    private static void setAreaTransparent(NativeImage image, int x, int y, int width, int height) {
        for(int i = x; i < width; ++i) {
            for(int j = y; j < height; ++j) {
                int k = image.getPixelRGBA(i, j);
                if((k >> 24 & 255) < 128) {
                    return;
                }
            }
        }

        for(int l = x; l < width; ++l) {
            for(int i1 = y; i1 < height; ++i1) {
                image.setPixelRGBA(l, i1, image.getPixelRGBA(l, i1) & 16777215);
            }
        }

    }

    private static void setAreaOpaque(NativeImage image, int x, int y, int width, int height) {
        for(int i = x; i < width; ++i) {
            for(int j = y; j < height; ++j) {
                image.setPixelRGBA(i, j, image.getPixelRGBA(i, j) | -16777216);
            }
        }

    }
}
