package dev.crucifixbeta.mod.gui.fonts;

import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.chars.Char2ObjectArrayMap;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.Objects;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.client.texture.NativeImage.Format;
import net.minecraft.util.Identifier;

@Environment(EnvType.CLIENT)
class GlyphMap {
   final char fromIncl;
   final char toExcl;
   final Font font;
   Font secondFont;
   final Identifier bindToTexture;
   final int pixelPadding;
   private final Char2ObjectArrayMap<Glyph> glyphs = new Char2ObjectArrayMap();
   int width;
   int height;
   boolean generated = false;

   public GlyphMap(char from, char to, Font font, Identifier identifier, int padding) {
      this.fromIncl = from;
      this.toExcl = to;
      this.font = font;
      this.bindToTexture = identifier;
      this.pixelPadding = padding;
   }

   public GlyphMap(char from, char to, Font font, Font secondFont, Identifier identifier, int padding) {
      this(from, to, font, identifier, padding);
      this.secondFont = secondFont;
   }

   public static void registerBufferedImageTexture(Identifier i, BufferedImage bi) {
      try {
         int var2 = bi.getWidth();
         int var3 = bi.getHeight();
         NativeImage var4 = new NativeImage(Format.RGBA, var2, var3, false);
         WritableRaster var5 = bi.getRaster();
         ColorModel var6 = bi.getColorModel();
         int var7 = var5.getNumBands();
         int var8 = var5.getDataBuffer().getDataType();

         Object var9 = switch (var8) {
            case 0 -> new byte[var7];
            case 1 -> new short[var7];
            default -> throw new IllegalArgumentException("Unknown data buffer type: " + var8);
            case 3 -> new int[var7];
            case 4 -> new float[var7];
            case 5 -> new double[var7];
         };

         for (int var10 = 0; var10 < var3; var10++) {
            for (int var11 = 0; var11 < var2; var11++) {
               var5.getDataElements(var11, var10, var9);
               int var12 = var6.getAlpha(var9);
               int var13 = var6.getRed(var9);
               int var14 = var6.getGreen(var9);
               int var15 = var6.getBlue(var9);
               int var16 = var12 << 24 | var15 << 16 | var14 << 8 | var13;
               var4.setColor(var11, var10, var16);
            }
         }

         NativeImageBackedTexture var18 = new NativeImageBackedTexture(var4);
         if (RenderSystem.isOnRenderThread()) {
            var18.upload();
            MinecraftClient.getInstance().getTextureManager().registerTexture(i, var18);
         } else {
            RenderSystem.recordRenderCall(() -> {
               var18.upload();
               MinecraftClient.getInstance().getTextureManager().registerTexture(i, var18);
            });
         }
      } catch (Throwable var17) {
         var17.printStackTrace();
      }
   }

   public Glyph getGlyph(char c) {
      if (!this.generated) {
         this.generate();
      }

      return (Glyph)this.glyphs.get(c);
   }

   public void destroy() {
      MinecraftClient.getInstance().getTextureManager().destroyTexture(this.bindToTexture);
      this.glyphs.clear();
      this.width = -1;
      this.height = -1;
      this.generated = false;
   }

   public boolean contains(char c) {
      return c >= this.fromIncl && c < this.toExcl;
   }

   private Font getFontForGlyph(char c) {
      return this.font.canDisplay(c) ? this.font : Objects.requireNonNullElse(this.secondFont, this.font);
   }

   public void generate() {
      if (!this.generated) {
         int var1 = this.toExcl - this.fromIncl - 1;
         int var2 = (int)(Math.ceil(Math.sqrt((double)var1)) * 1.5);
         this.glyphs.clear();
         int var3 = 0;
         int var4 = 0;
         int var5 = 0;
         int var6 = 0;
         int var7 = 0;
         int var8 = 0;
         int var9 = 0;
         ArrayList<Glyph> var10 = new ArrayList<>();
         AffineTransform var11 = new AffineTransform();

         for (FontRenderContext var12 = new FontRenderContext(var11, true, false); var3 <= var1; var4++) {
            char var13 = (char)(this.fromIncl + var3);
            Font var14 = this.getFontForGlyph(var13);
            Rectangle2D var15 = var14.getStringBounds(String.valueOf(var13), var12);
            int var16 = (int)Math.ceil(var15.getWidth());
            int var17 = (int)Math.ceil(var15.getHeight());
            var3++;
            var5 = Math.max(var5, var7 + var16);
            var6 = Math.max(var6, var8 + var17);
            if (var4 >= var2) {
               var7 = 0;
               var8 += var9 + this.pixelPadding;
               var4 = 0;
               var9 = 0;
            }

            var9 = Math.max(var9, var17);
            var10.add(new Glyph(var7, var8, var16, var17, var13, this));
            var7 += var16 + this.pixelPadding;
         }

         BufferedImage var18 = new BufferedImage(Math.max(var5 + this.pixelPadding, 1), Math.max(var6 + this.pixelPadding, 1), 2);
         this.width = var18.getWidth();
         this.height = var18.getHeight();
         Graphics2D var19 = var18.createGraphics();
         var19.setColor(new Color(255, 255, 255, 0));
         var19.fillRect(0, 0, this.width, this.height);
         var19.setColor(Color.WHITE);
         var19.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
         var19.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
         var19.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

         for (Glyph var21 : var10) {
            var19.setFont(this.getFontForGlyph(var21.value()));
            FontMetrics var22 = var19.getFontMetrics();
            var19.drawString(String.valueOf(var21.value()), var21.u(), var21.v() + var22.getAscent());
            this.glyphs.put(var21.value(), var21);
         }

         registerBufferedImageTexture(this.bindToTexture, var18);
         this.generated = true;
      }
   }
}
