package de.blight.editor;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;

public class EditorApp extends Application {

    // ── Viewport-Auflösung (JME3 rendert intern auf diese Größe) ────────────
    static final int VP_WIDTH  = 1024;
    static final int VP_HEIGHT = 640;

    // ── Asset-Verzeichnis ────────────────────────────────────────────────────
    private static final Path ASSET_ROOT = Paths.get("editor-assets");

    private final SharedInput  input       = new SharedInput();
    private WritableImage      jfxImage;
    private ImageView          viewport;
    private Label              statusLabel;

    // Drag-Tracking für Kamerarotation (mittlere Taste)
    private double prevDragX, prevDragY;

    // ── Asset-Tree-Items ─────────────────────────────────────────────────────
    private TreeItem<String> modelsNode;
    private TreeItem<String> texturesNode;
    private TreeItem<String> audioNode;

    // ── JavaFX Entry-Point ───────────────────────────────────────────────────

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        // WritableImage für den JME3-Frame-Export
        jfxImage = new WritableImage(VP_WIDTH, VP_HEIGHT);

        // JME3 starten (Hintergrund-Daemon-Thread)
        JmeEditorApp.launch(input, jfxImage, VP_WIDTH, VP_HEIGHT);

        // UI zusammenbauen
        BorderPane root = new BorderPane();
        root.setTop(buildTop());
        root.setLeft(buildAssetPanel());
        root.setCenter(buildViewport());
        root.setBottom(buildStatusBar());

        Scene scene = new Scene(root, 1280, 760);
        scene.setOnKeyPressed(e  -> handleKeyPress(e.getCode(), true));
        scene.setOnKeyReleased(e -> handleKeyPress(e.getCode(), false));

