import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import React, { createContext, useReducer, useRef, useState, useCallback, useMemo, useContext, useEffect } from 'react';
import { EventBus } from '../core/EventBus.js';
import { World } from '../ecs/World.js';
import { SceneManager } from '../scene/SceneManager.js';

class UndoStack {
    constructor(maxSize = 100) {
        this.stack = [];
        this.pointer = -1;
        this.events = new EventBus();
        this.maxSize = maxSize;
    }
    push(entry) {
        // Remove everything after current pointer
        this.stack.splice(this.pointer + 1);
        this.stack.push(entry);
        if (this.stack.length > this.maxSize)
            this.stack.shift();
        this.pointer = this.stack.length - 1;
        this.events.emit('change', null);
    }
    undo() {
        if (this.pointer < 0)
            return null;
        const entry = this.stack[this.pointer];
        entry.undo();
        this.pointer--;
        this.events.emit('change', null);
        return entry.description;
    }
    redo() {
        if (this.pointer >= this.stack.length - 1)
            return null;
        this.pointer++;
        const entry = this.stack[this.pointer];
        entry.redo();
        this.events.emit('change', null);
        return entry.description;
    }
    get canUndo() {
        return this.pointer >= 0;
    }
    get canRedo() {
        return this.pointer < this.stack.length - 1;
    }
    clear() {
        this.stack.length = 0;
        this.pointer = -1;
        this.events.emit('change', null);
    }
}
const DEFAULT_PANELS = [
    { id: 'hierarchy', title: 'Hierarchy', visible: true, position: 'left', width: 250 },
    { id: 'inspector', title: 'Inspector', visible: true, position: 'right', width: 300 },
    { id: 'assets', title: 'Assets', visible: true, position: 'bottom', height: 200 },
    { id: 'console', title: 'Console', visible: true, position: 'bottom', height: 150 },
];
const DEFAULT_STATE = {
    mode: 'edit',
    tool: 'select',
    selectedEntities: [],
    clipboard: [],
    gridSize: 16,
    snapToGrid: true,
    showGrid: true,
    showRulers: true,
    zoom: 1,
    panOffset: { x: 0, y: 0 },
    panels: DEFAULT_PANELS,
    currentSceneId: null,
    projectPath: null,
    dirty: false,
};
function editorReducer(state, action) {
    switch (action.type) {
        case 'SET_MODE':
            return { ...state, mode: action.mode };
        case 'SET_TOOL':
            return { ...state, tool: action.tool };
        case 'SELECT':
            return { ...state, selectedEntities: action.entities };
        case 'ADD_SELECTION':
            return { ...state, selectedEntities: [...new Set([...state.selectedEntities, ...action.entities])] };
        case 'DESELECT_ALL':
            return { ...state, selectedEntities: [] };
        case 'SET_CLIPBOARD':
            return { ...state, clipboard: action.entities };
        case 'SET_GRID_SIZE':
            return { ...state, gridSize: action.size };
        case 'TOGGLE_SNAP':
            return { ...state, snapToGrid: !state.snapToGrid };
        case 'TOGGLE_GRID':
            return { ...state, showGrid: !state.showGrid };
        case 'SET_ZOOM':
            return { ...state, zoom: Math.max(0.1, Math.min(10, action.zoom)) };
        case 'SET_PAN':
            return { ...state, panOffset: action.offset };
        case 'TOGGLE_PANEL':
            return {
                ...state,
                panels: state.panels.map(p => p.id === action.panelId ? { ...p, visible: !p.visible } : p),
            };
        case 'SET_SCENE':
            return { ...state, currentSceneId: action.sceneId };
        case 'MARK_DIRTY':
            return { ...state, dirty: true };
        case 'MARK_CLEAN':
            return { ...state, dirty: false };
        default:
            return state;
    }
}
const EditorContext = createContext(null);
function useEditor() {
    const ctx = useContext(EditorContext);
    if (!ctx)
        throw new Error('useEditor must be used within NiceGameEditor');
    return ctx;
}
function useKeyboardShortcuts(shortcuts) {
    useEffect(() => {
        const handler = (e) => {
            for (const s of shortcuts) {
                if (e.key.toLowerCase() === s.key.toLowerCase() &&
                    !!e.ctrlKey === !!s.ctrl &&
                    !!e.shiftKey === !!s.shift &&
                    !!e.altKey === !!s.alt) {
                    e.preventDefault();
                    s.action();
                    return;
                }
            }
        };
        window.addEventListener('keydown', handler);
        return () => window.removeEventListener('keydown', handler);
    }, [shortcuts]);
}
/* ── .ndg Project Format ──────────────────────────────────────── */
function serializeProject(project) {
    return JSON.stringify(project, null, 2);
}
function deserializeProject(json) {
    return JSON.parse(json);
}
const NiceGameEditor = ({ project, onSave, className, style, }) => {
    var _a, _b, _c, _d;
    const [state, dispatch] = useReducer(editorReducer, DEFAULT_STATE);
    const worldRef = useRef(new World());
    const undoRef = useRef(new UndoStack());
    const sceneManagerRef = useRef(new SceneManager());
    const eventsRef = useRef(new EventBus());
    const world = worldRef.current;
    const undoStack = undoRef.current;
    const sceneManager = sceneManagerRef.current;
    const events = eventsRef.current;
    const [worldVersion, setWorldVersion] = useState(0);
    const [consoleLogs, setConsoleLogs] = useState([]);
    const logIdRef = useRef(0);
    const refreshWorld = useCallback(() => setWorldVersion(v => v + 1), []);
    const addLog = useCallback((level, message) => {
        setConsoleLogs(prev => [...prev.slice(-199), { id: logIdRef.current++, level, message, timestamp: Date.now() }]);
    }, []);
    const createNewEntity = useCallback((name, components = ['Transform']) => {
        const entity = world.createEntity(name);
        for (const comp of components) {
            world.addComponent(entity.id, comp);
        }
        dispatch({ type: 'SELECT', entities: [entity.id] });
        dispatch({ type: 'MARK_DIRTY' });
        refreshWorld();
        addLog('info', `Created entity "${name}" (#${entity.id})`);
        return entity.id;
    }, [world, refreshWorld, addLog]);
    // Keyboard shortcuts
    const shortcuts = useMemo(() => [
        { key: 'z', ctrl: true, action: () => undoStack.undo(), description: 'Undo' },
        { key: 'z', ctrl: true, shift: true, action: () => undoStack.redo(), description: 'Redo' },
        { key: 'y', ctrl: true, action: () => undoStack.redo(), description: 'Redo' },
        { key: 's', ctrl: true, action: () => handleSave(), description: 'Save' },
        { key: 'v', action: () => dispatch({ type: 'SET_TOOL', tool: 'select' }), description: 'Select tool' },
        { key: 'w', action: () => dispatch({ type: 'SET_TOOL', tool: 'move' }), description: 'Move tool' },
        { key: 'e', action: () => dispatch({ type: 'SET_TOOL', tool: 'rotate' }), description: 'Rotate tool' },
        { key: 'r', action: () => dispatch({ type: 'SET_TOOL', tool: 'scale' }), description: 'Scale tool' },
        { key: 'g', ctrl: true, action: () => dispatch({ type: 'TOGGLE_GRID' }), description: 'Toggle grid' },
        { key: 'Delete', action: () => deleteSelected(), description: 'Delete selected' },
        { key: 'Escape', action: () => dispatch({ type: 'DESELECT_ALL' }), description: 'Deselect all' },
    ], []);
    useKeyboardShortcuts(shortcuts);
    const handleSave = useCallback(() => {
        var _a;
        if (!onSave)
            return;
        const sceneData = sceneManager.serializeScene(world, (_a = state.currentSceneId) !== null && _a !== void 0 ? _a : 'main', 'Main Scene');
        const proj = project !== null && project !== void 0 ? project : {
            formatVersion: 1,
            name: 'Untitled',
            version: '0.1.0',
            author: '',
            description: '',
            settings: {
                width: 1280, height: 720, targetFps: 60, maxPlayers: 4,
                physics: { gravity: { x: 0, y: 980 }, fixedTimestep: 1 / 60, velocityIterations: 8, positionIterations: 3 },
                audio: { masterVolume: 1, musicVolume: 0.7, sfxVolume: 1, voiceVolume: 1 },
                rendering: { pixelPerfect: false, antiAlias: true, backgroundColor: { r: 0.1, g: 0.1, b: 0.15, a: 1 } },
            },
            scenes: [sceneData],
            startSceneId: sceneData.id,
            assets: { assets: [] },
        };
        onSave(proj);
        dispatch({ type: 'MARK_CLEAN' });
    }, [world, sceneManager, state.currentSceneId, project, onSave]);
    const deleteSelected = useCallback(() => {
        const toDelete = [...state.selectedEntities];
        if (toDelete.length === 0)
            return;
        // Store for undo
        const snapshots = toDelete.map(id => ({
            entity: world.getEntity(id),
            id,
        }));
        for (const id of toDelete) {
            world.destroyEntity(id);
        }
        dispatch({ type: 'DESELECT_ALL' });
        dispatch({ type: 'MARK_DIRTY' });
        refreshWorld();
        addLog('info', `Deleted ${toDelete.length} entity(ies)`);
        undoStack.push({
            description: `Delete ${toDelete.length} entity(ies)`,
            undo: () => {
                // Recreating entities from snapshots would be more complex in real impl
                events.emit('editor:undo:delete', snapshots);
            },
            redo: () => {
                for (const id of toDelete)
                    world.destroyEntity(id);
            },
        });
    }, [state.selectedEntities, world, undoStack]);
    const ctxValue = useMemo(() => ({
        state,
        dispatch,
        world,
        undoStack,
        sceneManager,
        events,
        worldVersion,
        refreshWorld,
        createNewEntity,
        consoleLogs,
        addLog,
    }), [state, world, undoStack, sceneManager, events, worldVersion, refreshWorld, createNewEntity, consoleLogs, addLog]);
    return (jsx(EditorContext.Provider, { value: ctxValue, children: jsxs("div", { className: `nice-game-editor ${className !== null && className !== void 0 ? className : ''}`, style: {
                display: 'flex',
                flexDirection: 'column',
                width: '100%',
                height: '100%',
                overflow: 'hidden',
                backgroundColor: '#1e1e2e',
                color: '#cdd6f4',
                fontFamily: 'monospace',
                fontSize: 13,
                ...style,
            }, children: [jsx(EditorToolbar, {}), jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [((_a = state.panels.find(p => p.id === 'hierarchy')) === null || _a === void 0 ? void 0 : _a.visible) && (jsx(EditorPanelContainer, { title: "Hierarchy", width: 250, position: "left", children: jsx(HierarchyPanel, {}) })), jsx("div", { style: { flex: 1, position: 'relative', overflow: 'hidden' }, children: jsx(EditorViewport, {}) }), ((_b = state.panels.find(p => p.id === 'inspector')) === null || _b === void 0 ? void 0 : _b.visible) && (jsx(EditorPanelContainer, { title: "Inspector", width: 300, position: "right", children: jsx(InspectorPanel, {}) }))] }), jsxs("div", { style: { display: 'flex', borderTop: '1px solid #45475a' }, children: [((_c = state.panels.find(p => p.id === 'assets')) === null || _c === void 0 ? void 0 : _c.visible) && (jsx(EditorPanelContainer, { title: "Assets", height: 180, position: "bottom", children: jsx(AssetPanel, {}) })), ((_d = state.panels.find(p => p.id === 'console')) === null || _d === void 0 ? void 0 : _d.visible) && (jsx(EditorPanelContainer, { title: "Console", height: 180, position: "bottom", children: jsx(ConsolePanel, {}) }))] })] }) }));
};
/* ── Helpers ───────────────────────────────────────────────────── */
function getEntityBounds(entityId, world) {
    const shape = world.getComponent(entityId, 'Shape');
    if (shape) {
        if (shape.shape === 'circle')
            return { x: -shape.radius, y: -shape.radius, w: shape.radius * 2, h: shape.radius * 2 };
        return { x: -shape.width / 2, y: -shape.height / 2, w: shape.width, h: shape.height };
    }
    const sprite = world.getComponent(entityId, 'Sprite');
    if (sprite) {
        return { x: -sprite.width * sprite.anchor.x, y: -sprite.height * sprite.anchor.y, w: sprite.width, h: sprite.height };
    }
    const text = world.getComponent(entityId, 'Text');
    if (text)
        return { x: -30, y: -text.fontSize / 2, w: 60, h: text.fontSize };
    return { x: -8, y: -8, w: 16, h: 16 };
}
function getEntityWorldBounds(entityId, world) {
    const transform = world.getComponent(entityId, 'Transform');
    if (!transform)
        return null;
    const b = getEntityBounds(entityId, world);
    return { x: transform.position.x + b.x, y: transform.position.y + b.y, w: b.w, h: b.h };
}
function getCursorForTool(tool) {
    switch (tool) {
        case 'move': return 'move';
        case 'rotate': return 'crosshair';
        case 'scale': return 'nwse-resize';
        case 'pan': return 'grab';
        default: return 'default';
    }
}
function colorToCSS(c) {
    return `rgba(${Math.round(c.r * 255)},${Math.round(c.g * 255)},${Math.round(c.b * 255)},${c.a})`;
}
function cssToColor(hex) {
    const m = hex.match(/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
    if (!m)
        return { r: 1, g: 1, b: 1, a: 1 };
    return { r: parseInt(m[1], 16) / 255, g: parseInt(m[2], 16) / 255, b: parseInt(m[3], 16) / 255, a: 1 };
}
function colorToHex(c) {
    const r = Math.round(c.r * 255).toString(16).padStart(2, '0');
    const g = Math.round(c.g * 255).toString(16).padStart(2, '0');
    const b = Math.round(c.b * 255).toString(16).padStart(2, '0');
    return `#${r}${g}${b}`;
}
const ENTITY_PRESETS = [
    { label: 'Empty', icon: '○', components: ['Transform'] },
    { label: 'Sprite', icon: '🖼', components: ['Transform', 'Sprite'] },
    { label: 'Shape', icon: '■', components: ['Transform', 'Shape'] },
    { label: 'Text', icon: 'T', components: ['Transform', 'Text'] },
    { label: 'Physics Body', icon: '⚡', components: ['Transform', 'Shape', 'Collider2D', 'RigidBody2D'] },
    { label: 'Camera', icon: '📷', components: ['Transform', 'CameraTarget'] },
];
/* ── Sub-components ───────────────────────────────────────────── */
const EditorToolbar = () => {
    const { state, dispatch, createNewEntity, undoStack } = useEditor();
    const [showAddMenu, setShowAddMenu] = useState(false);
    const tools = ['select', 'move', 'rotate', 'scale', 'pan'];
    const toolIcons = { select: '⊕', move: '✥', rotate: '↻', scale: '⇲', pan: '✋' };
    const btnStyle = (active) => ({
        background: active ? '#585b70' : 'transparent',
        border: 'none', color: '#cdd6f4', padding: '4px 8px',
        borderRadius: 4, cursor: 'pointer', fontSize: 14,
    });
    return (jsxs("div", { style: {
            display: 'flex', alignItems: 'center', gap: 4,
            padding: '4px 8px', borderBottom: '1px solid #45475a',
            backgroundColor: '#181825',
        }, children: [tools.map(t => (jsx("button", { onClick: () => dispatch({ type: 'SET_TOOL', tool: t }), style: btnStyle(state.tool === t), title: `${t} (${t[0].toUpperCase()})`, children: toolIcons[t] }, t))), jsx("div", { style: { width: 1, height: 20, background: '#45475a', margin: '0 4px' } }), jsxs("div", { style: { position: 'relative' }, children: [jsx("button", { onClick: () => setShowAddMenu(!showAddMenu), style: { ...btnStyle(false), fontSize: 12, padding: '4px 10px', background: '#2f9e44', color: '#fff' }, children: "+ Entity" }), showAddMenu && (jsx("div", { style: {
                            position: 'absolute', top: '100%', left: 0, zIndex: 100,
                            background: '#1e1e2e', border: '1px solid #45475a', borderRadius: 4,
                            padding: 4, minWidth: 150, marginTop: 2,
                        }, children: ENTITY_PRESETS.map(p => (jsxs("button", { onClick: () => { createNewEntity(p.label, p.components); setShowAddMenu(false); }, style: {
                                display: 'block', width: '100%', textAlign: 'left',
                                background: 'transparent', border: 'none', color: '#cdd6f4',
                                padding: '4px 8px', cursor: 'pointer', borderRadius: 2, fontSize: 12,
                            }, children: [p.icon, " ", p.label] }, p.label))) }))] }), jsx("div", { style: { width: 1, height: 20, background: '#45475a', margin: '0 4px' } }), jsx("button", { onClick: () => dispatch({ type: 'SET_MODE', mode: state.mode === 'play' ? 'edit' : 'play' }), style: btnStyle(state.mode === 'play'), title: "Play/Stop", children: state.mode === 'play' ? '⏹' : '▶' }), jsx("button", { onClick: () => dispatch({ type: 'SET_MODE', mode: state.mode === 'pause' ? 'play' : 'pause' }), style: btnStyle(state.mode === 'pause'), title: "Pause", disabled: state.mode === 'edit', children: "\u23F8" }), jsx("div", { style: { width: 1, height: 20, background: '#45475a', margin: '0 4px' } }), jsx("button", { onClick: () => undoStack.undo(), style: btnStyle(false), title: "Undo (Ctrl+Z)", disabled: !undoStack.canUndo, children: "\u21A9" }), jsx("button", { onClick: () => undoStack.redo(), style: btnStyle(false), title: "Redo (Ctrl+Y)", disabled: !undoStack.canRedo, children: "\u21AA" }), jsx("div", { style: { width: 1, height: 20, background: '#45475a', margin: '0 4px' } }), jsx("button", { onClick: () => dispatch({ type: 'SET_ZOOM', zoom: state.zoom * 0.8 }), style: btnStyle(false), title: "Zoom Out", children: "\u2212" }), jsxs("span", { style: { fontSize: 11, minWidth: 40, textAlign: 'center' }, children: [(state.zoom * 100) | 0, "%"] }), jsx("button", { onClick: () => dispatch({ type: 'SET_ZOOM', zoom: state.zoom * 1.25 }), style: btnStyle(false), title: "Zoom In", children: "+" }), jsx("button", { onClick: () => { dispatch({ type: 'SET_ZOOM', zoom: 1 }); dispatch({ type: 'SET_PAN', offset: { x: 0, y: 0 } }); }, style: btnStyle(false), title: "Reset View", children: "\u2302" }), jsx("div", { style: { width: 1, height: 20, background: '#45475a', margin: '0 4px' } }), jsx("button", { onClick: () => dispatch({ type: 'TOGGLE_GRID' }), style: btnStyle(state.showGrid), title: "Toggle Grid (Ctrl+G)", children: "\u25A6" }), jsx("button", { onClick: () => dispatch({ type: 'TOGGLE_SNAP' }), style: btnStyle(state.snapToGrid), title: "Toggle Snap", children: "\u229E" }), jsx("div", { style: { flex: 1 } }), jsxs("span", { style: { fontSize: 11, opacity: 0.6 }, children: [state.mode.toUpperCase(), " | ", state.tool, " | ", state.dirty ? '● Unsaved' : '✓ Saved'] })] }));
};
const EditorPanelContainer = ({ title, width, height, position, children }) => {
    const isHorizontal = position === 'bottom';
    return (jsxs("div", { style: {
            width: isHorizontal ? '100%' : width,
            height: isHorizontal ? height : undefined,
            flex: isHorizontal ? undefined : undefined,
            borderLeft: position === 'right' ? '1px solid #45475a' : undefined,
            borderRight: position === 'left' ? '1px solid #45475a' : undefined,
            display: 'flex', flexDirection: 'column', overflow: 'hidden',
            backgroundColor: '#1e1e2e',
        }, children: [jsx("div", { style: {
                    padding: '4px 8px', fontSize: 11, fontWeight: 'bold',
                    backgroundColor: '#181825', borderBottom: '1px solid #313244',
                    textTransform: 'uppercase', letterSpacing: 1,
                }, children: title }), jsx("div", { style: { flex: 1, overflow: 'auto', padding: 4 }, children: children })] }));
};
const HierarchyPanel = () => {
    const { world, state, dispatch, createNewEntity, refreshWorld, addLog } = useEditor();
    const entities = world.allEntities().filter(e => e.parent == null);
    const getIcon = (id) => {
        if (world.hasComponent(id, 'CameraTarget'))
            return '📷';
        if (world.hasComponent(id, 'Sprite'))
            return '🖼';
        if (world.hasComponent(id, 'Text'))
            return 'T';
        if (world.hasComponent(id, 'Shape'))
            return '■';
        if (world.hasComponent(id, 'ParticleEmitter'))
            return '✨';
        if (world.hasComponent(id, 'AudioSource'))
            return '🔊';
        return '○';
    };
    const renderEntity = (e, depth) => {
        const isSelected = state.selectedEntities.includes(e.id);
        const children = e.children.map(cid => world.getEntity(cid)).filter(Boolean);
        return (jsxs(React.Fragment, { children: [jsxs("div", { onClick: () => dispatch({ type: 'SELECT', entities: [e.id] }), style: {
                        padding: '2px 6px', paddingLeft: 6 + depth * 16, cursor: 'pointer',
                        backgroundColor: isSelected ? '#45475a' : 'transparent',
                        borderRadius: 2, display: 'flex', alignItems: 'center', gap: 4, fontSize: 12,
                    }, children: [jsx("span", { style: { fontSize: 10, width: 14, textAlign: 'center' }, children: getIcon(e.id) }), jsx("span", { style: { flex: 1 }, children: e.name }), jsxs("span", { style: { opacity: 0.3, fontSize: 10 }, children: ["#", e.id] })] }), children.map(c => renderEntity(c, depth + 1))] }, e.id));
    };
    return (jsxs("div", { children: [jsxs("div", { style: { padding: '2px 6px', fontSize: 10, display: 'flex', gap: 4, borderBottom: '1px solid #313244', marginBottom: 4 }, children: [jsx("button", { onClick: () => createNewEntity('Entity'), style: { background: 'transparent', border: 'none', color: '#a6adc8', cursor: 'pointer', fontSize: 10 }, title: "Add Entity", children: "+" }), jsx("button", { onClick: () => {
                            if (state.selectedEntities.length > 0) {
                                const id = state.selectedEntities[0];
                                const child = world.createEntity('Child');
                                world.addComponent(child.id, 'Transform');
                                world.setParent(child.id, id);
                                dispatch({ type: 'SELECT', entities: [child.id] });
                                dispatch({ type: 'MARK_DIRTY' });
                                refreshWorld();
                                addLog('info', `Created child entity (#${child.id})`);
                            }
                        }, style: { background: 'transparent', border: 'none', color: '#a6adc8', cursor: 'pointer', fontSize: 10 }, title: "Add Child", disabled: state.selectedEntities.length === 0, children: "\u21B3+" }), jsx("span", { style: { flex: 1 } }), jsxs("span", { style: { color: '#6c7086' }, children: [world.entityCount, " entities"] })] }), entities.map(e => renderEntity(e, 0))] }));
};
/* ── Inspector sub-editors ─────────────────────────────────────── */
const fieldStyle = {
    width: '100%', background: '#313244', color: '#cdd6f4', border: '1px solid #45475a',
    borderRadius: 3, padding: '2px 4px', fontSize: 11,
};
const NumField = ({ label, value, onChange, step = 1 }) => (jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: label }), jsx("input", { type: "number", value: Math.round(value * 1000) / 1000, step: step, onChange: e => onChange(Number(e.target.value)), style: { ...fieldStyle, flex: 1 } })] }));
const ColField = ({ label, color, onChange }) => (jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: label }), jsx("input", { type: "color", value: colorToHex(color), onChange: e => onChange(cssToColor(e.target.value)), style: { width: 28, height: 20, border: 'none', padding: 0, cursor: 'pointer' } }), jsx("span", { style: { fontSize: 9, color: '#6c7086' }, children: colorToHex(color) })] }));
const TransformEditor = ({ entityId }) => {
    const { world, refreshWorld, dispatch } = useEditor();
    const t = world.getComponent(entityId, 'Transform');
    if (!t)
        return null;
    const update = (fn) => { fn(); dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); };
    return (jsxs(Fragment, { children: [jsx(NumField, { label: "Pos X", value: t.position.x, onChange: v => update(() => { t.position.x = v; }) }), jsx(NumField, { label: "Pos Y", value: t.position.y, onChange: v => update(() => { t.position.y = v; }) }), jsx(NumField, { label: "Rotation", value: t.rotation * (180 / Math.PI), step: 5, onChange: v => update(() => { t.rotation = v * (Math.PI / 180); }) }), jsx(NumField, { label: "Scale X", value: t.scale.x, step: 0.1, onChange: v => update(() => { t.scale.x = v; }) }), jsx(NumField, { label: "Scale Y", value: t.scale.y, step: 0.1, onChange: v => update(() => { t.scale.y = v; }) })] }));
};
const ShapeEditor = ({ entityId }) => {
    const { world, refreshWorld, dispatch } = useEditor();
    const s = world.getComponent(entityId, 'Shape');
    if (!s)
        return null;
    const update = (fn) => { fn(); dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); };
    return (jsxs(Fragment, { children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: "Shape" }), jsxs("select", { value: s.shape, onChange: e => update(() => { s.shape = e.target.value; }), style: { ...fieldStyle, flex: 1 }, children: [jsx("option", { value: "rect", children: "Rectangle" }), jsx("option", { value: "circle", children: "Circle" }), jsx("option", { value: "polygon", children: "Polygon" }), jsx("option", { value: "line", children: "Line" })] })] }), s.shape !== 'circle' && jsx(NumField, { label: "Width", value: s.width, onChange: v => update(() => { s.width = v; }) }), s.shape !== 'circle' && jsx(NumField, { label: "Height", value: s.height, onChange: v => update(() => { s.height = v; }) }), s.shape === 'circle' && jsx(NumField, { label: "Radius", value: s.radius, onChange: v => update(() => { s.radius = v; }) }), jsx(ColField, { label: "Fill", color: s.fillColor, onChange: c => update(() => { Object.assign(s.fillColor, c); }) }), jsx(ColField, { label: "Stroke", color: s.strokeColor, onChange: c => update(() => { Object.assign(s.strokeColor, c); }) }), jsx(NumField, { label: "Stroke W", value: s.strokeWidth, onChange: v => update(() => { s.strokeWidth = v; }) })] }));
};
const SpriteEditor = ({ entityId }) => {
    const { world, refreshWorld, dispatch } = useEditor();
    const s = world.getComponent(entityId, 'Sprite');
    if (!s)
        return null;
    const update = (fn) => { fn(); dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); };
    return (jsxs(Fragment, { children: [jsx(NumField, { label: "Width", value: s.width, onChange: v => update(() => { s.width = v; }) }), jsx(NumField, { label: "Height", value: s.height, onChange: v => update(() => { s.height = v; }) }), jsx(ColField, { label: "Tint", color: s.tint, onChange: c => update(() => { Object.assign(s.tint, c); }) }), jsx(NumField, { label: "Opacity", value: s.opacity, step: 0.1, onChange: v => update(() => { s.opacity = Math.max(0, Math.min(1, v)); }) }), jsx(NumField, { label: "Layer", value: s.layer, step: 1, onChange: v => update(() => { s.layer = v; }) }), jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: "Visible" }), jsx("input", { type: "checkbox", checked: s.visible, onChange: e => update(() => { s.visible = e.target.checked; }) })] })] }));
};
const TextEditor = ({ entityId }) => {
    const { world, refreshWorld, dispatch } = useEditor();
    const t = world.getComponent(entityId, 'Text');
    if (!t)
        return null;
    const update = (fn) => { fn(); dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); };
    return (jsxs(Fragment, { children: [jsxs("div", { style: { marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, color: '#a6adc8' }, children: "Text" }), jsx("input", { type: "text", value: t.text, onChange: e => update(() => { t.text = e.target.value; }), style: { ...fieldStyle, width: '100%' } })] }), jsx(NumField, { label: "Font Size", value: t.fontSize, onChange: v => update(() => { t.fontSize = v; }) }), jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: "Align" }), jsxs("select", { value: t.align, onChange: e => update(() => { t.align = e.target.value; }), style: { ...fieldStyle, flex: 1 }, children: [jsx("option", { value: "left", children: "Left" }), jsx("option", { value: "center", children: "Center" }), jsx("option", { value: "right", children: "Right" })] })] }), jsx(ColField, { label: "Color", color: t.color, onChange: c => update(() => { Object.assign(t.color, c); }) })] }));
};
const RigidBodyEditor = ({ entityId }) => {
    const { world, refreshWorld, dispatch } = useEditor();
    const rb = world.getComponent(entityId, 'RigidBody2D');
    if (!rb)
        return null;
    const update = (fn) => { fn(); dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); };
    return (jsxs(Fragment, { children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 2 }, children: [jsx("span", { style: { fontSize: 10, minWidth: 50, color: '#a6adc8' }, children: "Body" }), jsxs("select", { value: rb.bodyType, onChange: e => update(() => { rb.bodyType = e.target.value; }), style: { ...fieldStyle, flex: 1 }, children: [jsx("option", { value: "dynamic", children: "Dynamic" }), jsx("option", { value: "static", children: "Static" }), jsx("option", { value: "kinematic", children: "Kinematic" })] })] }), jsx(NumField, { label: "Mass", value: rb.mass, step: 0.1, onChange: v => update(() => { rb.mass = v; }) }), jsx(NumField, { label: "Drag", value: rb.drag, step: 0.01, onChange: v => update(() => { rb.drag = v; }) }), jsx(NumField, { label: "Bounce", value: rb.restitution, step: 0.05, onChange: v => update(() => { rb.restitution = v; }) }), jsx(NumField, { label: "Friction", value: rb.friction, step: 0.05, onChange: v => update(() => { rb.friction = v; }) }), jsx(NumField, { label: "Gravity", value: rb.gravityScale, step: 0.1, onChange: v => update(() => { rb.gravityScale = v; }) })] }));
};
const ADDABLE_COMPONENTS = [
    'Transform', 'Sprite', 'Shape', 'Collider2D', 'RigidBody2D',
    'Text', 'Animator', 'Script', 'AudioSource', 'ParticleEmitter', 'CameraTarget',
];
const InspectorPanel = () => {
    const { state, world, dispatch, refreshWorld, addLog } = useEditor();
    const [showAddComp, setShowAddComp] = useState(false);
    if (state.selectedEntities.length === 0) {
        return jsx("div", { style: { opacity: 0.5, padding: 8 }, children: "No selection" });
    }
    const entityId = state.selectedEntities[0];
    const entity = world.getEntity(entityId);
    if (!entity)
        return null;
    const compTypes = ADDABLE_COMPONENTS;
    const presentComponents = compTypes.filter(type => world.hasComponent(entityId, type));
    const componentEditors = {
        Transform: TransformEditor,
        Shape: ShapeEditor,
        Sprite: SpriteEditor,
        Text: TextEditor,
        RigidBody2D: RigidBodyEditor,
    };
    return (jsxs("div", { children: [jsxs("div", { style: { padding: '4px 0', marginBottom: 4, borderBottom: '1px solid #313244' }, children: [jsx("input", { type: "text", value: entity.name, onChange: e => { entity.name = e.target.value; dispatch({ type: 'MARK_DIRTY' }); refreshWorld(); }, style: { ...fieldStyle, fontWeight: 'bold', fontSize: 13 } }), jsxs("span", { style: { fontSize: 9, color: '#6c7086' }, children: ["ID: ", entityId] })] }), presentComponents.map(type => {
                const Editor = componentEditors[type];
                return (jsxs("div", { style: { marginBottom: 8, borderTop: '1px solid #313244', paddingTop: 4 }, children: [jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }, children: [jsx("span", { style: { fontSize: 11, fontWeight: 'bold', textTransform: 'uppercase', color: '#89b4fa' }, children: type }), type !== 'Transform' && (jsx("button", { onClick: () => {
                                        world.removeComponent(entityId, type);
                                        dispatch({ type: 'MARK_DIRTY' });
                                        refreshWorld();
                                        addLog('info', `Removed ${type} from #${entityId}`);
                                    }, style: { background: 'transparent', border: 'none', color: '#f38ba8', cursor: 'pointer', fontSize: 10 }, title: `Remove ${type}`, children: "\u2715" }))] }), Editor ? jsx(Editor, { entityId: entityId }) : (jsx("pre", { style: { fontSize: 10, opacity: 0.6, whiteSpace: 'pre-wrap', margin: 0 }, children: JSON.stringify(world.getComponent(entityId, type), null, 2) }))] }, type));
            }), jsxs("div", { style: { marginTop: 8, position: 'relative' }, children: [jsx("button", { onClick: () => setShowAddComp(!showAddComp), style: { width: '100%', padding: '4px 8px', background: '#313244', border: '1px solid #45475a', color: '#cdd6f4', borderRadius: 4, cursor: 'pointer', fontSize: 11 }, children: "+ Add Component" }), showAddComp && (jsx("div", { style: {
                            position: 'absolute', bottom: '100%', left: 0, right: 0,
                            background: '#1e1e2e', border: '1px solid #45475a', borderRadius: 4,
                            padding: 4, marginBottom: 2, maxHeight: 200, overflow: 'auto', zIndex: 10,
                        }, children: compTypes.filter(t => !world.hasComponent(entityId, t)).map(type => (jsx("button", { onClick: () => {
                                world.addComponent(entityId, type);
                                dispatch({ type: 'MARK_DIRTY' });
                                refreshWorld();
                                addLog('info', `Added ${type} to #${entityId}`);
                                setShowAddComp(false);
                            }, style: {
                                display: 'block', width: '100%', textAlign: 'left',
                                background: 'transparent', border: 'none', color: '#cdd6f4',
                                padding: '3px 6px', cursor: 'pointer', borderRadius: 2, fontSize: 11,
                            }, children: type }, type))) }))] })] }));
};
const AssetPanel = () => {
    const [tab, setTab] = useState('all');
    const tabBtnStyle = (active) => ({
        padding: '2px 8px', border: 'none', borderRadius: '3px 3px 0 0', cursor: 'pointer', fontSize: 10,
        background: active ? '#313244' : 'transparent', color: active ? '#cdd6f4' : '#6c7086',
    });
    const assets = [
        { name: 'player.png', type: 'sprites', icon: '🖼' },
        { name: 'enemy.png', type: 'sprites', icon: '🖼' },
        { name: 'tileset.png', type: 'sprites', icon: '🖼' },
        { name: 'background.jpg', type: 'sprites', icon: '🖼' },
        { name: 'jump.wav', type: 'audio', icon: '🔊' },
        { name: 'music.mp3', type: 'audio', icon: '🎵' },
        { name: 'coin.wav', type: 'audio', icon: '🔊' },
        { name: 'playerController.ts', type: 'scripts', icon: '📜' },
        { name: 'enemyAI.ts', type: 'scripts', icon: '📜' },
    ];
    const filtered = tab === 'all' ? assets : assets.filter(a => a.type === tab);
    return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [jsxs("div", { style: { display: 'flex', gap: 2, padding: '2px 4px', borderBottom: '1px solid #313244' }, children: [['all', 'sprites', 'audio', 'scripts'].map(t => (jsx("button", { onClick: () => setTab(t), style: tabBtnStyle(tab === t), children: t.charAt(0).toUpperCase() + t.slice(1) }, t))), jsx("span", { style: { flex: 1 } }), jsx("button", { style: { background: 'transparent', border: 'none', color: '#a6adc8', cursor: 'pointer', fontSize: 10 }, title: "Import Asset", children: "+ Import" })] }), jsx("div", { style: { flex: 1, overflow: 'auto', display: 'grid', gridTemplateColumns: 'repeat(auto-fill, 80px)', gap: 4, padding: 4 }, children: filtered.map(a => (jsxs("div", { draggable: true, style: {
                        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2,
                        padding: 4, background: '#313244', borderRadius: 4, cursor: 'grab',
                        fontSize: 9, textAlign: 'center',
                    }, children: [jsx("span", { style: { fontSize: 20 }, children: a.icon }), jsx("span", { style: { wordBreak: 'break-all' }, children: a.name })] }, a.name))) })] }));
};
const ConsolePanel = () => {
    const { consoleLogs } = useEditor();
    const [filter, setFilter] = useState('all');
    const scrollRef = useRef(null);
    useEffect(() => {
        if (scrollRef.current)
            scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }, [consoleLogs.length]);
    const filtered = filter === 'all' ? consoleLogs : consoleLogs.filter(l => l.level === filter);
    const levelColors = { info: '#89b4fa', warn: '#f9e2af', error: '#f38ba8' };
    const filterBtn = (f, label) => (jsx("button", { onClick: () => setFilter(f), style: { padding: '1px 6px', border: 'none', borderRadius: 2, cursor: 'pointer', fontSize: 9,
            background: filter === f ? '#313244' : 'transparent', color: filter === f ? '#cdd6f4' : '#6c7086' }, children: label }));
    return (jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', fontFamily: 'monospace' }, children: [jsxs("div", { style: { display: 'flex', gap: 2, padding: '2px 4px', borderBottom: '1px solid #313244' }, children: [filterBtn('all', 'All'), " ", filterBtn('info', 'Info'), " ", filterBtn('warn', 'Warn'), " ", filterBtn('error', 'Error'), jsx("span", { style: { flex: 1 } }), jsxs("span", { style: { fontSize: 9, color: '#6c7086' }, children: [filtered.length, " entries"] })] }), jsx("div", { ref: scrollRef, style: { flex: 1, overflow: 'auto', fontSize: 10 }, children: filtered.length === 0 ? (jsx("div", { style: { opacity: 0.4, padding: 8 }, children: "Console ready." })) : filtered.map(log => (jsxs("div", { style: { padding: '1px 4px', borderBottom: '1px solid #181825', display: 'flex', gap: 6 }, children: [jsx("span", { style: { color: '#6c7086', minWidth: 55 }, children: new Date(log.timestamp).toLocaleTimeString([], { hour12: false }) }), jsxs("span", { style: { color: levelColors[log.level], fontWeight: 'bold', minWidth: 35 }, children: ["[", log.level.toUpperCase(), "]"] }), jsx("span", { children: log.message })] }, log.id))) })] }));
};
const EditorViewport = () => {
    const canvasRef = useRef(null);
    const { state, dispatch, world, worldVersion, refreshWorld, undoStack } = useEditor();
    const dragging = useRef(false);
    const dragStart = useRef({ x: 0, y: 0 });
    const dragEntityStart = useRef({ x: 0, y: 0 });
    const panning = useRef(false);
    const panStart = useRef({ x: 0, y: 0, px: 0, py: 0 });
    // Draw loop
    useEffect(() => {
        const canvas = canvasRef.current;
        if (!canvas)
            return;
        const ctx = canvas.getContext('2d');
        if (!ctx)
            return;
        const { width, height } = canvas.getBoundingClientRect();
        canvas.width = width;
        canvas.height = height;
        ctx.fillStyle = '#11111b';
        ctx.fillRect(0, 0, width, height);
        ctx.save();
        ctx.translate(width / 2 + state.panOffset.x, height / 2 + state.panOffset.y);
        ctx.scale(state.zoom, state.zoom);
        // Grid
        if (state.showGrid) {
            const gs = state.gridSize;
            const halfW = (width / 2) / state.zoom;
            const halfH = (height / 2) / state.zoom;
            const offX = -state.panOffset.x / state.zoom;
            const offY = -state.panOffset.y / state.zoom;
            const startX = Math.floor((offX - halfW) / gs) * gs;
            const endX = Math.ceil((offX + halfW) / gs) * gs;
            const startY = Math.floor((offY - halfH) / gs) * gs;
            const endY = Math.ceil((offY + halfH) / gs) * gs;
            ctx.strokeStyle = 'rgba(69, 71, 90, 0.3)';
            ctx.lineWidth = 0.5 / state.zoom;
            for (let x = startX; x <= endX; x += gs) {
                ctx.beginPath();
                ctx.moveTo(x, startY);
                ctx.lineTo(x, endY);
                ctx.stroke();
            }
            for (let y = startY; y <= endY; y += gs) {
                ctx.beginPath();
                ctx.moveTo(startX, y);
                ctx.lineTo(endX, y);
                ctx.stroke();
            }
            // Origin axes
            ctx.strokeStyle = 'rgba(205, 214, 244, 0.15)';
            ctx.lineWidth = 1 / state.zoom;
            ctx.beginPath();
            ctx.moveTo(0, startY);
            ctx.lineTo(0, endY);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(startX, 0);
            ctx.lineTo(endX, 0);
            ctx.stroke();
        }
        // Render entities
        const entities = world.allEntities();
        for (const entity of entities) {
            if (!entity.enabled)
                continue;
            const transform = world.getComponent(entity.id, 'Transform');
            if (!transform)
                continue;
            ctx.save();
            ctx.translate(transform.position.x, transform.position.y);
            ctx.rotate(transform.rotation);
            ctx.scale(transform.scale.x, transform.scale.y);
            const isSelected = state.selectedEntities.includes(entity.id);
            // Shape
            const shape = world.getComponent(entity.id, 'Shape');
            if (shape && shape.visible) {
                ctx.fillStyle = colorToCSS(shape.fillColor);
                ctx.strokeStyle = colorToCSS(shape.strokeColor);
                ctx.lineWidth = shape.strokeWidth;
                switch (shape.shape) {
                    case 'rect':
                        ctx.fillRect(-shape.width / 2, -shape.height / 2, shape.width, shape.height);
                        if (shape.strokeWidth > 0)
                            ctx.strokeRect(-shape.width / 2, -shape.height / 2, shape.width, shape.height);
                        break;
                    case 'circle':
                        ctx.beginPath();
                        ctx.arc(0, 0, shape.radius, 0, Math.PI * 2);
                        ctx.fill();
                        if (shape.strokeWidth > 0)
                            ctx.stroke();
                        break;
                    case 'line':
                        ctx.beginPath();
                        ctx.moveTo(shape.lineStart.x, shape.lineStart.y);
                        ctx.lineTo(shape.lineEnd.x, shape.lineEnd.y);
                        ctx.stroke();
                        break;
                    case 'polygon':
                        if (shape.points.length > 2) {
                            ctx.beginPath();
                            ctx.moveTo(shape.points[0].x, shape.points[0].y);
                            for (let i = 1; i < shape.points.length; i++)
                                ctx.lineTo(shape.points[i].x, shape.points[i].y);
                            ctx.closePath();
                            ctx.fill();
                            if (shape.strokeWidth > 0)
                                ctx.stroke();
                        }
                        break;
                }
            }
            // Sprite
            const sprite = world.getComponent(entity.id, 'Sprite');
            if (sprite && sprite.visible) {
                ctx.globalAlpha = sprite.opacity;
                ctx.fillStyle = colorToCSS(sprite.tint);
                const ax = -sprite.width * sprite.anchor.x;
                const ay = -sprite.height * sprite.anchor.y;
                ctx.fillRect(ax, ay, sprite.width, sprite.height);
                // Sprite border
                ctx.strokeStyle = 'rgba(255,255,255,0.2)';
                ctx.lineWidth = 1 / state.zoom;
                ctx.strokeRect(ax, ay, sprite.width, sprite.height);
                // Sprite label
                ctx.fillStyle = 'rgba(255,255,255,0.5)';
                ctx.font = `${9 / state.zoom}px monospace`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(sprite.assetId || 'sprite', 0, 0);
                ctx.globalAlpha = 1;
            }
            // Text
            const text = world.getComponent(entity.id, 'Text');
            if (text && text.visible && text.text) {
                ctx.font = `${text.fontSize}px ${text.fontFamily}`;
                ctx.fillStyle = colorToCSS(text.color);
                ctx.textAlign = text.align;
                ctx.textBaseline = text.baseline;
                ctx.fillText(text.text, 0, 0);
            }
            // Particle emitter indicator
            const particles = world.getComponent(entity.id, 'ParticleEmitter');
            if (particles) {
                ctx.strokeStyle = '#f9e2af';
                ctx.lineWidth = 1 / state.zoom;
                ctx.setLineDash([3 / state.zoom, 3 / state.zoom]);
                ctx.beginPath();
                ctx.arc(0, 0, 12, 0, Math.PI * 2);
                ctx.stroke();
                ctx.setLineDash([]);
                ctx.fillStyle = '#f9e2af';
                ctx.font = `${8 / state.zoom}px monospace`;
                ctx.textAlign = 'center';
                ctx.fillText('✨', 0, 3 / state.zoom);
            }
            // Camera indicator
            const cam = world.getComponent(entity.id, 'CameraTarget');
            if (cam) {
                ctx.strokeStyle = '#a6e3a1';
                ctx.lineWidth = 1.5 / state.zoom;
                const hw = 60, hh = 34;
                ctx.strokeRect(-hw, -hh, hw * 2, hh * 2);
                ctx.font = `${8 / state.zoom}px monospace`;
                ctx.fillStyle = '#a6e3a1';
                ctx.textAlign = 'center';
                ctx.fillText('📷 Camera', 0, -hh - 4 / state.zoom);
            }
            // Selection highlight
            if (isSelected) {
                const b = getEntityBounds(entity.id, world);
                ctx.strokeStyle = '#89b4fa';
                ctx.lineWidth = 2 / state.zoom;
                ctx.setLineDash([4 / state.zoom, 4 / state.zoom]);
                ctx.strokeRect(b.x - 2, b.y - 2, b.w + 4, b.h + 4);
                ctx.setLineDash([]);
                // Corner handles
                const hs = 4 / state.zoom;
                ctx.fillStyle = '#89b4fa';
                ctx.fillRect(b.x - hs, b.y - hs, hs * 2, hs * 2);
                ctx.fillRect(b.x + b.w - hs, b.y - hs, hs * 2, hs * 2);
                ctx.fillRect(b.x - hs, b.y + b.h - hs, hs * 2, hs * 2);
                ctx.fillRect(b.x + b.w - hs, b.y + b.h - hs, hs * 2, hs * 2);
            }
            // Entity name label (only if no visible shape/sprite/text OR selected)
            if (isSelected || (!(shape === null || shape === void 0 ? void 0 : shape.visible) && !(sprite === null || sprite === void 0 ? void 0 : sprite.visible) && !(text === null || text === void 0 ? void 0 : text.visible))) {
                ctx.font = `${10 / state.zoom}px monospace`;
                ctx.fillStyle = isSelected ? '#89b4fa' : 'rgba(205, 214, 244, 0.4)';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'bottom';
                const b = getEntityBounds(entity.id, world);
                ctx.fillText(entity.name, 0, b.y - 4 / state.zoom);
                // Draw small dot for invisible/empty entities
                if (!(shape === null || shape === void 0 ? void 0 : shape.visible) && !(sprite === null || sprite === void 0 ? void 0 : sprite.visible) && !(text === null || text === void 0 ? void 0 : text.visible)) {
                    ctx.fillStyle = isSelected ? '#89b4fa' : 'rgba(205, 214, 244, 0.3)';
                    ctx.beginPath();
                    ctx.arc(0, 0, 3 / state.zoom, 0, Math.PI * 2);
                    ctx.fill();
                }
            }
            ctx.restore();
        }
        ctx.restore();
        // HUD
        ctx.fillStyle = '#6c7086';
        ctx.font = '10px monospace';
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillText(`Entities: ${world.entityCount} | Grid: ${state.gridSize}px`, 8, 8);
    }, [state, worldVersion, world]);
    // Mouse handlers
    const screenToWorld = useCallback((e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        const sx = e.clientX - rect.left;
        const sy = e.clientY - rect.top;
        return {
            x: (sx - canvas.width / 2 - state.panOffset.x) / state.zoom,
            y: (sy - canvas.height / 2 - state.panOffset.y) / state.zoom,
        };
    }, [state.zoom, state.panOffset]);
    const handlePointerDown = useCallback((e) => {
        // Middle click or pan tool = pan
        if (e.button === 1 || (e.button === 0 && state.tool === 'pan')) {
            panning.current = true;
            panStart.current = { x: e.clientX, y: e.clientY, px: state.panOffset.x, py: state.panOffset.y };
            e.target.setPointerCapture(e.pointerId);
            return;
        }
        if (e.button !== 0)
            return;
        const { x: wx, y: wy } = screenToWorld(e);
        // Hit test (reverse order for top-most)
        const entities = world.allEntities();
        let hitId = null;
        for (let i = entities.length - 1; i >= 0; i--) {
            const ent = entities[i];
            if (!ent.enabled)
                continue;
            const bounds = getEntityWorldBounds(ent.id, world);
            if (bounds && wx >= bounds.x && wx <= bounds.x + bounds.w && wy >= bounds.y && wy <= bounds.y + bounds.h) {
                hitId = ent.id;
                break;
            }
        }
        if (hitId !== null) {
            dispatch({ type: 'SELECT', entities: [hitId] });
            if (state.tool === 'select' || state.tool === 'move') {
                dragging.current = true;
                dragStart.current = { x: wx, y: wy };
                const t = world.getComponent(hitId, 'Transform');
                if (t)
                    dragEntityStart.current = { x: t.position.x, y: t.position.y };
            }
        }
        else {
            dispatch({ type: 'DESELECT_ALL' });
        }
        e.target.setPointerCapture(e.pointerId);
    }, [state.tool, state.panOffset, screenToWorld, world, dispatch]);
    const handlePointerMove = useCallback((e) => {
        if (panning.current) {
            const dx = e.clientX - panStart.current.x;
            const dy = e.clientY - panStart.current.y;
            dispatch({ type: 'SET_PAN', offset: { x: panStart.current.px + dx, y: panStart.current.py + dy } });
            return;
        }
        if (dragging.current && state.selectedEntities.length > 0) {
            const { x: wx, y: wy } = screenToWorld(e);
            const dx = wx - dragStart.current.x;
            const dy = wy - dragStart.current.y;
            const entityId = state.selectedEntities[0];
            const t = world.getComponent(entityId, 'Transform');
            if (t) {
                let nx = dragEntityStart.current.x + dx;
                let ny = dragEntityStart.current.y + dy;
                if (state.snapToGrid) {
                    nx = Math.round(nx / state.gridSize) * state.gridSize;
                    ny = Math.round(ny / state.gridSize) * state.gridSize;
                }
                t.position.x = nx;
                t.position.y = ny;
                dispatch({ type: 'MARK_DIRTY' });
                refreshWorld();
            }
        }
    }, [state.selectedEntities, state.snapToGrid, state.gridSize, screenToWorld, world, dispatch, refreshWorld]);
    const handlePointerUp = useCallback(() => {
        if (dragging.current && state.selectedEntities.length > 0) {
            const entityId = state.selectedEntities[0];
            const t = world.getComponent(entityId, 'Transform');
            if (t) {
                const finalPos = { x: t.position.x, y: t.position.y };
                const startPos = { ...dragEntityStart.current };
                if (finalPos.x !== startPos.x || finalPos.y !== startPos.y) {
                    undoStack.push({
                        description: 'Move entity',
                        undo: () => { t.position.x = startPos.x; t.position.y = startPos.y; refreshWorld(); },
                        redo: () => { t.position.x = finalPos.x; t.position.y = finalPos.y; refreshWorld(); },
                    });
                }
            }
        }
        dragging.current = false;
        panning.current = false;
    }, [state.selectedEntities, world, undoStack, refreshWorld]);
    const handleWheel = useCallback((e) => {
        e.preventDefault();
        const factor = e.deltaY > 0 ? 0.9 : 1.1;
        dispatch({ type: 'SET_ZOOM', zoom: state.zoom * factor });
    }, [state.zoom, dispatch]);
    return (jsx("canvas", { ref: canvasRef, style: { width: '100%', height: '100%', display: 'block', cursor: getCursorForTool(state.tool) }, onPointerDown: handlePointerDown, onPointerMove: handlePointerMove, onPointerUp: handlePointerUp, onWheel: handleWheel, onContextMenu: e => e.preventDefault() }));
};

export { NiceGameEditor, UndoStack, deserializeProject, serializeProject, useEditor };
//# sourceMappingURL=NiceGameEditor.js.map
