import { CommandHistory } from "./CommandHistory.js";
import { DiagnosticsOverlayModel } from "./DiagnosticsOverlayModel.js";
import { normalizeGizmoSettings } from "./Gizmo.js";
import { EditorStateModel } from "./EditorStateModel.js";
import { HierarchyModel } from "./HierarchyModel.js";
import { InspectorModel } from "./InspectorModel.js";
import { MaterialVariantWorkflow } from "./MaterialVariantWorkflow.js";
import { PickingService } from "./PickingService.js";
import { PrefabRegistry } from "./PrefabRegistry.js";
import { RotateGizmo } from "./RotateGizmo.js";
import { ScaleGizmo } from "./ScaleGizmo.js";
import { Selection } from "./Selection.js";
import { TranslateGizmo } from "./TranslateGizmo.js";
export class EditorRuntime {
    selection = new Selection();
    history = new CommandHistory();
    inspector = new InspectorModel();
    hierarchy = new HierarchyModel();
    materialVariants = new MaterialVariantWorkflow();
    diagnostics = new DiagnosticsOverlayModel();
    prefabs = new PrefabRegistry();
    state;
    picking = new PickingService();
    modeRef = "edit";
    activeToolRef = "select";
    gizmoSettingsRef;
    disposed = false;
    activePlayModeBridge = null;
    constructor(options = {}) {
        this.state = new EditorStateModel({ storage: options.stateStorage, storageKey: options.stateStorageKey });
        this.activeToolRef = this.state.activeTool;
        this.gizmoSettingsRef = this.state.gizmoSettings();
    }
    get mode() {
        return this.modeRef;
    }
    get activeTool() {
        return this.activeToolRef;
    }
    setTool(tool) {
        this.assertEditable();
        const normalized = tool.trim();
        if (normalized.length === 0) {
            throw new Error("EditorRuntime tool name cannot be empty.");
        }
        this.state.setActiveTool(normalized);
        this.activeToolRef = normalized;
    }
    configureGizmos(settings) {
        this.assertEditable();
        this.gizmoSettingsRef = normalizeGizmoSettings({ ...this.gizmoSettingsRef, ...settings });
        this.state.configureFromGizmoSettings(this.gizmoSettingsRef);
        return this.gizmoSettingsRef;
    }
    gizmoSettings() {
        this.assertAlive();
        return this.gizmoSettingsRef;
    }
    configureViewportState(settings) {
        this.assertEditable();
        this.state.configureViewport(settings);
        return this.state.snapshot();
    }
    editorStateSnapshot() {
        this.assertAlive();
        return this.state.snapshot();
    }
    setMode(mode) {
        this.assertAlive();
        if (this.activePlayModeBridge && mode === "edit") {
            this.exitPlayMode();
            return;
        }
        this.modeRef = mode;
    }
    pruneSelection(alive) {
        this.assertEditable();
        this.selection.prune(alive);
    }
    currentSelection() {
        this.assertAlive();
        return this.selection.current();
    }
    select(ids) {
        this.assertEditable();
        this.selection.set(ids);
    }
    clearSelection() {
        this.assertEditable();
        this.selection.clear();
    }
    async executeCommand(command, context) {
        this.assertEditable();
        await this.history.execute(command, context);
    }
    async executeTransaction(commands, context) {
        this.assertEditable();
        await this.history.executeTransaction(commands, context);
    }
    async undo(context) {
        this.assertEditable();
        await this.history.undo(context);
    }
    async redo(context) {
        this.assertEditable();
        await this.history.redo(context);
    }
    clearHistory() {
        this.assertEditable();
        this.history.clear();
    }
    inspect(target) {
        this.assertAlive();
        return this.inspector.describe(target);
    }
    async editInspectedProperty(target, path, value, context) {
        this.assertEditable();
        await this.history.execute(this.inspector.createSetPropertyCommand(target, path, value), context);
    }
    describeHierarchy(root) {
        this.assertAlive();
        return this.hierarchy.describe(root, new Set(this.selection.current()));
    }
    flattenHierarchy(root) {
        this.assertAlive();
        return this.hierarchy.flatten(root, new Set(this.selection.current()));
    }
    setPickTargets(targets) {
        this.assertEditable();
        this.picking.setTargets(targets);
    }
    configurePickingBuffer(width, height) {
        this.assertEditable();
        this.picking.resizePickingBuffer(width, height);
        return this.picking.snapshot();
    }
    pick(ray) {
        this.assertAlive();
        return this.picking.pick(ray);
    }
    pickingSnapshot() {
        this.assertAlive();
        return this.picking.snapshot();
    }
    async translateTarget(target, input) {
        this.assertEditable();
        if (!target) {
            return;
        }
        const gizmo = new TranslateGizmo(this.history, this.gizmoSettingsRef);
        try {
            gizmo.setTarget(target);
            await gizmo.drag(input);
        }
        finally {
            gizmo.dispose();
        }
    }
    async rotateTarget(target, input) {
        this.assertEditable();
        if (!target) {
            return;
        }
        const gizmo = new RotateGizmo(this.history, this.gizmoSettingsRef);
        try {
            gizmo.setTarget(target);
            await gizmo.drag(input);
        }
        finally {
            gizmo.dispose();
        }
    }
    async scaleTarget(target, input) {
        this.assertEditable();
        if (!target) {
            return;
        }
        const gizmo = new ScaleGizmo(this.history, this.gizmoSettingsRef);
        try {
            gizmo.setTarget(target);
            await gizmo.drag(input);
        }
        finally {
            gizmo.dispose();
        }
    }
    updateDiagnostics(input) {
        this.assertAlive();
        return this.diagnostics.update(input);
    }
    diagnosticsSnapshot() {
        this.assertAlive();
        return this.diagnostics.snapshot();
    }
    registerMaterialVariants(assetId, variants, selected = null) {
        this.assertEditable();
        return this.materialVariants.register(assetId, variants, selected);
    }
    setMaterialVariant(assetId, variant) {
        this.assertEditable();
        return this.materialVariants.select(assetId, variant);
    }
    materialVariantState(assetId) {
        this.assertAlive();
        return this.materialVariants.state(assetId);
    }
    enterPlayMode(bridge) {
        this.assertAlive();
        if (this.modeRef !== "edit" || this.activePlayModeBridge) {
            throw new Error("EditorRuntime can only enter play mode from edit mode.");
        }
        bridge.enter();
        this.activePlayModeBridge = bridge;
        this.modeRef = "play";
    }
    pausePlayMode() {
        this.assertAlive();
        if (this.modeRef !== "play") {
            throw new Error("EditorRuntime can only pause while play mode is active.");
        }
        this.modeRef = "paused";
    }
    resumePlayMode() {
        this.assertAlive();
        if (this.modeRef !== "paused") {
            throw new Error("EditorRuntime can only resume from paused play mode.");
        }
        this.modeRef = "play";
    }
    exitPlayMode({ restore = true } = {}) {
        this.assertAlive();
        this.activePlayModeBridge?.exit({ restore });
        this.activePlayModeBridge = null;
        this.modeRef = "edit";
    }
    snapshot() {
        this.assertAlive();
        return {
            mode: this.modeRef,
            activeTool: this.activeToolRef,
            selection: this.selection.current(),
            canUndo: this.history.canUndo,
            canRedo: this.history.canRedo,
            undoDepth: this.history.undoDepth,
            redoDepth: this.history.redoDepth,
            materialVariants: this.materialVariants.snapshot(),
            prefabCount: this.prefabs.list().length,
            picking: this.picking.snapshot(),
            diagnostics: this.diagnostics.snapshot(),
            gizmoSettings: this.gizmoSettingsRef,
            editorState: this.state.snapshot()
        };
    }
    dispose() {
        this.activePlayModeBridge?.exit({ restore: true });
        this.activePlayModeBridge = null;
        this.selection.clear();
        this.history.clear();
        this.materialVariants.clear();
        this.diagnostics.clear();
        this.prefabs.clear();
        this.activeToolRef = "select";
        this.disposed = true;
    }
    assertAlive() {
        if (this.disposed) {
            throw new Error("EditorRuntime has been disposed");
        }
    }
    assertEditable() {
        this.assertAlive();
        if (this.modeRef !== "edit") {
            throw new Error("EditorRuntime edit commands can only run in edit mode.");
        }
    }
}
//# sourceMappingURL=EditorRuntime.js.map