package p.studio.controls.shell;

import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import p.studio.Container;
import p.studio.controls.lifecycle.StudioControlLifecycle;
import p.studio.controls.lifecycle.StudioControlLifecycleSupport;
import p.studio.execution.StudioExecutionSessionService;
import p.studio.execution.StudioExecutionSnapshot;
import p.studio.execution.StudioExecutionState;
import p.studio.utilities.events.EventSubscription;
import p.studio.utilities.i18n.I18n;

public final class StudioRunSurfaceControl extends HBox implements StudioControlLifecycle {
    private final BooleanProperty running = new SimpleBooleanProperty(false);
    private final StudioExecutionSessionService executionSessionService;
    private final Runnable playAction;
    private final Runnable stopAction;
    private final Button toggleButton = new Button();
    private EventSubscription sessionSubscription;

    public StudioRunSurfaceControl(
            final StudioExecutionSessionService executionSessionService,
            final Runnable playAction,
            final Runnable stopAction) {
        StudioControlLifecycleSupport.install(this, this);
        this.executionSessionService = executionSessionService;
        this.playAction = playAction;
        this.stopAction = stopAction;
        getStyleClass().add("studio-run-surface");
        setSpacing(8);
        setAlignment(Pos.CENTER_LEFT);

        final Label icon = new Label();
        icon.getStyleClass().add("studio-run-toggle-icon");
        icon.textProperty().bind(Bindings.when(running).then("\u25A0").otherwise("\u25B6"));

        final StackPane iconShell = new StackPane(icon);
        iconShell.getStyleClass().add("studio-run-toggle-icon-shell");

        final Label text = new Label();
        text.getStyleClass().add("studio-run-toggle-text");
        text.textProperty().bind(Bindings.createStringBinding(
                () -> Container.i18n().text(running.get() ? I18n.TOOLBAR_STOP : I18n.TOOLBAR_PLAY),
                running));

        final HBox content = new HBox(8, iconShell, text);
        content.setAlignment(Pos.CENTER_LEFT);

        toggleButton.getStyleClass().add("studio-run-toggle-button");
        toggleButton.graphicProperty().set(content);
        toggleButton.setOnAction(ignored -> handleToggleRequested());

        running.addListener((ignored, oldValue, newValue) -> {
            toggleButton.getStyleClass().removeAll("studio-run-toggle-play", "studio-run-toggle-stop");
            toggleButton.getStyleClass().add(newValue ? "studio-run-toggle-stop" : "studio-run-toggle-play");
        });
        toggleButton.getStyleClass().add("studio-run-toggle-play");

        getChildren().add(toggleButton);
    }

    public boolean isRunning() {
        return running.get();
    }

    public BooleanProperty runningProperty() {
        return running;
    }

    @Override
    public void subscribe() {
        if (sessionSubscription != null) {
            return;
        }
        sessionSubscription = executionSessionService.subscribe(this::handleSessionUpdate);
    }

    private void handleSessionUpdate(final StudioExecutionSnapshot snapshot) {
        if (Platform.isFxApplicationThread()) {
            running.set(isRunningState(snapshot.state()));
            return;
        }
        Platform.runLater(() -> running.set(isRunningState(snapshot.state())));
    }

    @Override
    public void unsubscribe() {
        if (sessionSubscription == null) {
            return;
        }
        sessionSubscription.unsubscribe();
        sessionSubscription = null;
    }

    private void handleToggleRequested() {
        final StudioExecutionState state = executionSessionService.snapshot().state();
        if (isRunningState(state)) {
            stopAction.run();
            return;
        }
        playAction.run();
    }

    private boolean isRunningState(final StudioExecutionState state) {
        return state == StudioExecutionState.PREPARING
                || state == StudioExecutionState.CONNECTING
                || state == StudioExecutionState.RUNNING;
    }
}
