package io.xpipe.app.comp.base;

import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.SimpleRegionBuilder;
import io.xpipe.app.core.*;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.platform.ColorHelper;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.GlobalTimer;

import javafx.animation.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.Window;

import java.time.Duration;

public class AppMainWindowContentComp extends SimpleRegionBuilder {

    private final Stage stage;

    public AppMainWindowContentComp(Stage stage) {
        this.stage = stage;
    }

    @Override
    public Region createSimple() {
        var overlay = AppDialog.getModalOverlays();
        var loaded = AppMainWindow.getLoadedContent();
        var sidebarPresent = new SimpleBooleanProperty();
        var bg = RegionBuilder.of(() -> {
            var loadingIcon = new ImageView();
            loadingIcon.setFitWidth(80);
            loadingIcon.setFitHeight(80);

            var dark =
                    AppPrefs.get() != null && AppPrefs.get().theme().getValue().isDark();
            loadingIcon.setOpacity(dark ? 0.95 : 0.93);

            var color = AppPrefs.get() != null
                    ? ColorHelper.withOpacity(
                            AppPrefs.get().theme().getValue().getEmphasisColor().get(), dark ? 0.7 : 0.85)
                    : Color.TRANSPARENT;
            DropShadow shadow = new DropShadow();
            shadow.setRadius(10);
            shadow.setColor(color);

            var loadingAnimation = new AnimationTimer() {

                long offset;

                @Override
                public void handle(long now) {
                    // Increment offset as we are always having 60fps
                    // Prevents animation jumps when the animation timer isn't called for a long time
                    offset += 1000 / 60;

                    // Move shadow in a circle
                    var rad = -(offset % 1300.0) / 1300.0 * 2 * Math.PI;
                    var x = Math.sin(rad);
                    var y = Math.cos(rad);
                    shadow.setOffsetX(x * 3);
                    shadow.setOffsetY(y * 3);
                }
            };

            loadingIcon.setEffect(shadow);
            loadingAnimation.start();

            // This allows for assigning logos even if AppImages has not been initialized yet
            AppResources.with(AppResources.MAIN_MODULE, "", path -> {
                var image = AppPrefs.get() != null
                                && AppPrefs.get().theme().getValue().isDark()
                        ? path.resolve("loading-160-dark.png")
                        : path.resolve("loading-160.png");
                loadingIcon.setImage(AppImages.loadImage(image));
            });

            loadingIcon.visibleProperty().bind(Bindings.isEmpty(AppDialog.getModalOverlays()));

            var version = new LabelComp(
                    (AppNames.ofCurrent().getName()) + " " + AppProperties.get().getVersion());
            version.apply(struc -> {
                AppFontSizes.apply(struc, appFontSizes -> "15");
                struc.setOpacity(0.65);
            });

            var loadingTextCounter = new SimpleIntegerProperty();
            GlobalTimer.scheduleUntil(Duration.ofMillis(500), false, () -> {
                if (loaded.getValue() != null) {
                    return true;
                }

                loadingTextCounter.set((loadingTextCounter.get() + 1) % 4);
                return false;
            });
            var loadingTextAnimated = Bindings.createStringBinding(
                    () -> {
                        var base = AppMainWindow.getLoadingText().getValue();
                        if (base == null) {
                            return null;
                        }
                        return base + " " + (".".repeat(loadingTextCounter.get()))
                                + (" ".repeat(3 - loadingTextCounter.get()));
                    },
                    AppMainWindow.getLoadingText(),
                    loadingTextCounter);
            var text = new LabelComp(loadingTextAnimated);
            text.style("loading-text");
            text.apply(struc -> {
                struc.setOpacity(0.8);
            });

            var vbox = new VBox(
                    RegionBuilder.vspacer().build(),
                    loadingIcon,
                    RegionBuilder.vspacer(19).build(),
                    version.build(),
                    RegionBuilder.vspacer().build(),
                    text.build(),
                    RegionBuilder.vspacer(20).build());
            vbox.setAlignment(Pos.CENTER);

            var pane = new StackPane(vbox);
            pane.setAlignment(Pos.TOP_LEFT);
            pane.getStyleClass().add("background");

            loaded.subscribe(struc -> {
                if (struc != null) {
                    TrackEvent.info("Window content node set");
                    PlatformThread.runNestedLoopIteration();
                    struc.prepareAddition();
                    pane.getStyleClass().remove("background");
                    loadingAnimation.stop();
                    pane.getChildren().remove(vbox);
                    pane.getChildren().add(struc.get());
                    sidebarPresent.set(true);
                    PlatformThread.runNestedLoopIteration();
                    struc.show();
                    TrackEvent.info("Window content node shown");
                } else if (!pane.getChildren().contains(vbox)) {
                    loadingTextCounter.set(3);
                    TrackEvent.info("Window content node removed");
                    PlatformThread.runNestedLoopIteration();
                    pane.getChildren().clear();
                    pane.getStyleClass().add("background");
                    pane.getChildren().add(vbox);
                    sidebarPresent.set(false);
                    loadingAnimation.start();
                    PlatformThread.runNestedLoopIteration();
                }
            });

            overlay.addListener((ListChangeListener<? super ModalOverlay>) c -> {
                if (c.next() && c.wasAdded()) {
                    AppMainWindow.get().focus();

                    // Close blocking modal windows
                    var childWindows = Window.getWindows().stream()
                            .filter(window -> window instanceof Stage s && stage.equals(s.getOwner()))
                            .toList();
                    childWindows.forEach(window -> {
                        ((Stage) window).close();
                    });
                }
            });

            return pane;
        });

        var modal = new ModalOverlayStackComp(bg, overlay);
        var r = modal.build();
        var p = r.lookupAll(".modal-overlay-stack-element");
        sidebarPresent.subscribe(v -> {
            if (v) {
                p.forEach(node -> {
                    node.pseudoClassStateChanged(PseudoClass.getPseudoClass("loaded"), true);
                });
            }
        });

        return r;
    }
}
