package dev.photosync.ui.widget;

import dev.photosync.mcapi.render.RenderBridge;
import dev.photosync.ui.Chrome;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * A labelled rectangle that runs something when clicked.
 *
 * <p>The label is mutable because several of them change in place -- "Test
 * connection" becomes "Testing..." and then "Connected" -- and swapping the
 * widget out would lose the layout it was given.
 */
@Accessors(fluent = true)
public final class Button extends Widget {

    /** How much a button asks to stand out. */
    public enum Emphasis {
        /** The obvious next action. One per screen at most. */
        PRIMARY,
        /** Everything else. */
        NORMAL,
        /** Destructive: cancel an upload, quit with work outstanding. */
        DANGER
    }

    @Setter
    private String label;

    @Setter
    private Emphasis emphasis = Emphasis.NORMAL;

    private final Runnable action;
    private boolean pressed;

    public Button(Chrome chrome, String label, Runnable action) {
        super(chrome);
        this.label = label;
        this.action = action;
    }

    public Button emphasized(Emphasis value) {
        this.emphasis = value;
        return this;
    }

    @Override
    public void render(RenderBridge render, int mouseX, int mouseY) {
        boolean hovered = hovering(mouseX, mouseY);
        int background = background(hovered);
        render.fill(bounds().x(), bounds().y(), bounds().width(), bounds().height(), background);
        render.border(bounds().x(), bounds().y(), bounds().width(), bounds().height(), borderColour(hovered));
        chrome.centered(render, chrome.elide(render, label, bounds().width() - 6), bounds(), foreground());
    }

    @Override
    public boolean mouseClicked(double mouseX, double mouseY, int button) {
        if (button != 0 || !hovering(mouseX, mouseY)) {
            return false;
        }
        pressed = true;
        return true;
    }

    @Override
    public boolean mouseReleased(double mouseX, double mouseY, int button) {
        if (!pressed) {
            return false;
        }
        pressed = false;
        // Only fire if the cursor is still on the button, so a click can be
        // taken back by dragging off it -- the convention everywhere else.
        if (hovering(mouseX, mouseY)) {
            action.run();
        }
        return true;
    }

    private int background(boolean hovered) {
        if (!enabled()) {
            return theme().fade(theme().surface(), 0.6f);
        }
        return switch (emphasis) {
            case PRIMARY -> pressed || hovered ? theme().accentHover() : theme().accent();
            case DANGER -> pressed ? theme().danger() : theme().surfaceFor(hovered, false);
            case NORMAL -> theme().surfaceFor(hovered, pressed);
        };
    }

    private int borderColour(boolean hovered) {
        if (!enabled()) {
            return theme().panelBorder();
        }
        return switch (emphasis) {
            case PRIMARY -> theme().accentHover();
            case DANGER -> hovered ? theme().danger() : theme().panelBorder();
            case NORMAL -> hovered ? theme().scrollThumbHover() : theme().panelBorder();
        };
    }

    private int foreground() {
        if (!enabled()) {
            return theme().textFaint();
        }
        return switch (emphasis) {
            case PRIMARY -> theme().accentText();
            case DANGER -> theme().danger();
            case NORMAL -> theme().text();
        };
    }
}
