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

import com.darkmagician6.eventapi.EventTarget;
import com.google.gson.JsonObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.gui.screen.ChatScreen;
import net.minecraft.client.util.math.Vector2f;
import zenith.zov.Zenith;
import zenith.zov.base.events.impl.input.EventMouse;
import zenith.zov.base.events.impl.input.EventSetScreen;
import zenith.zov.base.events.impl.other.EventWindowResize;
import zenith.zov.base.events.impl.player.EventUpdate;
import zenith.zov.base.events.impl.render.EventRender2D;
import zenith.zov.client.hud.elements.component.ChunkFinderComponent;
import zenith.zov.client.hud.elements.component.CooldownComponent;
import zenith.zov.client.hud.elements.component.HootBarComponent;
import zenith.zov.client.hud.elements.component.InformationComponent;
import zenith.zov.client.hud.elements.component.InventoryComponent;
import zenith.zov.client.hud.elements.component.KeybindsComponent;
import zenith.zov.client.hud.elements.component.MusicInfoComponent;
import zenith.zov.client.hud.elements.component.NotifyComponent;
import zenith.zov.client.hud.elements.component.PlayerListComponent;
import zenith.zov.client.hud.elements.component.PotionsComponent;
import zenith.zov.client.hud.elements.component.RegionMapComponent;
import zenith.zov.client.hud.elements.component.ScoreBoardComponent;
import zenith.zov.client.hud.elements.component.StaffComponent;
import zenith.zov.client.hud.elements.component.TargetHudComponent;
import zenith.zov.client.hud.elements.component.WatermarkComponent;
import zenith.zov.client.hud.elements.draggable.DraggableHudElement;
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.MultiBooleanSetting;
import zenith.zov.client.modules.api.setting.impl.NumberSetting;
import zenith.zov.utility.math.MathUtil;
import zenith.zov.utility.render.display.Render2DUtil;
import zenith.zov.utility.render.display.base.CustomDrawContext;
import zenith.zov.utility.render.display.base.GuiUtil;

@ModuleAnnotation(
    name = "Interface",
    category = Category.RENDER,
    description = "Client Interface"
)
public final class Interface extends Module {
    public static final Interface INSTANCE = new Interface();
    private final MultiBooleanSetting elementsSetting = MultiBooleanSetting.create(
        "Elements",
        List.of(
            "Watermark",
            "Effects",
            "Staff",
            "Notifications",
            "Inventory",
            "Cooldowns",
            "Information",
            "Binds",
            "Target HUD",
            "Music",
            "Hotbar",
            "ChunkFinder",
            "Scoreboard",
            "Tab",
            "Region Map"
        )
    );
    private final List<DraggableHudElement> elements = new ArrayList<>();
    private DraggableHudElement draggingElement = null;
    private float dragOffsetX;
    private float dragOffsetY;
    private final NumberSetting scale = new NumberSetting("Size", 2.0F, 1.0F, 3.0F, 0.1F, (oldValue, newValue) -> {
        float width = (float)mc.getWindow().getWidth() / newValue;
        float height = (float)mc.getWindow().getHeight() / newValue;

        for (DraggableHudElement element : this.elements) {
            element.windowResized(width, height);
        }
    });
    private BooleanSetting corners = new BooleanSetting("Triangles", true);
    private BooleanSetting blur = new BooleanSetting("Blur", false);
    private BooleanSetting glow = new BooleanSetting("Glow", false);
    long init = 0L;

