package dev.crucifixbeta.mod.modules.impl.render;

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.PreRender2DEvent;
import dev.crucifixbeta.api.events.impl.TotemParticleEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.render.ColorUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.ColorSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import java.awt.Color;
import java.util.Random;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.render.Camera;
import net.minecraft.network.packet.s2c.play.WorldTimeUpdateS2CPacket;
import net.minecraft.world.biome.Biome.Precipitation;
import org.joml.Matrix4f;

@Environment(EnvType.CLIENT)
public class Ambience extends Module {
   public static Ambience INSTANCE;
   public final ColorSetting filter = this.add(new ColorSetting("Filter", new Color(255, 255, 255, 20)).injectBoolean(false));
   public final ColorSetting worldColor = this.add(new ColorSetting("WorldColor", new Color(-1, true)).injectBoolean(true));
   public final BooleanSetting customTime = this.add(new BooleanSetting("CustomTime", false).setParent());
   public final SliderSetting time = this.add(new SliderSetting("Time", 0, 0, 24000, this.customTime::isOpen));
   public final BooleanSetting worldWeather = this.add(new BooleanSetting("WorldWeather", false).setParent());
   public final EnumSetting<Ambience.WeatherMode> weather = this.add(
      new EnumSetting<>("Weather", Ambience.WeatherMode.Clear, this.worldWeather::isOpen)
   );
   public final BooleanSetting forceWeather = this.add(new BooleanSetting("Force", false, this.worldWeather::isOpen));
   public final SliderSetting weatherAmount = this.add(
      new SliderSetting("Amount", 1.0, 0.0, 1.0, 0.01, () -> this.worldWeather.isOpen() && this.forceWeather.getValue())
   );
   public final ColorSetting fog = this.add(new ColorSetting("FogColor", new Color(13401557)).injectBoolean(false));
   public final ColorSetting sky = this.add(new ColorSetting("SkyColor", new Color(0)).injectBoolean(false));
   public final ColorSetting cloud = this.add(new ColorSetting("CloudColor", new Color(0)).injectBoolean(false));
   public final ColorSetting dimensionColor = this.add(new ColorSetting("DimensionColor", new Color(0)).injectBoolean(false));
   public final BooleanSetting fogDistance = this.add(new BooleanSetting("FogDistance", false).setParent());
   public final SliderSetting fogStart = this.add(new SliderSetting("FogStart", 50, 0, 1000, this.fogDistance::isOpen));
   public final SliderSetting fogEnd = this.add(new SliderSetting("FogEnd", 100, 0, 1000, this.fogDistance::isOpen));
   public final BooleanSetting fullBright = this.add(new BooleanSetting("FullBright", false));
   public final BooleanSetting forceOverworld = this.add(new BooleanSetting("ForceOverworld", false));
   public final BooleanSetting customLuminance = this.add(new BooleanSetting("CustomLuminance", false).setParent().injectTask(() -> {
      if (!nullCheck()) {
         mc.worldRenderer.reload();
      }
   }));
   public final SliderSetting luminance = this.add(new SliderSetting("Luminance", 15, 0, 15, this.customLuminance::isOpen).injectTask(() -> {
      if (!nullCheck() && this.customLuminance.getValue()) {
         mc.worldRenderer.reload();
      }
   }));
   public final BooleanSetting skyBox = this.add(new BooleanSetting("SkyBox", false).setParent());
   private final BooleanSetting skyBoxReplaceVanilla = this.add(new BooleanSetting("SkyBoxReplaceVanilla", true, this.skyBox::isOpen));
   public final EnumSetting<Ambience.SkyShader> skyBoxMode = this.add(
      new EnumSetting<>("SkyBoxMode", Ambience.SkyShader.CosmicHorizon, this.skyBox::isOpen)
   );
   public final SliderSetting skyBoxSpeed = this.add(
      new SliderSetting("SkyBoxSpeed", 4.0, 0.0, 20.0, 0.1, () -> this.skyBox.isOpen() && !this.skyBoxMode.is(Ambience.SkyShader.SolidColor))
   );
   public final ColorSetting skyBoxColor = this.add(new ColorSetting("SkyBoxColor", new Color(255, 255, 255, 255), this.skyBox::isOpen));
   public final ColorSetting skyBoxSecondColor = this.add(
      new ColorSetting(
         "SkyBoxSecondColor",
         new Color(255, 255, 255, 255),
         () -> this.skyBox.isOpen() && (this.skyBoxMode.is(Ambience.SkyShader.GradientFade) || this.skyBoxMode.is(Ambience.SkyShader.MonolithFlow))
      )
   );
   public final SliderSetting skyBoxStep = this.add(
      new SliderSetting("SkyBoxStep", 0.5, 0.0, 4.0, 0.01, () -> this.skyBox.isOpen() && this.skyBoxMode.is(Ambience.SkyShader.GradientFade))
   );
   public final SliderSetting skyBoxResolutionScale = this.add(
      new SliderSetting("ResolutionScale", 0.75, 0.25, 1.0, 0.05, () -> this.skyBox.isOpen() && !this.skyBoxMode.is(Ambience.SkyShader.SolidColor))
   );
   public final SliderSetting skyBoxShaderScale = this.add(
      new SliderSetting("ShaderScale", 1.0, 0.1, 3.0, 0.01, () -> this.skyBox.isOpen() && this.skyBoxMode.is(Ambience.SkyShader.MonolithFlow))
   );
   public final SliderSetting skyBoxShaderIntensity = this.add(
      new SliderSetting(
         "ShaderIntensity", 0.005, 0.001, 0.03, 5.0E-4, () -> this.skyBox.isOpen() && this.skyBoxMode.is(Ambience.SkyShader.MonolithFlow)
      )
   );
   public final BooleanSetting skyBoxMouseFollow = this.add(
      new BooleanSetting("MouseFollow", false, () -> this.skyBox.isOpen() && this.skyBoxMode.is(Ambience.SkyShader.MonolithFlow))
   );
   public final BooleanSetting totemParticle = this.add(new BooleanSetting("TotemParticle", false).setParent());
   public final SliderSetting totemParticleVelocityXZ = this.add(
      new SliderSetting("VelocityXZ", 100.0, 0.0, 500.0, 1.0, this.totemParticle::isOpen).setSuffix("%")
   );
   public final SliderSetting totemParticleVelocityY = this.add(
      new SliderSetting("VelocityY", 100.0, 0.0, 500.0, 1.0, this.totemParticle::isOpen).setSuffix("%")
   );
   private final ColorSetting totemParticleColor = this.add(
      new ColorSetting("ParticleColor", new Color(255, 255, 255, 255), this.totemParticle::isOpen)
   );
   private final ColorSetting totemParticleSecondColor = this.add(
      new ColorSetting("ParticleColor2", new Color(0, 0, 0, 255), this.totemParticle::isOpen)
   );
   long oldTime;
   private final Random totemParticleRandom = new Random();

