package de.winniepat.citrus.draggui;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.*;
import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
import java.awt.Color;
import java.util.Collection;

public class HudEditorScreen extends Screen {
    private enum SelectedModule {
        NONE,
        FPS,
        PING,
        SERVER,
        KEYS,
        ARMOR,
        POTIONS
    }

    private SelectedModule dragging = SelectedModule.NONE;
    private SelectedModule pickingColorFor = SelectedModule.NONE;

    private int dragOffsetX, dragOffsetY;
    private TextFieldWidget hexInput, rInput, gInput, bInput;
    private ButtonWidget resetButton;

    private float currentHue = 0f, currentSat = 0f, currentBri = 0f;
    private boolean isDraggingHue = false, isDraggingSB = false;
    private boolean ignoreUpdates = false;

    private final int windowW = 185, windowH = 185;
    private final int boxW = 120, boxH = 100, sliderW = 12;

    public HudEditorScreen() { super(Text.literal("HUD Editor")); }

    @Override
    protected void init() {
        int cx = this.width / 2;
        int cy = this.height / 2;

        hexInput = new TextFieldWidget(textRenderer, cx - 80, cy + 55, 55, 16, Text.literal("Hex"));
        hexInput.setMaxLength(7);
        hexInput.setChangedListener(this::onHexChanged);

        rInput = createRGBField(cx - 20, cy + 55, "R");
        gInput = createRGBField(cx + 15, cy + 55, "G");
        bInput = createRGBField(cx + 50, cy + 55, "B");

        resetButton = ButtonWidget.builder(Text.literal("Reset Default"), button -> {
            updateAll(0xFFFFFFFF, true);
        }).dimensions(cx - 80, cy + 78, 160, 20).build();

        this.addDrawableChild(hexInput);
        this.addDrawableChild(rInput);
        this.addDrawableChild(gInput);
        this.addDrawableChild(bInput);
        this.addDrawableChild(resetButton);
    }

    private TextFieldWidget createRGBField(int x, int y, String label) {
        TextFieldWidget tf = new TextFieldWidget(textRenderer, x, y, 30, 16, Text.literal(label));
        tf.setMaxLength(3);
        tf.setChangedListener(s -> onRGBChanged());
        return tf;
    }

    @Override
    public void render(DrawContext context, int mouseX, int mouseY, float delta) {
        this.renderBackground(context, mouseX, mouseY, delta);
        HudConfig c = HudConfig.INSTANCE;
        MinecraftClient client = MinecraftClient.getInstance();

        String fpsStr = client.getCurrentFps() + " FPS";
        renderModule(context, c.fpsX, c.fpsY, textRenderer.getWidth(fpsStr), 10, fpsStr, c.fpsColor, dragging == SelectedModule.FPS);

        int ping = 0;
        if (client.getNetworkHandler() != null && client.player != null) {
            PlayerListEntry entry = client.getNetworkHandler().getPlayerListEntry(client.player.getUuid());
            if (entry != null) ping = entry.getLatency();
        }
        String pingStr = ping + "ms";
        renderModule(context, c.pingX, c.pingY, Math.max(textRenderer.getWidth(pingStr), 30), 10, pingStr, c.pingColor, dragging == SelectedModule.PING);

        String srv = client.getCurrentServerEntry() != null ? client.getCurrentServerEntry().address : "Singleplayer";
        renderModule(context, c.serverX, c.serverY, textRenderer.getWidth(srv), 10, srv, c.serverColor, dragging == SelectedModule.SERVER);

        drawLiveKeys(context, c.keyX, c.keyY, c.keyColor, dragging == SelectedModule.KEYS);

        drawLiveArmor(context, c.armorX, c.armorY, dragging == SelectedModule.ARMOR);

        drawLivePotions(context, c.potionX, c.potionY, c.potionColor, dragging == SelectedModule.POTIONS);

        if (pickingColorFor != SelectedModule.NONE) {
            renderColorPicker(context, mouseX, mouseY, delta);
        }
    }