    private Interface() {
        this.addElement(new WatermarkComponent("Watermark", 0.0F, 0.0F, 960.0F, 495.5F, 10.0F, 10.0F, DraggableHudElement.Align.TOP_LEFT));
        this.addElement(new PotionsComponent("Potions", 0.0F, 0.0F, 960.0F, 495.5F, 119.15234F, 73.0F, DraggableHudElement.Align.TOP_LEFT));
        this.addElement(new StaffComponent("Staff", 0.0F, 0.0F, 960.0F, 495.5F, 10.0F, 73.0F, DraggableHudElement.Align.TOP_LEFT));
        NotifyComponent notifyComponent = new NotifyComponent(
            "Notify", 181.80615F, 135.5F, 960.0F, 495.5F, 157.03516F, -72.5F, DraggableHudElement.Align.CENTER
        );
        this.addElement(notifyComponent);
        Zenith.getInstance().getNotifyManager().setNotifyComponent(notifyComponent);
        this.addElement(new InventoryComponent("Inventory", 269.0F, 229.0F, 960.0F, 495.5F, -11.5F, -74.0F, DraggableHudElement.Align.BOTTOM_RIGHT));
        this.addElement(new CooldownComponent("Cooldown", 349.0F, 0.0F, 960.0F, 495.5F, -11.5F, 73.0F, DraggableHudElement.Align.TOP_RIGHT));
        this.addElement(new InformationComponent("Information", 0.0F, 0.0F, 960.0F, 495.5F, 10.0F, 41.5F, DraggableHudElement.Align.TOP_LEFT));
        this.addElement(new KeybindsComponent("Keybinds", 349.0F, 0.0F, 960.0F, 495.5F, -122.0F, 73.0F, DraggableHudElement.Align.TOP_RIGHT));
        this.addElement(new TargetHudComponent("TargetHUD", 166.5F, 128.5F, 960.0F, 495.5F, 0.0F, 31.75F, DraggableHudElement.Align.CENTER));

        try {
            this.addElement(new MusicInfoComponent("MusicInfo", 342.0F, 257.0F, 960.0F, 495.5F, -11.5F, -16.5F, DraggableHudElement.Align.BOTTOM_RIGHT));
        } catch (NoClassDefFoundError var3) {
            System.err.println("MediaInfo dependency not available. Music info component will be skipped.");
        } catch (Exception var4) {
            System.err.println("Failed to initialize MusicInfo component: " + var4.getMessage());
        }

        this.addElement(new HootBarComponent("Hotbar", 116.5F, 265.0F, 960.0F, 495.5F, 0.0F, -16.5F, DraggableHudElement.Align.BOTTOM_CENTER));
        this.addElement(new ChunkFinderComponent("ChunkFinder", 0.0F, 0.0F, 960.0F, 495.5F, 10.0F, 100.0F, DraggableHudElement.Align.TOP_LEFT));
        this.addElement(new ScoreBoardComponent("Scoreboard", 0.0F, 0.0F, 960.0F, 495.5F, -10.0F, 10.0F, DraggableHudElement.Align.CENTER_RIGHT));
        this.addElement(new PlayerListComponent("Tab"));
        this.addElement(new RegionMapComponent("Region Map", 0.0F, 0.0F, 960.0F, 495.5F, 10.0F, 150.0F, DraggableHudElement.Align.TOP_LEFT));
        this.setEnabled(false);

        for (int i = 0; i < this.elementsSetting.getBooleanSettings().size(); i++) {
            this.elementsSetting.getBooleanSettings().get(i).setEnabled(true);
        }
    }

    @Override
    public void onEnable() {
        this.init = System.currentTimeMillis();
        super.onEnable();
    }

    @Override
    public JsonObject save() {
        JsonObject object = super.save();
        JsonObject propertiesObject = new JsonObject();

        for (DraggableHudElement element : this.elements) {
            propertiesObject.add(element.getName(), element.save());
        }

        object.add("HudElements", propertiesObject);
        return object;
    }

    @Override
    public void load(JsonObject object) {
        super.load(object);
        if (object.has("HudElements") && object.get("HudElements").isJsonObject()) {
            JsonObject propertiesObject = object.getAsJsonObject("HudElements");

            for (DraggableHudElement element : this.elements) {
                String key = element.getName();
                if (propertiesObject.has(key) && propertiesObject.get(key).isJsonObject()) {
                    element.load(propertiesObject.getAsJsonObject(key));
                }
            }
        }
    }

    private void addElement(DraggableHudElement element) {
        this.elements.add(element);
    }

    public List<DraggableHudElement> getElements() {
        return this.elements;
    }

    public boolean shouldRender(DraggableHudElement element) {
        int index = this.elements.indexOf(element);
        return index >= 0 && index < this.elementsSetting.getBooleanSettings().size()
            ? this.elementsSetting.getBooleanSettings().get(index).isEnabled()
            : false;
    }

    @EventTarget
    public void onRender(EventRender2D event) {
        if (this.isEnabled()) {
            CustomDrawContext ctx = event.getContext();
            float width = (float)mc.getWindow().getWidth() / this.getCustomScale();
            float height = (float)mc.getWindow().getHeight() / this.getCustomScale();

            for (DraggableHudElement element : this.elements) {
                if (this.shouldRender(element)) {
                    try {
                        element.render(ctx);
                    } catch (Exception var10) {
                        var10.printStackTrace();
                    }

                    if (this.draggingElement != element && System.currentTimeMillis() - this.init < 5000L) {
                        element.windowResized(width, height);
                    }
                }
            }

            if (mc.currentScreen instanceof ChatScreen && this.draggingElement != null) {
                Vector2f mousePos = GuiUtil.getMouse((double)this.getCustomScale());
                double mouseX = (double)mousePos.getX();
                double mouseY = (double)mousePos.getY();
                this.draggingElement.set(ctx, (float)mouseX - this.dragOffsetX, (float)mouseY - this.dragOffsetY, this, width, height);
            }
        }
    }

