package dev.crucifixbeta.mod.modules.impl.client.hud;

import dev.crucifixbeta.api.utils.render.Render2DUtil;
import dev.crucifixbeta.api.utils.render.TextUtil;
import dev.crucifixbeta.core.impl.FontManager;
import dev.crucifixbeta.mod.modules.HudModule;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;

@Environment(EnvType.CLIENT)
public class EffectHud extends HudModule {
   public static EffectHud INSTANCE;
   public final SliderSetting scale = this.add(new SliderSetting("Scale", 1.0, 0.5, 2.5, 0.05));
   public final SliderSetting nameScale = this.add(new SliderSetting("NameScale", 0.85, 0.5, 1.5, 0.05));
   public final SliderSetting durationScale = this.add(new SliderSetting("DurScale", 0.7, 0.4, 1.5, 0.05));
   public final SliderSetting cornerRadius = this.add(new SliderSetting("CornerRadius", 7.0, 0.0, 14.0, 0.5));
   public final SliderSetting animSpeed = this.add(new SliderSetting("AnimSpeed", 10.0, 1.0, 20.0, 0.5));
   public final ColorSetting backgroundColor = this.add(new ColorSetting("BgColor", new Color(15, 15, 15, 200)));
   public final BooleanSetting showIcon = this.add(new BooleanSetting("ShowIcon", true));
   public final BooleanSetting showPill = this.add(new BooleanSetting("ShowPill", true));
   public final BooleanSetting tintName = this.add(new BooleanSetting("TintName", true));
   public final BooleanSetting hideAmbient = this.add(new BooleanSetting("HideAmbient", false));
   private final Map<String, Float> alphaMap = new HashMap<>();
   private final Map<String, Integer> maxDurationMap = new HashMap<>();
   private final Map<String, EffectHud.EffectCache> displayCache = new HashMap<>();
   private long lastRenderNanos;
   private static final float ROW_HEIGHT;
   private static final float ROW_GAP;
   private static final float PAD_X;
   private static final float PAD_Y;
   private static final float ICON_SIZE;
   private static final float ICON_GAP;
   private static final float PILL_W;
   private static final float PILL_GAP;
   private static final float NAME_DUR_GAP;

   public EffectHud() {
      super("EffectHud", "\u836f\u6c34HUD", 2, 10);
      INSTANCE = this;
   }

