package asm.mixins;

import dev.crucifixbeta.mod.modules.impl.render.Ambience;
import dev.crucifixbeta.mod.modules.impl.render.NoRender;
import java.awt.Color;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.dimension.DimensionType;
import org.joml.Vector3f;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Environment(EnvType.CLIENT)
@Mixin({LightmapTextureManager.class})
public class MixinLightmapTextureManager {
   @Final
   @Shadow
   private NativeImageBackedTexture texture;
   @Final
   @Shadow
   private NativeImage image;
   @Shadow
   private boolean dirty;
   @Shadow
   private float flickerIntensity;
   @Final
   @Shadow
   private GameRenderer renderer;
   @Final
   @Shadow
   private MinecraftClient client;

   @Shadow
   private static void clamp(Vector3f vec) {
      vec.set(MathHelper.clamp(vec.x, 0.0F, 1.0F), MathHelper.clamp(vec.y, 0.0F, 1.0F), MathHelper.clamp(vec.z, 0.0F, 1.0F));
   }

   @Shadow
   public static float getBrightness(DimensionType type, int lightLevel) {
      float var2 = (float)lightLevel / 15.0F;
      float var3 = var2 / (4.0F - 3.0F * var2);
      return MathHelper.lerp(type.ambientLight(), var3, 1.0F);
   }

   @Redirect(
      method = {"update"},
      at = @At(
         value = "INVOKE",
         target = "Lnet/minecraft/client/network/ClientPlayerEntity;hasStatusEffect(Lnet/minecraft/registry/entry/RegistryEntry;)Z",
         ordinal = 0,
         remap = false
      ),
      require = 0
   )
   private boolean nightVisionHook(ClientPlayerEntity instance, RegistryEntry<StatusEffect> registryEntry) {
      return Ambience.INSTANCE.isOn() && Ambience.INSTANCE.fullBright.getValue() ? true : instance.hasStatusEffect(registryEntry);
   }

   @Inject(
      method = {"update"},
      at = {@At("HEAD")},
      cancellable = true
   )
   public void updateHook(float delta, CallbackInfo ci) {
      if (Ambience.INSTANCE.isOn() && Ambience.INSTANCE.worldColor.booleanValue) {
         ci.cancel();
         if (this.dirty) {
            this.dirty = false;
            this.client.getProfiler().push("lightTex");
            ClientWorld var3 = this.client.world;
            if (var3 != null) {
               float var4 = var3.getSkyBrightness(1.0F);
               float var5 = var3.getLightningTicksLeft() > 0 ? 1.0F : var4 * 0.95F + 0.05F;
               float var6 = this.client.options.getDarknessEffectScale().getValue().floatValue();
               float var7 = this.getDarknessFactor(delta) * var6;
               float var8 = this.getDarkness(this.client.player, var7, delta) * var6;
               Vector3f var9 = new Vector3f(var4, var4, 1.0F).lerp(new Vector3f(1.0F, 1.0F, 1.0F), 0.35F);
               float var10 = this.flickerIntensity + 1.5F;
               Vector3f var11 = new Vector3f();

               for (int var12 = 0; var12 < 16; var12++) {
                  for (int var13 = 0; var13 < 16; var13++) {
                     float var14 = getBrightness(var3.getDimension(), var12) * var5;
                     float var15 = getBrightness(var3.getDimension(), var13) * var10;
                     float var16 = var15 * ((var15 * 0.6F + 0.4F) * 0.6F + 0.4F);
                     float var17 = var15 * (var15 * var15 * 0.6F + 0.4F);
                     var11.set(var15, var16, var17);
                     boolean var18 = var3.getDimensionEffects().shouldBrightenLighting();
                     if (var18) {
                        var11.lerp(new Vector3f(0.99F, 1.12F, 1.0F), 0.25F);
                        clamp(var11);
                     } else {
                        Vector3f var19 = new Vector3f(var9).mul(var14);
                        var11.add(var19);
                        var11.lerp(new Vector3f(0.75F, 0.75F, 0.75F), 0.04F);
                        if (this.renderer.getSkyDarkness(delta) > 0.0F) {
                           float var20 = this.renderer.getSkyDarkness(delta);
                           Vector3f var21 = new Vector3f(var11).mul(0.7F, 0.6F, 0.6F);
                           var11.lerp(var21, var20);
                        }
                     }

                     float var24 = Math.max(var11.x(), Math.max(var11.y(), var11.z()));
                     if (var24 < 1.0F) {
                        this.image
                           .setColor(
                              var13,
                              var12,
                              new Color(
                                    Ambience.INSTANCE.worldColor.getValue().getBlue(),
                                    Ambience.INSTANCE.worldColor.getValue().getGreen(),
                                    Ambience.INSTANCE.worldColor.getValue().getRed(),
                                    Ambience.INSTANCE.worldColor.getValue().getAlpha()
                                 )
                                 .getRGB()
                           );
                     } else {
                        if (!var18) {
                           if (var8 > 0.0F) {
                              var11.add(-var8, -var8, -var8);
                           }

                           clamp(var11);
                        }

                        var24 = this.client.options.getGamma().getValue().floatValue();
                        Vector3f var26 = new Vector3f(this.easeOutQuart(var11.x), this.easeOutQuart(var11.y), this.easeOutQuart(var11.z));
                        var11.lerp(var26, Math.max(0.0F, var24 - var7));
                        var11.lerp(new Vector3f(0.75F, 0.75F, 0.75F), 0.04F);
                        clamp(var11);
                        var11.mul(255.0F);
                        int var27 = (int)var11.x();
                        int var22 = (int)var11.y();
                        int var23 = (int)var11.z();
                        this.image.setColor(var13, var12, 0xFF000000 | var23 << 16 | var22 << 8 | var27);
                     }
                  }
               }

               this.texture.upload();
               this.client.getProfiler().pop();
            }
         }
      }
   }

   @Inject(
      method = {"getDarknessFactor(F)F"},
      at = {@At("HEAD")},
      cancellable = true
   )
   private void getDarknessFactor(float tickDelta, CallbackInfoReturnable<Float> info) {
      if (NoRender.INSTANCE.isOn() && NoRender.INSTANCE.darkness.getValue()) {
         info.setReturnValue(0.0F);
      }
   }

   @Shadow
   private float easeOutQuart(float x) {
      float var2 = 1.0F - x;
      return 1.0F - var2 * var2 * var2 * var2;
   }

   @Shadow
   private float getDarknessFactor(float delta) {
      StatusEffectInstance var2 = this.client.player.getStatusEffect(StatusEffects.DARKNESS);
      return var2 != null ? var2.getFadeFactor(this.client.player, delta) : 0.0F;
   }

   @Shadow
   private float getDarkness(LivingEntity entity, float factor, float delta) {
      float var4 = 0.45F * factor;
      return Math.max(0.0F, MathHelper.cos(((float)entity.age - delta) * (float) Math.PI * 0.025F) * var4);
   }
}