    @EventTarget
    public void onMouse(EventMouse event) {
        if (!(mc.currentScreen instanceof ChatScreen)) {
            this.draggingElement.release();
            this.draggingElement = null;
        } else {
            Vector2f mousePos = GuiUtil.getMouse((double)this.getCustomScale());
            double mouseX = (double)mousePos.getX();
            double mouseY = (double)mousePos.getY();
            if (event.getAction() == 1 && event.getButton() == 0) {
                List<DraggableHudElement> reversedElements = new ArrayList<>(this.elements);
                Collections.reverse(reversedElements);

                for (DraggableHudElement element : reversedElements) {
                    if (this.shouldRender(element) && element.isMouseOver(mouseX, mouseY)) {
                        this.draggingElement = element;
                        this.dragOffsetX = (float)mouseX - element.getX();
                        this.dragOffsetY = (float)mouseY - element.getY();
                        break;
                    }
                }
            } else if (event.getAction() == 0) {
                this.draggingElement.release();
                this.draggingElement = null;
            }
        }
    }

    public float getCustomScale() {
        return this.scale.getCurrent();
    }

    public org.joml.Vector2f getNearest(float x, float y) {
        float minDeltaX = Float.MAX_VALUE;
        float minDeltaY = Float.MAX_VALUE;
        float thoroughness = 2.0F;
        org.joml.Vector2f nearest = new org.joml.Vector2f(-1.0F, -1.0F);

        for (DraggableHudElement s : this.elements) {
            if (!s.equals(this.draggingElement)) {
                float tempXA = s.getX();
                float tempYA = s.getY();
                float tempXB = s.getX() + s.getWidth();
                float tempYB = s.getY() + s.getHeight();
                float tempXC = s.getX() + s.getWidth() / 2.0F;
                float tempYC = s.getY() + s.getHeight() / 2.0F;
                float minX = this.getNearest(tempXA, tempXB, tempXC, x);
                float minY = this.getNearest(tempYA, tempYB, tempYC, y);
                float deltaX = MathUtil.goodSubtract(minX, x);
                float deltaY = MathUtil.goodSubtract(minY, y);
                if (deltaX < minDeltaX) {
                    minDeltaX = deltaX;
                    if (deltaX < thoroughness) {
                        nearest.x = minX;
                    }
                }

                if (deltaY < minDeltaY) {
                    minDeltaY = deltaY;
                    if (deltaY < thoroughness) {
                        nearest.y = minY;
                    }
                }
            }
        }

        if (nearest.x == -1.0F || nearest.y == -1.0F) {
            float tempXAx = (float)mc.getWindow().getScaledWidth() / 2.0F;
            float tempYAx = (float)mc.getWindow().getScaledHeight() / 2.0F;
            float minXx = this.getNearest(tempXAx, tempXAx, tempXAx, x);
            float minYx = this.getNearest(tempYAx, tempYAx, tempYAx, y);
            float deltaXx = MathUtil.goodSubtract(minXx, x);
            float deltaYx = MathUtil.goodSubtract(minYx, y);
            if (deltaXx < minDeltaX && deltaXx < thoroughness) {
                nearest.x = minXx;
            }

            if (deltaYx < minDeltaY && deltaYx < thoroughness) {
                nearest.y = minYx;
            }
        }

        return nearest;
    }

    public float getNearest(float a, float b, float c, float target) {
        float nearest = a;
        if (MathUtil.goodSubtract(b, target) < MathUtil.goodSubtract(a, target)) {
            nearest = b;
        }

        if (MathUtil.goodSubtract(c, target) < MathUtil.goodSubtract(nearest, target)) {
            nearest = c;
        }

        return nearest;
    }

    public boolean isEnableScoreBar() {
        return this.elementsSetting.isEnable(12);
    }

    public boolean isEnableHotBar() {
        return this.elementsSetting.isEnable(10);
    }

    public boolean isEnableTab() {
        return this.elementsSetting.isEnable(13);
    }

    @EventTarget
    public void resize(EventWindowResize eventWindowResize) {
        float width = (float)mc.getWindow().getWidth() / this.getCustomScale();
        float height = (float)mc.getWindow().getHeight() / this.getCustomScale();

        for (DraggableHudElement element : this.elements) {
            element.windowResized(width, height);
        }
    }

    @EventTarget
    public void update(EventUpdate eventUpdate) {
        if (Render2DUtil.glowCache.size() > 400) {
            Render2DUtil.glowCache.values().removeIf(v -> {
                if (v.tick()) {
                    v.destroy();
                    return true;
                } else {
                    return false;
                }
            });
        }

        for (DraggableHudElement draggableHudElement : this.elements) {
            draggableHudElement.tick();
        }
    }

    public boolean isBlur() {
        return this.blur.isEnabled();
    }

    public boolean isGlow() {
        return this.glow.isEnabled();
    }

    public boolean isCorners() {
        return this.corners.isEnabled();
    }

    @EventTarget
    public void screenEvent(EventSetScreen event) {
        if (event.getScreen() instanceof ChatScreen) {
            this.init = System.currentTimeMillis();
        }
    }

    public int getGlowRadius() {
        return 10;
    }
}