   public Ambience() {
      super("Ambience", "Custom ambience", Module.Category.Render);
      this.setChinese("\u81ea\u5b9a\u4e49\u73af\u5883");
      INSTANCE = this;
   }

   @EventListener
   public void onRender2D(PreRender2DEvent event) {
      if (this.filter.booleanValue) {
         event.drawContext.fill(0, 0, mc.getWindow().getScaledWidth(), mc.getWindow().getScaledHeight(), this.filter.getValue().getRGB());
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (this.customTime.getValue()) {
         mc.world.setTimeOfDay((long)this.time.getValue());
      }
   }

   @Override
   public void onEnable() {
      if (!nullCheck()) {
         this.oldTime = mc.world.getTimeOfDay();
         if (this.customTime.getValue()) {
            mc.world.setTimeOfDay((long)this.time.getValue());
         }
      }
   }

   @Override
   public void onDisable() {
      if (!nullCheck()) {
         mc.world.setTimeOfDay(this.oldTime);
         this.reloadLocalShader();
      }
   }

   @Override
   public void onToggle() {
      this.reloadLocalShader();
      CrucifixBeta.SHADER.reloadShaders();
   }

   @Override
   public void onLogin() {
      this.reloadLocalShader();
      CrucifixBeta.SHADER.reloadShaders();
   }

   @EventListener
   public void onReceivePacket(PacketEvent.Receive event) {
      if (event.getPacket() instanceof WorldTimeUpdateS2CPacket && this.customTime.getValue()) {
         this.oldTime = ((WorldTimeUpdateS2CPacket)event.getPacket()).getTime();
         event.cancel();
      }
   }

   @EventListener
   public void onTotemParticle(TotemParticleEvent event) {
      if (this.totemParticle.getValue()) {
         event.cancel();
         event.velocityZ = event.velocityZ * (this.totemParticleVelocityXZ.getValue() / 100.0);
         event.velocityX = event.velocityX * (this.totemParticleVelocityXZ.getValue() / 100.0);
         event.velocityY = event.velocityY * (this.totemParticleVelocityY.getValue() / 100.0);
         event.color = ColorUtil.fadeColor(this.totemParticleColor.getValue(), this.totemParticleSecondColor.getValue(), this.totemParticleRandom.nextDouble());
      }
   }

   public boolean shouldReplaceVanillaSkyBox() {
      return this.isOn() && this.skyBox.getValue() && this.skyBoxReplaceVanilla.getValue();
   }

   public boolean shouldRenderSkyBox() {
      return this.isOn() && this.skyBox.getValue();
   }

   public boolean shouldControlWeather() {
      return this.isOn() && this.worldWeather.getValue();
   }

   public float getWeatherGradient() {
      if (!this.shouldControlWeather()) {
         return -1.0F;
      } else if (this.weather.is(Ambience.WeatherMode.Clear)) {
         return 0.0F;
      } else {
         return this.forceWeather.getValue() ? this.weatherAmount.getValueFloat() : 1.0F;
      }
   }

   public Precipitation getPrecipitation() {
      return switch ((Ambience.WeatherMode)this.weather.getValue()) {
         case Clear -> Precipitation.NONE;
         case Rain -> Precipitation.RAIN;
         case Snow -> Precipitation.SNOW;
      };
   }

   public void renderSkybox(Matrix4f positionMatrix, float tickDelta) {
      this.renderSkybox(positionMatrix, null, tickDelta);
   }

   public void renderSkybox(Matrix4f positionMatrix, Camera camera, float tickDelta) {
      if (this.shouldRenderSkyBox() && CrucifixBeta.SHADER != null) {
         CrucifixBeta.SHADER.applySky(this);
      }
   }

   private void reloadLocalShader() {
   }
@Environment(EnvType.CLIENT)
   public static enum SkyShader {
      SolidColor,
      GradientFade,
      GlowingFlow,
      RainbowAura,
      CosmicHorizon,
      MonolithFlow;

}

   @Environment(EnvType.CLIENT)
   public static enum WeatherMode {
      Clear,
      Rain,
      Snow;

}
}
