package p.studio.workspaces.editor;

import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.OverrunStyle;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import p.studio.Container;
import p.studio.utilities.i18n.I18n;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

public final class EditorTabStrip extends HBox {
    private static final double TAB_WIDTH_HINT = 176.0;
    private static final double TAB_HEIGHT_HINT = 34.0;
    private static final double STRIP_HEIGHT_HINT = 50.0;
    private static final double OVERFLOW_WIDTH_HINT = 110.0;

    private final MenuButton overflowButton = new MenuButton();
    private final Region spacer = new Region();
    private final List<EditorOpenFileBuffer> openFiles = new ArrayList<>();
    private Consumer<Path> tabSelectionAction = path -> { };
    private Path activePath;

    public EditorTabStrip() {
        setAlignment(Pos.CENTER_LEFT);
        setSpacing(10);
        getStyleClass().add("editor-workspace-tab-strip");
        setMinHeight(STRIP_HEIGHT_HINT);
        setPrefHeight(STRIP_HEIGHT_HINT);
        setMaxHeight(STRIP_HEIGHT_HINT);
        HBox.setHgrow(spacer, Priority.ALWAYS);

        overflowButton.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_TABS_OVERFLOW));
        overflowButton.setFocusTraversable(false);
        overflowButton.getStyleClass().addAll(
                "studio-button",
                "studio-button-secondary",
                "editor-workspace-tab-overflow");
        widthProperty().addListener((ignored, previous, current) -> rebuild());
        rebuild();
    }

    public void setTabSelectionAction(final Consumer<Path> tabSelectionAction) {
        this.tabSelectionAction = Objects.requireNonNull(tabSelectionAction, "tabSelectionAction");
    }

    public void showOpenFiles(final List<EditorOpenFileBuffer> files, final Path activePath) {
        this.openFiles.clear();
        this.openFiles.addAll(Objects.requireNonNull(files, "files"));
        this.activePath = activePath == null ? null : activePath.toAbsolutePath().normalize();
        rebuild();
    }

    private void rebuild() {
        getChildren().clear();
        if (openFiles.isEmpty()) {
            overflowButton.getItems().clear();
            getChildren().add(spacer);
            overflowButton.setVisible(false);
            overflowButton.setManaged(false);
            return;
        }

        final int visibleCount = visibleCount();
        final int activeIndex = activeIndex();
        final int startIndex = Math.max(0, Math.min(activeIndex - visibleCount + 1, openFiles.size() - visibleCount));
        final int endIndex = Math.min(openFiles.size(), startIndex + visibleCount);

        for (int index = startIndex; index < endIndex; index++) {
            final var fileBuffer = openFiles.get(index);
            final var tabButton = new Button(fileBuffer.tabLabel());
            tabButton.setFocusTraversable(false);
            tabButton.getStyleClass().addAll(
                    "studio-button",
                    "studio-button-secondary",
                    "editor-workspace-tab-button",
                    "editor-workspace-tab-button-read-only");
            applyTabMetrics(tabButton);
            if (fileBuffer.path().equals(activePath)) {
                tabButton.getStyleClass().add("editor-workspace-tab-button-active");
            }
            tabButton.setOnAction(event -> tabSelectionAction.accept(fileBuffer.path()));
            getChildren().add(tabButton);
        }

        getChildren().add(spacer);
        final boolean hasOverflow = openFiles.size() > visibleCount;
        overflowButton.getItems().setAll(buildOverflowItems(startIndex, endIndex));
        overflowButton.setVisible(hasOverflow);
        overflowButton.setManaged(hasOverflow);
        overflowButton.setDisable(!hasOverflow);
        getChildren().add(overflowButton);
    }

    private List<MenuItem> buildOverflowItems(final int startIndex, final int endIndex) {
        final var items = new ArrayList<MenuItem>();
        for (int index = 0; index < openFiles.size(); index++) {
            if (index >= startIndex && index < endIndex) {
                continue;
            }
            final var fileBuffer = openFiles.get(index);
            final var item = new MenuItem(fileBuffer.tabLabel());
            item.setOnAction(event -> tabSelectionAction.accept(fileBuffer.path()));
            items.add(item);
        }
        return items;
    }

    private int activeIndex() {
        if (activePath == null) {
            return Math.max(0, openFiles.size() - 1);
        }
        for (int index = 0; index < openFiles.size(); index++) {
            if (openFiles.get(index).path().equals(activePath)) {
                return index;
            }
        }
        return Math.max(0, openFiles.size() - 1);
    }

    private int visibleCount() {
        final double availableWidth = getWidth() > 0 ? getWidth() : 760;
        final double usableWidth = Math.max(availableWidth - OVERFLOW_WIDTH_HINT, TAB_WIDTH_HINT);
        return Math.max(1, (int) Math.floor(usableWidth / TAB_WIDTH_HINT));
    }

    private void applyTabMetrics(final Button button) {
        button.setMinWidth(TAB_WIDTH_HINT);
        button.setPrefWidth(TAB_WIDTH_HINT);
        button.setMaxWidth(TAB_WIDTH_HINT);
        button.setMinHeight(TAB_HEIGHT_HINT);
        button.setPrefHeight(TAB_HEIGHT_HINT);
        button.setMaxHeight(TAB_HEIGHT_HINT);
        button.setTextOverrun(OverrunStyle.ELLIPSIS);
        button.setMnemonicParsing(false);
    }
}