    private void drawLiveKeys(DrawContext ctx, int x, int y, int color, boolean drag) {
        MinecraftClient client = MinecraftClient.getInstance();
        if (drag) ctx.fill(x - 2, y - 2, x + 66, y + 88, 0x4400FF00);

        drawKey(ctx, x + 22, y, 20, 20, "W", client.options.forwardKey.isPressed(), color);
        drawKey(ctx, x, y + 22, 20, 20, "A", client.options.leftKey.isPressed(), color);
        drawKey(ctx, x + 22, y + 22, 20, 20, "S", client.options.backKey.isPressed(), color);
        drawKey(ctx, x + 44, y + 22, 20, 20, "D", client.options.rightKey.isPressed(), color);
        drawKey(ctx, x, y + 44, 31, 20, "LMB", client.options.attackKey.isPressed(), color);
        drawKey(ctx, x + 33, y + 44, 31, 20, "RMB", client.options.useKey.isPressed(), color);
        drawKey(ctx, x, y + 66, 31, 20, "Shf", client.options.sneakKey.isPressed(), color);
        drawKey(ctx, x + 33, y + 66, 31, 20, "Jmp", client.options.jumpKey.isPressed(), color);
    }

    private void drawKey(DrawContext ctx, int x, int y, int w, int h, String label, boolean active, int color) {
        ctx.fill(x, y, x + w, y + h, active ? 0x88FFFFFF : 0x44000000);
        ctx.drawCenteredTextWithShadow(textRenderer, label, x + w / 2, y + (h / 2 - 4), color);
    }

    private void drawLivePotions(DrawContext ctx, int x, int y, int color, boolean drag) {
        assert client != null;
        assert client.player != null;
        Collection<StatusEffectInstance> effects = client.player.getStatusEffects();

        if (effects.isEmpty()) {
            String placeholder = "[Potion Effects]";
            ctx.fill(x - 2, y - 2, x + textRenderer.getWidth(placeholder) + 2, y + 10, drag ? 0x8800FF00 : 0x44FFFFFF);
            ctx.drawText(textRenderer, placeholder, x, y, color, true);
            return;
        }

        int offset = 0;
        for (StatusEffectInstance effect : effects) {
            int durationSeconds = effect.getDuration() / 20;
            String time = String.format("%d:%02d", durationSeconds / 60, durationSeconds % 60);
            String name = Text.translatable(effect.getTranslationKey()).getString();
            String level = effect.getAmplifier() > 0 ? " " + (effect.getAmplifier() + 1) : "";
            String fullText = name + level + " (" + time + ")";

            if (drag && offset == 0) ctx.fill(x - 2, y - 2, x + textRenderer.getWidth(fullText) + 2, y + (effects.size() * 11), 0x4400FF00);
            ctx.drawText(textRenderer, fullText, x, y + offset, color, true);
            offset += 11;
        }
    }

    private void drawLiveArmor(DrawContext ctx, int x, int y, boolean drag) {
        if (drag) ctx.fill(x - 2, y - 2, x + 20, y + 82, 0x4400FF00);
        int i = 0;
        boolean hasArmor = false;
        assert client != null;
        assert client.player != null;
        for (ItemStack stack : client.player.getInventory().armor) {
            if (!stack.isEmpty()) {
                ctx.drawItem(stack, x, y + (3 - i) * 20);
                hasArmor = true;
            }
            i++;
        }
        if (!hasArmor) ctx.drawBorder(x, y, 18, 78, 0x88FFFFFF);
    }

