package zenith.zov.client.modules.impl.render;

import com.darkmagician6.eventapi.EventTarget;
import com.mojang.blaze3d.systems.RenderSystem;
import java.awt.Color;
import java.util.HashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import net.minecraft.block.AirBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RotationAxis;
import zenith.zov.base.events.impl.player.EventUpdate;
import zenith.zov.base.events.impl.render.EventRender3D;
import zenith.zov.client.modules.api.Category;
import zenith.zov.client.modules.api.Module;
import zenith.zov.client.modules.api.ModuleAnnotation;
import zenith.zov.client.modules.api.setting.impl.BooleanSetting;
import zenith.zov.client.modules.api.setting.impl.ModeSetting;
import zenith.zov.client.modules.api.setting.impl.NumberSetting;
import zenith.zov.utility.interfaces.IMinecraft;

@ModuleAnnotation(
    name = "HitParticles",
    category = Category.RENDER,
    description = "Shows visual particles when entities take damage"
)
public final class HitParticles extends Module implements IMinecraft {
    public static final HitParticles INSTANCE = new HitParticles();
    private final ModeSetting mode = new ModeSetting("Mode", "Stars", "Orbiz", "Stars", "Hearts", "Bloom", "Text");
    private final ModeSetting physics = new ModeSetting("Physics", "Fall", "Fall", "Fly");
    private final NumberSetting colorRed = new NumberSetting("Color Red", 0.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting colorGreen = new NumberSetting("Color Green", 255.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting colorBlue = new NumberSetting("Color Blue", 0.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting colorAlpha = new NumberSetting("Color Alpha", 255.0F, 0.0F, 255.0F, 1.0F);
    private final BooleanSetting onlySelf = new BooleanSetting("Self", false);
    private final NumberSetting amount = new NumberSetting("Amount", 2.0F, 1.0F, 5.0F, 1.0F);
    private final NumberSetting lifeTime = new NumberSetting("LifeTime", 2.0F, 1.0F, 10.0F, 1.0F);
    private final NumberSetting speed = new NumberSetting("Speed", 2.0F, 1.0F, 20.0F, 1.0F);
    private final NumberSetting starsScale = new NumberSetting("Scale", 3.0F, 1.0F, 10.0F, 0.1F);
    private final ModeSetting colorMode = new ModeSetting("ColorMode", "Custom", "Custom", "Sync");
    private final NumberSetting healColorRed = new NumberSetting("Heal Color Red", 48.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting healColorGreen = new NumberSetting("Heal Color Green", 64.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting healColorBlue = new NumberSetting("Heal Color Blue", 16.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting damageColorRed = new NumberSetting("Damage Color Red", 241.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting damageColorGreen = new NumberSetting("Damage Color Green", 98.0F, 0.0F, 255.0F, 1.0F);
    private final NumberSetting damageColorBlue = new NumberSetting("Damage Color Blue", 51.0F, 0.0F, 255.0F, 1.0F);
    private final HashMap<Integer, Float> healthMap = new HashMap<>();
    private final CopyOnWriteArrayList<HitParticles.Particle> particles = new CopyOnWriteArrayList<>();

    private HitParticles() {
    }

    @EventTarget
    public void onUpdate(EventUpdate event) {
        if (mc.player != null && mc.world != null) {
            this.particles.removeIf(HitParticles.Particle::update);
            if (this.mode.get().equals("Text")) {
                for (Entity entity : mc.world.getEntities()) {
                    if (entity != null && !(mc.player.squaredDistanceTo(entity) > 256.0) && entity.isAlive() && entity instanceof LivingEntity) {
                        LivingEntity lent = (LivingEntity)entity;
                        Color c = this.colorMode.get().equals("Sync")
                            ? this.getRandomColor()
                            : new Color(
                                (int)this.colorRed.getCurrent(),
                                (int)this.colorGreen.getCurrent(),
                                (int)this.colorBlue.getCurrent(),
                                (int)this.colorAlpha.getCurrent()
                            );
                        float health = lent.getHealth() + lent.getAbsorptionAmount();
                        float lastHealth = this.healthMap.getOrDefault(entity.getId(), health);
                        this.healthMap.put(entity.getId(), health);
                        if (lastHealth != health) {
                            this.particles
                                .add(
                                    new HitParticles.Particle(
                                        (float)lent.getX(),
                                        this.random((float)(lent.getY() + (double)lent.getHeight()), (float)lent.getY()),
                                        (float)lent.getZ(),
                                        c,
                                        (float)this.random(0, 180),
                                        this.random(10.0F, 60.0F),
                                        health - lastHealth
                                    )
                                );
                        }
                    }
                }
            } else {
                for (PlayerEntity player : mc.world.getPlayers()) {
                    if ((!this.onlySelf.isEnabled() || player == mc.player) && player.hurtTime > 0) {
                        Color c = this.colorMode.get().equals("Sync")
                            ? this.getRandomColor()
                            : new Color(
                                (int)this.colorRed.getCurrent(),
                                (int)this.colorGreen.getCurrent(),
                                (int)this.colorBlue.getCurrent(),
                                (int)this.colorAlpha.getCurrent()
                            );

                        for (int i = 0; i < (int)this.amount.getCurrent(); i++) {
                            this.particles
                                .add(
                                    new HitParticles.Particle(
                                        (float)player.getX(),
                                        this.random((float)(player.getY() + (double)player.getHeight()), (float)player.getY()),
                                        (float)player.getZ(),
                                        c,
                                        (float)this.random(0, 180),
                                        this.random(10.0F, 60.0F),
                                        0.0F
                                    )
                                );
                        }
                    }
                }
            }
        }
    }

    @EventTarget
    public void onRender3D(EventRender3D event) {
        if (mc.player != null && mc.world != null) {
            RenderSystem.disableDepthTest();

            for (HitParticles.Particle particle : this.particles) {
                particle.render(event.getMatrix(), event.getPartialTicks());
            }

            RenderSystem.enableDepthTest();
        }
    }

    private Color getRandomColor() {
        return new Color((int)(Math.random() * 255.0), (int)(Math.random() * 255.0), (int)(Math.random() * 255.0), 255);
    }

    private float random(float min, float max) {
        return (float)(Math.random() * (double)(max - min) + (double)min);
    }

    private int random(int min, int max) {
        return (int)(Math.random() * (double)(max - min) + (double)min);
    }

    private void drawOrbiz(MatrixStack matrixStack, float offset, double alpha, Color color) {
    }

    private void drawStar(MatrixStack matrixStack, Color color, float size) {
    }

    private void drawHeart(MatrixStack matrixStack, Color color, float size) {
    }

    private void drawBloom(MatrixStack matrixStack, Color color, float size) {
    }

    public class Particle {
        float x;
        float y;
        float z;
        float px;
        float py;
        float pz;
        float motionX;
        float motionY;
        float motionZ;
        float rotationAngle;
        float rotationSpeed;
        float health;
        long time;
        Color color;

        public Particle(float x, float y, float z, Color color, float rotationAngle, float rotationSpeed, float health) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.px = x;
            this.py = y;
            this.pz = z;
            this.motionX = HitParticles.this.random(-HitParticles.this.speed.getCurrent() / 50.0F, HitParticles.this.speed.getCurrent() / 50.0F);
            this.motionY = HitParticles.this.random(-HitParticles.this.speed.getCurrent() / 50.0F, HitParticles.this.speed.getCurrent() / 50.0F);
            this.motionZ = HitParticles.this.random(-HitParticles.this.speed.getCurrent() / 50.0F, HitParticles.this.speed.getCurrent() / 50.0F);
            this.time = System.currentTimeMillis();
            this.color = color;
            this.rotationAngle = rotationAngle;
            this.rotationSpeed = rotationSpeed;
            this.health = health;
        }

        public long getTime() {
            return this.time;
        }

        public boolean update() {
            double sp = Math.sqrt((double)(this.motionX * this.motionX + this.motionZ * this.motionZ));
            this.px = this.x;
            this.py = this.y;
            this.pz = this.z;
            this.x = this.x + this.motionX;
            this.y = this.y + this.motionY;
            this.z = this.z + this.motionZ;
            if (this.posBlock((double)this.x, (double)(this.y - HitParticles.this.starsScale.getCurrent() / 10.0F), (double)this.z)) {
                this.motionY = -this.motionY / 1.1F;
                this.motionX /= 1.1F;
                this.motionZ /= 1.1F;
            } else if (this.posBlock((double)this.x - sp, (double)this.y, (double)this.z - sp)
                || this.posBlock((double)this.x + sp, (double)this.y, (double)this.z + sp)
                || this.posBlock((double)this.x + sp, (double)this.y, (double)this.z - sp)
                || this.posBlock((double)this.x - sp, (double)this.y, (double)this.z + sp)
                || this.posBlock((double)this.x + sp, (double)this.y, (double)this.z)
                || this.posBlock((double)this.x - sp, (double)this.y, (double)this.z)
                || this.posBlock((double)this.x, (double)this.y, (double)this.z + sp)
                || this.posBlock((double)this.x, (double)this.y, (double)this.z - sp)) {
                this.motionX = -this.motionX;
                this.motionZ = -this.motionZ;
            }

            if (HitParticles.this.physics.get().equals("Fall")) {
                this.motionY -= 0.035F;
            }

            this.motionX /= 1.005F;
            this.motionZ /= 1.005F;
            this.motionY /= 1.005F;
            return System.currentTimeMillis() - this.getTime() > (long)HitParticles.this.lifeTime.getCurrent() * 1000L;
        }

        public void render(MatrixStack matrixStack, float partialTicks) {
            float size = HitParticles.this.starsScale.getCurrent();
            float scale = HitParticles.this.mode.get().equals("Text") ? 0.025F * size : 0.07F;
            double posX = (double)this.interpolate(this.px, this.x, partialTicks) - IMinecraft.mc.getEntityRenderDispatcher().camera.getPos().getX();
            double posY = (double)this.interpolate(this.py, this.y, partialTicks) + 0.1 - IMinecraft.mc.getEntityRenderDispatcher().camera.getPos().getY();
            double posZ = (double)this.interpolate(this.pz, this.z, partialTicks) - IMinecraft.mc.getEntityRenderDispatcher().camera.getPos().getZ();
            matrixStack.push();
            matrixStack.translate(posX, posY, posZ);
            matrixStack.scale(scale, scale, scale);
            matrixStack.translate(size / 2.0F, size / 2.0F, size / 2.0F);
            matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-IMinecraft.mc.gameRenderer.getCamera().getYaw()));
            matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(IMinecraft.mc.gameRenderer.getCamera().getPitch()));
            if (HitParticles.this.mode.get().equals("Text")) {
                matrixStack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180.0F));
            } else {
                matrixStack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(this.rotationAngle = this.rotationAngle + partialTicks * this.rotationSpeed));
            }

            matrixStack.translate(-size / 2.0F, -size / 2.0F, -size / 2.0F);
            String var11 = HitParticles.this.mode.get();
            switch (var11) {
                case "Orbiz":
                    HitParticles.this.drawOrbiz(matrixStack, 0.0F, 0.3, this.color);
                    HitParticles.this.drawOrbiz(matrixStack, -0.1F, 0.5, this.color);
                    HitParticles.this.drawOrbiz(matrixStack, -0.2F, 0.7, this.color);
                    break;
                case "Stars":
                    HitParticles.this.drawStar(matrixStack, this.color, size);
                    break;
                case "Hearts":
                    HitParticles.this.drawHeart(matrixStack, this.color, size);
                    break;
                case "Bloom":
                    HitParticles.this.drawBloom(matrixStack, this.color, size);
                    break;
                case "Text":
                    if (this.health > 0.0F) {
                        new Color(
                            (int)HitParticles.this.healColorRed.getCurrent(),
                            (int)HitParticles.this.healColorGreen.getCurrent(),
                            (int)HitParticles.this.healColorBlue.getCurrent()
                        );
                    } else {
                        new Color(
                            (int)HitParticles.this.damageColorRed.getCurrent(),
                            (int)HitParticles.this.damageColorGreen.getCurrent(),
                            (int)HitParticles.this.damageColorBlue.getCurrent()
                        );
                    }
            }

            matrixStack.scale(0.8F, 0.8F, 0.8F);
            matrixStack.pop();
        }

        private boolean posBlock(double x, double y, double z) {
            Block b = IMinecraft.mc.world.getBlockState(BlockPos.ofFloored(x, y, z)).getBlock();
            return !(b instanceof AirBlock) && b != Blocks.WATER && b != Blocks.LAVA;
        }

        private float interpolate(float prev, float current, float delta) {
            return prev + (current - prev) * delta;
        }
    }
}
