import { normalizeGizmoSettings } from "./Gizmo.js";
const defaultViewportSettings = {
    showGrid: true,
    gridSize: 10,
    gridSubdivisions: 10,
    showAxisGizmo: true,
    showStats: true,
    showWireframe: false,
    showBounds: false,
    showLights: true,
    showCameras: true,
    fov: 60,
    near: 0.1,
    far: 1000
};
const defaultGridSnapSettings = {
    snapToGrid: true,
    positionSnap: 1,
    rotationSnapEnabled: false,
    rotationSnapDegrees: 15,
    scaleSnapEnabled: false,
    scaleSnap: 0.1
};
export class EditorStateModel {
    activeToolRef = "select";
    transformSpaceRef = "world";
    pivotModeRef = "center";
    viewportRef = defaultViewportSettings;
    gridSnapRef = defaultGridSnapSettings;
    listeners = new Set();
    storageKey;
    storage;
    persisted = false;
    changes = 0;
    constructor(options = {}) {
        this.storage = options.storage;
        this.storageKey = options.storageKey ?? "aura3d:editor-state:externalParity";
        this.load();
    }
    get activeTool() {
        return this.activeToolRef;
    }
    get transformSpace() {
        return this.transformSpaceRef;
    }
    get pivotMode() {
        return this.pivotModeRef;
    }
    setActiveTool(tool) {
        const normalized = tool.trim();
        if (normalized.length === 0)
            throw new Error("Editor state active tool cannot be empty.");
        this.setValue("activeTool", this.activeToolRef, normalized, (value) => {
            this.activeToolRef = value;
        });
    }
    setTransformSpace(space) {
        this.setValue("transformSpace", this.transformSpaceRef, space, (value) => {
            this.transformSpaceRef = value;
        });
    }
    setPivotMode(mode) {
        this.setValue("pivotMode", this.pivotModeRef, mode, (value) => {
            this.pivotModeRef = value;
        });
    }
    configureViewport(settings) {
        const next = normalizeViewportSettings({ ...this.viewportRef, ...settings });
        this.setValue("viewport", this.viewportRef, next, (value) => {
            this.viewportRef = value;
        });
        return this.viewportRef;
    }
    configureGridSnap(settings) {
        const next = normalizeGridSnapSettings({ ...this.gridSnapRef, ...settings });
        this.setValue("gridSnap", this.gridSnapRef, next, (value) => {
            this.gridSnapRef = value;
        });
        return this.gridSnapRef;
    }
    configureFromGizmoSettings(settings) {
        this.setTransformSpace(settings.spaceMode);
        this.setPivotMode(settings.pivotMode);
        this.configureGridSnap({
            snapToGrid: settings.snapEnabled,
            positionSnap: settings.positionSnap,
            rotationSnapEnabled: settings.snapEnabled,
            rotationSnapDegrees: settings.rotationSnapDegrees,
            scaleSnapEnabled: settings.snapEnabled,
            scaleSnap: settings.scaleSnap
        });
    }
    gizmoSettings() {
        return normalizeGizmoSettings({
            snapEnabled: this.gridSnapRef.snapToGrid,
            positionSnap: this.gridSnapRef.positionSnap,
            rotationSnapDegrees: this.gridSnapRef.rotationSnapDegrees,
            scaleSnap: this.gridSnapRef.scaleSnap,
            spaceMode: this.transformSpaceRef,
            pivotMode: this.pivotModeRef
        });
    }
    snapPosition(value) {
        return this.gridSnapRef.snapToGrid ? snap(value, this.gridSnapRef.positionSnap) : value;
    }
    snapRotation(radians) {
        return this.gridSnapRef.rotationSnapEnabled ? snap(radians, this.gridSnapRef.rotationSnapDegrees * Math.PI / 180) : radians;
    }
    snapScale(value) {
        return this.gridSnapRef.scaleSnapEnabled ? snap(value, this.gridSnapRef.scaleSnap) : value;
    }
    subscribe(listener) {
        this.listeners.add(listener);
        return () => this.listeners.delete(listener);
    }
    snapshot() {
        return {
            activeTool: this.activeToolRef,
            transformSpace: this.transformSpaceRef,
            pivotMode: this.pivotModeRef,
            viewport: this.viewportRef,
            gridSnap: this.gridSnapRef,
            persisted: this.persisted,
            storageKey: this.storageKey,
            changeCount: this.changes,
            evidence: {
                oldCodebasePort: true,
                persistentEditorState: this.persisted,
                viewportSettings: this.viewportRef.gridSize > 0 && this.viewportRef.fov > 0 && this.viewportRef.far > this.viewportRef.near,
                gridSnapSettings: this.gridSnapRef.positionSnap > 0 && this.gridSnapRef.rotationSnapDegrees > 0 && this.gridSnapRef.scaleSnap > 0,
                transformSpacePivotMode: Boolean(this.transformSpaceRef) && Boolean(this.pivotModeRef)
            }
        };
    }
    reset() {
        this.activeToolRef = "select";
        this.transformSpaceRef = "world";
        this.pivotModeRef = "center";
        this.viewportRef = defaultViewportSettings;
        this.gridSnapRef = defaultGridSnapSettings;
        this.changes += 1;
        this.persist();
        this.emit({ property: "reset", oldValue: null, newValue: this.snapshot() });
    }
    setValue(property, current, next, apply) {
        if (deepEqual(current, next))
            return;
        apply(next);
        this.changes += 1;
        this.persist();
        this.emit({ property, oldValue: current, newValue: next });
    }
    emit(change) {
        for (const listener of this.listeners)
            listener(change);
    }
    persist() {
        if (!this.storage)
            return;
        this.storage.setItem(this.storageKey, JSON.stringify({
            activeTool: this.activeToolRef,
            transformSpace: this.transformSpaceRef,
            pivotMode: this.pivotModeRef,
            viewport: this.viewportRef,
            gridSnap: this.gridSnapRef
        }));
        this.persisted = true;
    }
    load() {
        if (!this.storage)
            return;
        const text = this.storage.getItem(this.storageKey);
        if (!text)
            return;
        try {
            const parsed = JSON.parse(text);
            if (!isRecord(parsed))
                return;
            this.activeToolRef = typeof parsed.activeTool === "string" && parsed.activeTool.trim().length > 0 ? parsed.activeTool : this.activeToolRef;
            this.transformSpaceRef = parsed.transformSpace === "local" ? "local" : "world";
            this.pivotModeRef = parsed.pivotMode === "active" || parsed.pivotMode === "individual" ? parsed.pivotMode : "center";
            this.viewportRef = normalizeViewportSettings({ ...this.viewportRef, ...(isRecord(parsed.viewport) ? parsed.viewport : {}) });
            this.gridSnapRef = normalizeGridSnapSettings({ ...this.gridSnapRef, ...(isRecord(parsed.gridSnap) ? parsed.gridSnap : {}) });
            this.persisted = true;
        }
        catch {
            this.persisted = false;
        }
    }
}
export function createMemoryEditorStateStorage(initial = {}) {
    const values = { ...initial };
    return {
        values,
        getItem(key) {
            return values[key] ?? null;
        },
        setItem(key, value) {
            values[key] = value;
        },
        removeItem(key) {
            delete values[key];
        }
    };
}
function normalizeViewportSettings(settings) {
    return {
        showGrid: booleanOr(settings.showGrid, defaultViewportSettings.showGrid),
        gridSize: positive(settings.gridSize, defaultViewportSettings.gridSize),
        gridSubdivisions: Math.max(1, Math.round(positive(settings.gridSubdivisions, defaultViewportSettings.gridSubdivisions))),
        showAxisGizmo: booleanOr(settings.showAxisGizmo, defaultViewportSettings.showAxisGizmo),
        showStats: booleanOr(settings.showStats, defaultViewportSettings.showStats),
        showWireframe: booleanOr(settings.showWireframe, defaultViewportSettings.showWireframe),
        showBounds: booleanOr(settings.showBounds, defaultViewportSettings.showBounds),
        showLights: booleanOr(settings.showLights, defaultViewportSettings.showLights),
        showCameras: booleanOr(settings.showCameras, defaultViewportSettings.showCameras),
        fov: clamp(positive(settings.fov, defaultViewportSettings.fov), 10, 140),
        near: positive(settings.near, defaultViewportSettings.near),
        far: Math.max(positive(settings.far, defaultViewportSettings.far), positive(settings.near, defaultViewportSettings.near) + 1)
    };
}
function normalizeGridSnapSettings(settings) {
    return {
        snapToGrid: booleanOr(settings.snapToGrid, defaultGridSnapSettings.snapToGrid),
        positionSnap: positive(settings.positionSnap, defaultGridSnapSettings.positionSnap),
        rotationSnapEnabled: booleanOr(settings.rotationSnapEnabled, defaultGridSnapSettings.rotationSnapEnabled),
        rotationSnapDegrees: positive(settings.rotationSnapDegrees, defaultGridSnapSettings.rotationSnapDegrees),
        scaleSnapEnabled: booleanOr(settings.scaleSnapEnabled, defaultGridSnapSettings.scaleSnapEnabled),
        scaleSnap: positive(settings.scaleSnap, defaultGridSnapSettings.scaleSnap)
    };
}
function booleanOr(value, fallback) {
    return typeof value === "boolean" ? value : fallback;
}
function positive(value, fallback) {
    const number = Number(value);
    return Number.isFinite(number) && number > 0 ? number : fallback;
}
function clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
}
function snap(value, increment) {
    return Math.round(value / increment) * increment;
}
function deepEqual(left, right) {
    return JSON.stringify(left) === JSON.stringify(right);
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
//# sourceMappingURL=EditorStateModel.js.map