    private void renderColorPicker(DrawContext context, int mouseX, int mouseY, float delta) {
        int x = (this.width - windowW) / 2;
        int y = (this.height - windowH) / 2;

        context.fill(x, y, x + windowW, y + windowH, 0xEE111111);
        context.drawBorder(x, y, windowW, windowH, 0xFFFFFFFF);

        int bx = x + 10, by = y + 10;
        context.fillGradient(bx, by, bx + boxW, by + boxH, Color.HSBtoRGB(currentHue, 1f, 1f), 0xFF000000);

        for (int i = 0; i < boxH; i++) {
            context.fill(bx + boxW + 10, by + i, bx + boxW + 10 + sliderW, by + i + 1, 0xFF000000 | Color.HSBtoRGB(i / (float) boxH, 1f, 1f));
        }

        if (isDraggingHue) {
            currentHue = MathHelper.clamp((mouseY - by) / (float) boxH, 0f, 1f); syncFromHSB();
        } else if (isDraggingSB) {
            currentSat = MathHelper.clamp((mouseX - bx) / (float) boxW, 0f, 1f);
            currentBri = MathHelper.clamp(1 - (mouseY - by) / (float) boxH, 0f, 1f);
            syncFromHSB();
        }

        hexInput.render(context, mouseX, mouseY, delta);
        rInput.render(context, mouseX, mouseY, delta);
        gInput.render(context, mouseX, mouseY, delta);
        bInput.render(context, mouseX, mouseY, delta);
        resetButton.render(context, mouseX, mouseY, delta);

        context.drawText(textRenderer, "Hex", hexInput.getX(), hexInput.getY() - 10, 0xFFAAAAAA, false);
    }

    private void renderModule(DrawContext ctx, int x, int y, int w, int h, String text, int col, boolean drag) {
        ctx.fill(x - 2, y - 2, x + w + 2, y + h + 2, drag ? 0x8800FF00 : 0x44FFFFFF);
        ctx.drawText(textRenderer, text, x, y, col, true);
    }

    @Override
    public boolean mouseClicked(double mx, double my, int b) {
        if (pickingColorFor != SelectedModule.NONE) {
            if (super.mouseClicked(mx, my, b)) return true;
            this.setFocused(null);

            int x = (this.width - windowW) / 2 + 10;
            int y = (this.height - windowH) / 2 + 10;

            if (isHovering(mx, my, x + boxW + 10, y, sliderW, boxH)) {
                isDraggingHue = true;
                return true;
            }

            if (isHovering(mx, my, x, y, boxW, boxH)) {
                isDraggingSB = true;
                return true;
            }

            if (!isHovering(mx, my, (this.width-windowW)/2, (this.height-windowH)/2, windowW, windowH)) {
                pickingColorFor = SelectedModule.NONE;
            }
            return true;
        }
        SelectedModule hovered = getHoveredModule(mx, my);
        if (hovered != SelectedModule.NONE) {
            if (b == 0) {
                dragging = hovered;
                dragOffsetX = (int)mx - getX(hovered);
                dragOffsetY = (int)my - getY(hovered);
            } else if (b == 1) {
                pickingColorFor = hovered;
                updateAll(getModuleColor(hovered), true);
            }
            return true;
        }
        return super.mouseClicked(mx, my, b);
    }