   @Override
   public void onRender2D(DrawContext ctx, float tickDelta) {
      if (!nullCheck() && mc.player != null) {
         Collection<StatusEffectInstance> var3 = mc.player.getStatusEffects();
         if (var3.isEmpty() && this.alphaMap.isEmpty()) {
            this.clearHudBounds();
         } else {
            long var4 = System.nanoTime();
            float var6 = this.lastRenderNanos == 0L ? 0.05F : clamp((float)(var4 - this.lastRenderNanos) / 1.0E9F, 0.001F, 0.08F);
            this.lastRenderNanos = var4;
            List<EffectHud.EffectInfo> var7 = this.collectItems(var3, var6);
            if (var7.isEmpty()) {
               this.clearHudBounds();
            } else {
               boolean var8 = HudSetting.useFont() && FontManager.isCustomFontEnabled();
               boolean var9 = false;
               float var10 = this.scale.getValueFloat();
               float var11 = this.nameScale.getValueFloat() * var10;
               float var12 = this.durationScale.getValueFloat() * var10;
               float var13 = 28.0F * var10;
               float var14 = 4.0F * var10;
               float var15 = 8.0F * var10;
               float var16 = 6.0F * var10;
               float var17 = 16.0F * var10;
               float var18 = 8.0F * var10;
               float var19 = 4.0F * var10;
               float var20 = this.cornerRadius.getValueFloat() * var10;
               boolean var21 = this.showIcon.getValue();
               boolean var22 = this.showPill.getValue();
               float var23 = 1.0F;
               float var24 = 0.0F;
               boolean var25 = true;

               for (EffectHud.EffectInfo var27 : var7) {
                  float var28 = clamp(var27.alpha, 0.0F, 1.0F);
                  if (!(var28 < 0.001F)) {
                     var23 = Math.max(var23, var27.totalWidth);
                     if (!var25) {
                        var24 += var14 * var28;
                     }

                     var24 += var13 * var28;
                     var25 = false;
                  }
               }

               if (var24 <= 0.5F) {
                  this.clearHudBounds();
               } else {
                  int var55 = Math.max(1, (int)Math.ceil((double)var23));
                  int var56 = Math.max(1, (int)Math.ceil((double)var24));
                  float var57 = alignHalf((float)this.getHudRenderX(var55));
                  float var29 = alignHalf((float)this.getHudRenderY(var56));
                  boolean var30 = true;

                  for (EffectHud.EffectInfo var32 : var7) {
                     float var33 = clamp(var32.alpha, 0.0F, 1.0F);
                     if (!(var33 < 0.001F)) {
                        if (!var30) {
                           var29 += var14 * var33;
                        }

                        var30 = false;
                        float var34 = alignHalf(var32.totalWidth);
                        float var35 = alignHalf(var29);
                        Color var36 = withAlpha(this.backgroundColor.getValue(), var33);
                        Render2DUtil.drawRoundedRect(ctx.getMatrices(), var57, var35, var34, var13, Math.min(var20, Math.min(var34, var13) / 2.0F), var36);
                        float var37 = var57 + var15;
                        if (var21 && var32.iconId != null) {
                           float var38 = alignHalf(var35 + (var13 - var17) / 2.0F);

                           try {
                              ctx.drawTexture(
                                 var32.iconId,
                                 Math.round(var37),
                                 Math.round(var38),
                                 0.0F,
                                 0.0F,
                                 Math.round(var17),
                                 Math.round(var17),
                                 Math.round(var17),
                                 Math.round(var17)
                              );
                           } catch (Exception var54) {
                           }

                           var37 += var17 + var18;
                        }

                        float var58 = 9.0F * var11;
                        float var39 = 9.0F * var12;
                        float var40 = var58 + var39 + 2.0F * var10;
                        float var41 = alignHalf(var35 + (var13 - var40) / 2.0F);
                        float var42 = alignHalf(var41 + var58 + 2.0F * var10);
                        float var43 = alignHalf(var37);
                        Color var44 = this.tintName.getValue()
                           ? withAlpha(brighten(var32.effectColor, 1.15F), var33)
                           : new Color(255, 255, 255, (int)(235.0F * var33));
                        drawAnchoredScaledText(ctx, var32.name, var43, var41, var44.getRGB(), var11, var9, var8);
                        Color var45 = new Color(180, 180, 180, (int)(220.0F * var33));
                        drawAnchoredScaledText(ctx, var32.duration, var43, var42, var45.getRGB(), var12, var9, var8);
                        if (var22) {
                           float var46 = var57 + var34 - var15 - var19;
                           float var47 = var13 - var16 * 2.0F;
                           float var48 = var35 + var16;
                           float var49 = var19 / 2.0F;
                           Render2DUtil.drawRoundedRect(ctx.getMatrices(), var46, var48, var19, var47, var49, new Color(0, 0, 0, (int)(140.0F * var33)));
                           float var50 = var47 * var32.progress;
                           if (var50 > 0.5F) {
                              float var51 = var48 + (var47 - var50);
                              Color var52 = withAlpha(brighten(var32.effectColor, 1.3F), var33);
                              Color var53 = withAlpha(var32.effectColor, var33);
                              Render2DUtil.drawRoundedRect(ctx.getMatrices(), var46, var51, var19, var50, var49, var53);
                              if (var50 > var19 * 2.0F) {
                                 Render2DUtil.drawRect(ctx.getMatrices(), var46, var51, var19, Math.min(var50 * 0.35F, var47 * 0.4F), var52);
                              }
                           }
                        }

                        var29 += var13 * var33;
                     }
                  }

                  this.setHudBounds(Math.round(var57), Math.round((float)this.getHudRenderY(var56)), var55, var56);
               }
            }
         }
      } else {
         this.clearHudBounds();
      }
   }

   private List<EffectHud.EffectInfo> collectItems(Collection<StatusEffectInstance> active, float dt) {
      float var3 = this.animSpeed.getValueFloat();
      float var4 = this.scale.getValueFloat();
      float var5 = this.nameScale.getValueFloat() * var4;
      float var6 = this.durationScale.getValueFloat() * var4;
      boolean var7 = HudSetting.useFont() && FontManager.isCustomFontEnabled();
      boolean var8 = this.showIcon.getValue();
      boolean var9 = this.showPill.getValue();
      float var10 = 8.0F * var4;
      float var11 = 16.0F * var4;
      float var12 = 8.0F * var4;
      float var13 = 4.0F * var4;
      float var14 = 8.0F * var4;
      HashSet var15 = new HashSet();
      HashMap var16 = new HashMap();

      for (StatusEffectInstance var18 : active) {
         if (!this.hideAmbient.getValue() || !var18.isAmbient()) {
            String var19 = Registries.STATUS_EFFECT.getId(var18.getEffectType().value()).toString();
            var15.add(var19);
            var16.put(var19, var18);
            this.alphaMap.put(var19, lerp(this.alphaMap.getOrDefault(var19, 0.0F), 1.0F, var3 * dt));
            int var20 = var18.getDuration();
            if (var20 > 0) {
               this.maxDurationMap.put(var19, Math.max(this.maxDurationMap.getOrDefault(var19, 0), var20));
            }
         }
      }

      Iterator var36 = this.alphaMap.entrySet().iterator();

      while (var36.hasNext()) {
         Entry var37 = (Entry)var36.next();
         if (!var15.contains(var37.getKey())) {
            float var39 = lerp((Float)var37.getValue(), 0.0F, var3 * dt);
            if (var39 < 0.001F) {
               var36.remove();
               this.maxDurationMap.remove(var37.getKey());
               this.displayCache.remove(var37.getKey());
            } else {
               var37.setValue(var39);
            }
         }
      }

      ArrayList var38 = new ArrayList();

      for (Entry var41 : this.alphaMap.entrySet()) {
         String var21 = (String)var41.getKey();
         StatusEffectInstance var22 = (StatusEffectInstance)var16.get(var21);
         int var23 = var22 != null ? var22.getAmplifier() : 0;
         int var24 = var22 != null ? var22.getDuration() : 0;
         boolean var25 = var22 != null && var22.isInfinite();
         EffectHud.EffectCache var26 = this.displayCache.get(var21);
         String var27;
         String var28;
         Color var29;
         Identifier var30;
         if (var22 != null) {
            var27 = var22.getEffectType().value().getName().getString() + romanLevel(var23);
            var28 = var25 ? "Inf" : formatDuration(var24);
            var29 = new Color(var22.getEffectType().value().getColor() & 16777215);
            var30 = getIconTexture(var21);
            this.displayCache.put(var21, new EffectHud.EffectCache(var27, var28, var29, var30));
         } else if (var26 != null) {
            var27 = var26.name();
            var28 = var26.duration();
            var29 = var26.effectColor();
            var30 = var26.iconId();
         } else {
            var27 = var21;
            var28 = "";
            var29 = Color.WHITE;
            var30 = getIconTexture(var21);
         }

         float var31 = var7 ? FontManager.ui.getWidth(var27) * var5 : (float)mc.textRenderer.getWidth(var27) * var5;
         float var32 = var7 ? FontManager.ui.getWidth(var28) * var6 : (float)mc.textRenderer.getWidth(var28) * var6;
         float var33 = var10;
         if (var8) {
            var33 = var10 + var11 + var12;
         }

         var33 += Math.max(var31, var32);
         if (var9) {
            var33 += var14 + var13;
         }

         var33 += var10;
         int var34 = this.maxDurationMap.getOrDefault(var21, Math.max(var24, 1));
         float var35 = var25 ? 1.0F : clamp((float)var24 / (float)Math.max(1, var34), 0.0F, 1.0F);
         var38.add(new EffectHud.EffectInfo(var27, var28, (Float)var41.getValue(), var29, var33, var35, var30));
      }

      var38.sort(Comparator.comparing(EffectHud.EffectInfo::name));
      return var38;
   }