        stage.setTitle("Blight World Editor");
        stage.setScene(scene);
        stage.setMinWidth(900);
        stage.setMinHeight(600);
        stage.setOnCloseRequest(e -> Platform.exit());
        stage.show();
    }

    // ── Oberer Bereich: MenuBar + ToolBar ────────────────────────────────────

    private VBox buildTop() {
        // Menüleiste
        MenuBar menuBar = new MenuBar();
        Menu fileMenu = new Menu("Datei");
        MenuItem newItem  = new MenuItem("Neue Karte");
        MenuItem saveItem = new MenuItem("Speichern");
        fileMenu.getItems().addAll(newItem, saveItem);
        Menu viewMenu = new Menu("Ansicht");
        MenuItem resetCam = new MenuItem("Kamera zurücksetzen");
        resetCam.setOnAction(e -> input.addMouseDelta(0, 0)); // noop, Kamera reset via SharedInput wäre aufwändiger
        viewMenu.getItems().add(resetCam);
        menuBar.getMenus().addAll(fileMenu, viewMenu);

        // Werkzeugleiste
        ToolBar toolBar = new ToolBar();
        Button heightTool = new Button("▲▼ Höhe");
        heightTool.setStyle("-fx-font-weight:bold;");
        heightTool.setTooltip(new Tooltip(
            "Linksklick: Terrain anheben\nRechtsklick: Terrain absenken"));

        Separator sep1 = new Separator(Orientation.VERTICAL);

        Label brushLabel = new Label("Pinselstärke:");
        Slider brushSlider = new Slider(0.1, 1.0, 1.0);
        brushSlider.setPrefWidth(100);
        brushSlider.setMajorTickUnit(0.5);
        brushSlider.setShowTickMarks(true);

        Separator sep2 = new Separator(Orientation.VERTICAL);

        Label hint = new Label("WASD/QE: Kamera  |  Mitte-Drag: Drehen  |  L-Klick: hoch  |  R-Klick: tief");
        hint.setStyle("-fx-text-fill: #555;");

        toolBar.getItems().addAll(heightTool, sep1, brushLabel, brushSlider, sep2, hint);

        VBox top = new VBox(menuBar, toolBar);
        return top;
    }

    // ── Linke Seite: Asset-Panel ─────────────────────────────────────────────

    private VBox buildAssetPanel() {
        VBox panel = new VBox(6);
        panel.setPadding(new Insets(8));
        panel.setPrefWidth(210);
        panel.setStyle("-fx-background-color: #f0f0f0; -fx-border-color: #ccc; -fx-border-width: 0 1 0 0;");

        Label title = new Label("Assets");
        title.setStyle("-fx-font-weight: bold; -fx-font-size: 13;");

        // Baum
        TreeItem<String> root = new TreeItem<>("Projekt");
        root.setExpanded(true);
        modelsNode   = new TreeItem<>("Models");
        texturesNode = new TreeItem<>("Texturen");
        audioNode    = new TreeItem<>("Audio");
        root.getChildren().addAll(modelsNode, texturesNode, audioNode);

        // Bestehende Assets laden
        loadAssetsInto(modelsNode,   "models",   ".j3o", ".obj", ".fbx", ".gltf", ".glb");
        loadAssetsInto(texturesNode, "textures", ".png", ".jpg", ".jpeg", ".bmp", ".tga", ".dds");
        loadAssetsInto(audioNode,    "audio",    ".ogg", ".wav", ".mp3");

        TreeView<String> tree = new TreeView<>(root);
        tree.setShowRoot(false);
        VBox.setVgrow(tree, Priority.ALWAYS);

        // Kontextmenü im Baum (Datei öffnen im Dateimanager)
        ContextMenu ctx = new ContextMenu();
        MenuItem showItem = new MenuItem("Im Dateisystem anzeigen");
        ctx.getItems().add(showItem);
        tree.setContextMenu(ctx);

        // Import-Button
        Button importBtn = new Button("⊕ Import…");
        importBtn.setMaxWidth(Double.MAX_VALUE);
        importBtn.setOnAction(e -> handleImport(tree.getScene().getWindow()));

        panel.getChildren().addAll(title, tree, importBtn);
        return panel;
    }

    private void loadAssetsInto(TreeItem<String> parent, String subDir, String... exts) {
        Path dir = ASSET_ROOT.resolve(subDir);
        if (!Files.exists(dir)) return;
        try (var stream = Files.list(dir)) {
            stream.filter(p -> {
                String name = p.getFileName().toString().toLowerCase();
                for (String ext : exts) if (name.endsWith(ext)) return true;
                return false;
            }).map(p -> new TreeItem<>(p.getFileName().toString()))
              .forEach(parent.getChildren()::add);
        } catch (IOException ignored) {}
    }

    private void handleImport(javafx.stage.Window owner) {
        FileChooser fc = new FileChooser();
        fc.setTitle("Assets importieren");
        fc.getExtensionFilters().addAll(
            new FileChooser.ExtensionFilter("Alle unterstützten Dateien",
                "*.j3o","*.obj","*.fbx","*.gltf","*.glb",
                "*.png","*.jpg","*.jpeg","*.bmp","*.tga","*.dds",
                "*.ogg","*.wav","*.mp3"),
            new FileChooser.ExtensionFilter("Modelle", "*.j3o","*.obj","*.fbx","*.gltf","*.glb"),
            new FileChooser.ExtensionFilter("Texturen", "*.png","*.jpg","*.jpeg","*.bmp","*.tga","*.dds"),
            new FileChooser.ExtensionFilter("Audio", "*.ogg","*.wav","*.mp3")
        );
        var files = fc.showOpenMultipleDialog(owner);
        if (files == null) return;

        for (File file : files) {
            String name = file.getName().toLowerCase();
            String subDir;
            TreeItem<String> parent;
            if (name.matches(".*\\.(j3o|obj|fbx|gltf|glb)")) {
                subDir = "models";  parent = modelsNode;
            } else if (name.matches(".*\\.(ogg|wav|mp3)")) {
                subDir = "audio";   parent = audioNode;
            } else {
                subDir = "textures"; parent = texturesNode;
            }
            try {
                Path dest = ASSET_ROOT.resolve(subDir);
                Files.createDirectories(dest);
                Path target = dest.resolve(file.getName());
                Files.copy(file.toPath(), target, StandardCopyOption.REPLACE_EXISTING);
                parent.getChildren().add(new TreeItem<>(file.getName()));
                parent.setExpanded(true);
                setStatus("Importiert: " + file.getName());
            } catch (IOException ex) {
                setStatus("Fehler beim Import: " + ex.getMessage());
            }
        }
    }

    // ── Zentraler Bereich: JME3-Viewport ────────────────────────────────────

    private StackPane buildViewport() {
        viewport = new ImageView(jfxImage);
        viewport.setPreserveRatio(false);
        viewport.setFocusTraversable(true);

        StackPane pane = new StackPane(viewport);
        pane.setStyle("-fx-background-color: #1a1a2e;");

        // ImageView auf Pane-Größe binden → JME3-Pixelskalierung aktualisieren
        pane.widthProperty().addListener((o, oldW, newW) -> {
            viewport.setFitWidth(newW.doubleValue());
            input.viewportScaleX = VP_WIDTH / newW.doubleValue();
        });
        pane.heightProperty().addListener((o, oldH, newH) -> {
            viewport.setFitHeight(newH.doubleValue());
            input.viewportScaleY = VP_HEIGHT / newH.doubleValue();
        });

        // ── Maus-Events ──────────────────────────────────────────────────────
        viewport.setOnMousePressed(e -> {
            viewport.requestFocus();
            if (e.getButton() == MouseButton.MIDDLE) {
                prevDragX = e.getX();
                prevDragY = e.getY();
            }
            if (e.getButton() == MouseButton.PRIMARY) {
                submitEdit(e.getX(), e.getY(), +1);
            }
            if (e.getButton() == MouseButton.SECONDARY) {
                submitEdit(e.getX(), e.getY(), -1);
            }
        });

        viewport.setOnMouseDragged(e -> {
            if (e.isMiddleButtonDown()) {
                double dx = e.getX() - prevDragX;
                double dy = e.getY() - prevDragY;
                input.addMouseDelta((int) dx, (int) dy);
                prevDragX = e.getX();
                prevDragY = e.getY();
            }
            if (e.isPrimaryButtonDown()) {
                submitEdit(e.getX(), e.getY(), +1);
            }
            if (e.isSecondaryButtonDown()) {
                submitEdit(e.getX(), e.getY(), -1);
            }
        });

        viewport.setOnScroll(e -> {
            // Scrollen = Kamera vorwärts/rückwärts
            double delta = e.getDeltaY();
            input.forward  = delta > 0;
            input.backward = delta < 0;
            // Nach kurzem Delay zurücksetzen (kein physischer Key-Release)
            javafx.animation.PauseTransition pause =
                new javafx.animation.PauseTransition(javafx.util.Duration.millis(150));
            pause.setOnFinished(ev -> { input.forward = false; input.backward = false; });
            pause.play();
        });

        return pane;
    }

    private void submitEdit(double x, double y, int action) {
        input.editQueue.offer(new SharedInput.TerrainEdit((float) x, (float) y, action));
    }

    // ── Statusleiste ─────────────────────────────────────────────────────────

    private HBox buildStatusBar() {
        statusLabel = new Label("Bereit  |  Werkzeug: Höhe  |  WASD/QE: Bewegen  |  Mitte-Drag: Drehen");
        statusLabel.setPadding(new Insets(3, 8, 3, 8));
        statusLabel.setStyle("-fx-font-size: 11; -fx-text-fill: #333;");
        HBox bar = new HBox(statusLabel);
        bar.setStyle("-fx-background-color: #e8e8e8; -fx-border-color: #bbb; -fx-border-width: 1 0 0 0;");
        return bar;
    }

    private void setStatus(String msg) {
        Platform.runLater(() -> statusLabel.setText(msg));
    }

    // ── Tastatur-Handling ────────────────────────────────────────────────────

    private void handleKeyPress(KeyCode code, boolean pressed) {
        switch (code) {
            case W -> input.forward  = pressed;
            case S -> input.backward = pressed;
            case A -> input.left     = pressed;
            case D -> input.right    = pressed;
            case Q -> input.up       = pressed;
            case E -> input.down     = pressed;
        }
    }
}