    @Override
    public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
        return super.keyPressed(keyCode, scanCode, modifiers);
    }

    @Override
    public boolean charTyped(char chr, int modifiers) {
        return super.charTyped(chr, modifiers);
    }

    private SelectedModule getHoveredModule(double mx, double my) {
        HudConfig c = HudConfig.INSTANCE;
        if (isHovering(mx, my, c.fpsX, c.fpsY, 50, 10)) return SelectedModule.FPS;
        if (isHovering(mx, my, c.pingX, c.pingY, 40, 10)) return SelectedModule.PING;
        if (isHovering(mx, my, c.serverX, c.serverY, 80, 10)) return SelectedModule.SERVER;
        if (isHovering(mx, my, c.keyX, c.keyY, 66, 88)) return SelectedModule.KEYS;
        if (isHovering(mx, my, c.armorX, c.armorY, 20, 80)) return SelectedModule.ARMOR;
        if (isHovering(mx, my, c.potionX, c.potionY, 100, 40)) return SelectedModule.POTIONS;
        return SelectedModule.NONE;
    }

    private void onHexChanged(String hex) {
        if (!ignoreUpdates && (hex.length() == 7 || hex.length() == 6)) {
            try {
                String h = hex.startsWith("#") ? hex.substring(1) : hex;
                updateAll(Integer.parseInt(h, 16) | 0xFF000000, false);
            } catch (Exception ignored) {}
        }
    }

    private void onRGBChanged() {
        if (!ignoreUpdates) {
            try {
                updateAll(new Color(Integer.parseInt(
                        rInput.getText()),
                        Integer.parseInt(gInput.getText()),
                        Integer.parseInt(bInput.getText())).getRGB(),
                        false);
            } catch (Exception ignored) {

            }
        }
    }

    private void syncFromHSB() {
        updateAll(Color.HSBtoRGB(currentHue, currentSat, currentBri), false);
    }

    private void updateAll(int rgb, boolean force) {
        ignoreUpdates = true;
        int color = rgb | 0xFF000000;
        HudConfig c = HudConfig.INSTANCE;
        switch (pickingColorFor) {
            case FPS->c.fpsColor=color;
            case PING->c.pingColor=color;
            case SERVER->c.serverColor=color;
            case KEYS->c.keyColor=color;
            case ARMOR->c.armorColor=color;
            case POTIONS->c.potionColor=color;
        }

        float[] hsb = Color.RGBtoHSB((color>>16)&0xFF, (color>>8)&0xFF, color&0xFF, null);
        currentHue = hsb[0]; currentSat = hsb[1]; currentBri = hsb[2];
        if (force || !hexInput.isFocused()) hexInput.setText(String.format("#%06X", (0xFFFFFF & color)));
        if (force || !rInput.isFocused()) rInput.setText(String.valueOf((color>>16)&0xFF));
        if (force || !gInput.isFocused()) gInput.setText(String.valueOf((color>>8)&0xFF));
        if (force || !bInput.isFocused()) bInput.setText(String.valueOf(color&0xFF));
        ignoreUpdates = false;
    }

    private boolean isHovering(double mx, double my, int x, int y, int w, int h) {
        return mx >= x && mx <= x + w && my >= y && my <= y + h;
    }

    private int getX(SelectedModule m) {
        HudConfig c = HudConfig.INSTANCE;
        return switch(m) {
            case FPS->c.fpsX;
            case PING->c.pingX;
            case SERVER->c.serverX;
            case KEYS->c.keyX;
            case ARMOR->c.armorX;
            case POTIONS->c.potionX;
            default->0;
        };
    }

    private int getY(SelectedModule m) {
        HudConfig c = HudConfig.INSTANCE;
        return switch(m) {
            case FPS->c.fpsY;
            case PING->c.pingY;
            case SERVER->c.serverY;
            case KEYS->c.keyY;
            case ARMOR->c.armorY;
            case POTIONS->c.potionY;
            default->0;
        };
    }

    private int getModuleColor(SelectedModule m) {
        HudConfig c = HudConfig.INSTANCE;
        return switch(m) {
            case FPS->c.fpsColor;
            case PING->c.pingColor;
            case SERVER->c.serverColor;
            case KEYS->c.keyColor;
            case ARMOR->c.armorColor;
            case POTIONS->c.potionColor;
            default->-1;
        };
    }

    @Override public boolean mouseDragged(double mx, double my, int b, double dx, double dy) {
        if (dragging != SelectedModule.NONE) {
            HudConfig c = HudConfig.INSTANCE; int nx = (int)mx - dragOffsetX, ny = (int)my - dragOffsetY;
            switch(dragging) {
                case FPS->{
                    c.fpsX=nx;
                    c.fpsY=ny;
                }
                case PING->{
                    c.pingX=nx;
                    c.pingY=ny;
                }
                case SERVER->{
                    c.serverX=nx;
                    c.serverY=ny;
                }
                case KEYS->{
                    c.keyX=nx;
                    c.keyY=ny;
                }
                case ARMOR->{
                    c.armorX=nx;
                    c.armorY=ny;
                }
                case POTIONS->{
                    c.potionX=nx;
                    c.potionY=ny;
                }
            }
            return true;
        }
        return super.mouseDragged(mx, my, b, dx, dy);
    }

    @Override public boolean mouseReleased(double mx, double my, int b) {
        isDraggingHue = isDraggingSB = false;
        dragging = SelectedModule.NONE;
        return super.mouseReleased(mx, my, b);
    }

    @Override public void close() {
        HudConfig.save();
        super.close();
    }
}