   private static Identifier getIconTexture(String effectId) {
      String var1 = effectId.contains(":") ? effectId.substring(effectId.indexOf(58) + 1) : effectId;
      return Identifier.of("minecraft", "textures/mob_effect/" + var1 + ".png");
   }

   private static String formatDuration(int ticks) {
      int var1 = Math.max(0, ticks / 20);
      return String.format(Locale.ROOT, "%d:%02d", var1 / 60, var1 % 60);
   }

   private static String romanLevel(int amplifier) {
      int var1 = amplifier + 1;
      if (var1 <= 1) {
         return "";
      } else {
         return " " + switch (var1) {
            case 2 -> "II";
            case 3 -> "III";
            case 4 -> "IV";
            case 5 -> "V";
            case 6 -> "VI";
            case 7 -> "VII";
            case 8 -> "VIII";
            case 9 -> "IX";
            case 10 -> "X";
            default -> Integer.toString(var1);
         };
      }
   }

   private static float clamp(float v, float lo, float hi) {
      return Math.max(lo, Math.min(hi, v));
   }

   private static float lerp(float a, float b, float t) {
      return a + (b - a) * clamp(t, 0.0F, 1.0F);
   }

   private static float alignHalf(float v) {
      return (float)Math.floor((double)v) + 0.5F;
   }

   private static Color withAlpha(Color c, float alphaMul) {
      int var2 = (int)clamp((float)c.getAlpha() * alphaMul, 0.0F, 255.0F);
      return new Color(c.getRed(), c.getGreen(), c.getBlue(), var2);
   }

   private static Color brighten(Color c, float factor) {
      int var2 = (int)clamp((float)c.getRed() * factor, 0.0F, 255.0F);
      int var3 = (int)clamp((float)c.getGreen() * factor, 0.0F, 255.0F);
      int var4 = (int)clamp((float)c.getBlue() * factor, 0.0F, 255.0F);
      return new Color(var2, var3, var4, c.getAlpha());
   }

   private static void drawAnchoredScaledText(DrawContext ctx, String text, float x, float y, int color, float scale, boolean shadow, boolean customFont) {
      MatrixStack var8 = ctx.getMatrices();
      var8.push();
      var8.translate(x, y, 0.0F);
      var8.scale(scale, scale, 1.0F);
      TextUtil.drawString(ctx, text, 0.0, 0.0, color, customFont, shadow);
      var8.pop();
   }

   static {

      NAME_DUR_GAP = 2.0F;
      PILL_GAP = 8.0F;
      PILL_W = 4.0F;
      ICON_GAP = 8.0F;
      ICON_SIZE = 16.0F;
      PAD_Y = 6.0F;
      PAD_X = 8.0F;
      ROW_GAP = 4.0F;
      ROW_HEIGHT = 28.0F;
   }
@Environment(EnvType.CLIENT)
   private static record EffectCache(String name, String duration, Color effectColor, Identifier iconId) {
   }

   @Environment(EnvType.CLIENT)
   private static record EffectInfo(String name, String duration, float alpha, Color effectColor, float totalWidth, float progress, Identifier iconId) {
   }
}
